diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..96c3869 --- /dev/null +++ b/.clang-format @@ -0,0 +1,103 @@ +# NTIA/ITS C++ Clang-Format Style Options +# Updated 9/25/2024 +--- +AlignAfterOpenBracket: BlockIndent +AlignOperands: AlignAfterOperator +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortLambdasOnASingleLine: All +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BasedOnStyle: WebKit +BinPackArguments: false +BinPackParameters: false +BitFieldColonSpacing: After +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false + BeforeLambdaBody: false + BeforeWhile: false +BreakBeforeBinaryOperators: All +BreakBeforeBraces: Attach +BreakInheritanceList: AfterColon +BreakBeforeConceptDeclarations: false +BreakConstructorInitializers: AfterColon +BreakStringLiterals: true +ColumnLimit: 80 +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +EmptyLineBeforeAccessModifier: Never +FixNamespaceComments: true +IncludeBlocks: Regroup +IndentAccessModifiers: true +IndentCaseBlocks: true +IndentCaseLabels: true +IndentExternBlock: Indent +IndentGotoLabels: true +IndentPPDirectives: BeforeHash +IndentRequires: true +IndentWidth: 4 +IndentWrappedFunctionNames: true +KeepEmptyLinesAtTheStartOfBlocks: false +Language: Cpp +LineEnding: CRLF +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 4 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PointerAlignment: Right +ReflowComments: false +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: false +SpaceBeforeInheritanceColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInConditionalStatement: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: c++14 +TabWidth: 4 +UseTab: Never diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml new file mode 100644 index 0000000..75f0645 --- /dev/null +++ b/.github/workflows/ctest.yml @@ -0,0 +1,58 @@ +# This action compiles the library and runs all unit tests using an OS and CMake matrix +# Doxygen documentation is also built. Build fails on missing documentation. +name: Unit Tests + +on: + push: + branches: ["main", "dev"] + pull_request: + branches: ["main", "dev"] + workflow_dispatch: + +# Define the matrix for different operating systems +jobs: + build-and-test: + name: ${{ matrix.os }} / CMake ${{ matrix.cmakeVersion }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + # Windows-2019 is used as a dedicated 32-bit build/test platform + os: [ubuntu-latest, macos-latest, windows-latest, windows-2019] + # CMake >= 3.21 is required to use "--preset " and discover generators + cmakeVersion: ["3.21", latest] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Clone required submodules + run: | + git submodule init extern/googletest + git submodule init extern/doxygen-awesome-css + git submodule update + + - name: Install CMake + uses: lukka/get-cmake@latest + with: + cmakeVersion: ${{ matrix.cmakeVersion }} + + - name: Install Doxygen + uses: ssciwr/doxygen-install@v1 + with: + version: "1.11.0" + + - name: "CMake: Build and Test (32-bit)" + if: matrix.os == 'windows-2019' + uses: lukka/run-cmake@v10 + with: + configurePreset: release32 + buildPreset: release32 + testPreset: release32 + + - name: "CMake: Build and Test (64-bit)" + if: matrix.os != 'windows-2019' + uses: lukka/run-cmake@v10 + with: + configurePreset: release64 + buildPreset: release64 + testPreset: release64 diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml new file mode 100644 index 0000000..544c7dc --- /dev/null +++ b/.github/workflows/doxygen.yml @@ -0,0 +1,70 @@ +# This action builds **AND DEPLOYS** Doxygen documentation to GitHub Pages +# Doxygen site is DEPLOYED if this action is triggered by publishing a release. +# Doxygen site is NOT DEPLOYED (only built) when triggered by pull request or dispatched. +name: C++ Docs + +on: + release: + types: ["published"] + pull_request: + branches: ["main", "dev"] + workflow_dispatch: + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Clone doxygen-awesome-css submodule + run: | + git submodule init extern/doxygen-awesome-css + git submodule update + + - name: Install Doxygen + uses: ssciwr/doxygen-install@v1 + with: + version: "1.11.0" + + - name: Setup GitHub Pages + if: ${{ github.event_name == 'release' }} + id: pages + uses: actions/configure-pages@v5 + + - name: Install CMake + uses: lukka/get-cmake@latest + + - name: Build documentation with Doxygen + uses: lukka/run-cmake@v10 + with: + configurePreset: docsOnly + buildPreset: docsOnly + + - name: Upload GitHub Pages artifact + uses: actions/upload-pages-artifact@v3 + if: ${{ github.event_name == 'release' }} + with: + path: ./docs/html/ + + deploy: + if: ${{ github.event_name == 'release'}} + needs: build + permissions: + contents: read + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..c369e5d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,70 @@ +# This action compiles multi-platform binaries for a release. +# It is triggered when a new tag is made with a version number starting with "v" +name: Create Release Artifacts + +on: + push: + tags: ['v[0-9]+.*'] + workflow_dispatch: + +permissions: + contents: write + +jobs: + create_release_artifacts: + name: Create release artifacts + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-2019, windows-latest] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install CMake # (latest stable version) + uses: lukka/get-cmake@latest + + - name: "CMake: Build (32-bit)" + if: matrix.os == 'windows-2019' + uses: lukka/run-cmake@v10 + with: + configurePreset: release32 + configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF', '-DRUN_TESTS=OFF']" + buildPreset: release32 + + - name: "CMake: Build (64-bit)" + if: matrix.os != 'windows-2019' + uses: lukka/run-cmake@v10 + with: + configurePreset: release64 + configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF', '-DRUN_TESTS=OFF']" + buildPreset: release64 + + - name: Upload release artifact (macOS or Linux) + if: runner.os != 'Windows' + uses: actions/upload-artifact@v4 + with: + name: release-${{ matrix.os }} + path: | + ${{ github.workspace }}/bin/*.dylib + ${{ github.workspace }}/bin/*.so + if-no-files-found: error + overwrite: true + + - name: Upload release artifact (Windows x86) + if: matrix.os == 'windows-2019' + uses: actions/upload-artifact@v4 + with: + name: release-windows-x86 + path: ${{ github.workspace }}\bin\Debug\*.dll + if-no-files-found: error + overwrite: true + + - name: Upload release artifact (Windows x64) + if: matrix.os == 'windows-latest' + uses: actions/upload-artifact@v4 + with: + name: release-windows-x64 + path: ${{ github.workspace }}\bin\Debug\*.dll + if-no-files-found: error + overwrite: true diff --git a/.gitignore b/.gitignore index 0d01ff9..738bc1d 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,31 @@ Thumbs.db *.aps **/obj **/x64 -**/x86 \ No newline at end of file +**/x86 + +######### +## CMake +######### +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps +build + + +########### +## Doxygen +########### +docs/html + +########### +## VS Code +########### +.vscode \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6ee26b7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,7 @@ +[submodule "extern/doxygen-awesome-css"] + path = extern/doxygen-awesome-css + url = https://github.com/jothepro/doxygen-awesome-css +[submodule "extern/googletest"] + path = extern/googletest + url = https://github.com/google/googletest + branch = v1.12.x diff --git a/.zenodo.json b/.zenodo.json index bcbd657..e1256ec 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -4,14 +4,16 @@ "orcid": "0000-0002-7417-4009", "affiliation": "The Institute for Telecommunication Sciences", "name": "Kozma Jr, William" + }, + { + "orcid": "0000-0001-8437-6504", + "affiliation": "The Institute for Telecommunication Sciences", + "name": "Romaniello, Anthony W." } ], - "title": "Recommendation ITU-R P.2108-1, Release 1.0", - + "description": "Models for the prediction of clutter loss", "upload_type": "software", - "version": "1.0.0", - "keywords": ["Study Group 3", "ITU-R", "P2108", "clutter", "propagation"] } \ No newline at end of file diff --git a/CITATION.cff b/CITATION.cff index 1c1824d..56cd2cf 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -1,16 +1,46 @@ cff-version: 1.2.0 -title: 'Recommendation ITU-R P.2108-1' -message: >- - If you use this software, please cite it using the - metadata from this file. +title: >- + Recommendation ITU-R P.2108-1, U.S. Reference Software + Implementation +message: Please cite this software using these metadata. type: software authors: - - given-names: William - family-names: Kozma - name-suffix: Jr - email: wkozma@ntia.gov - affiliation: The Institute for Telecommunication Sciences + - family-names: Kozma + given-names: William + name-suffix: Jr. + affiliation: >- + U.S. Department of Commerce, National + Telecommunications and Information Administration, + Institute for Telecommunication Sciences orcid: 'https://orcid.org/0000-0002-7417-4009' -doi: 10.5281/zenodo.7114523 -url: https://github.com/NTIA/p2108 -version: 0.0.0 + email: wkozma@ntia.gov + - family-names: Romaniello + given-names: Anthony W. + affiliation: >- + U.S. Department of Commerce, National + Telecommunications and Information Administration, + Institute for Telecommunication Sciences + orcid: 'https://orcid.org/0000-0001-8437-6504' + email: aromaniello@ntia.gov + - name: >- + U.S. Department of Commerce, National + Telecommunications and Information Administration, + Institute for Telecommunications Sciences + address: 325 Broadway + city: Boulder + region: Colorado + post-code: '80305' + country: US + email: code@ntia.gov + website: 'https://its.ntia.gov' +repository-code: 'https://github.com/NTIA/p2108' +url: 'https://github.com/NTIA/propagation/wiki' +repository: 'https://github.com/NTIA/p2108' +keywords: + - propagation + - rf + - clutter + - itu +license: 'NTIA Public Domain' +version: 1.0.0 +date-released: '2024-07-12' diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b2c22ee --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,70 @@ +# >=3.21 required for Ninja Generators to use absolute paths. +# See https://stackoverflow.com/questions/69846931/ +# This is relevant for specifying unit test data file paths +# Automated testing only runs >=3.21 for this reason. +# >=3.14 required for GoogleTest v1.12.x +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + +########################################### +## PROJECT METADATA +########################################### +set(LIB_NAME "P2108") # Name of library/target +set(LIB_NAMESPACE "ITS.ITU.PSeries") # Namespace for the named library +project( + "${LIB_NAMESPACE}.${LIB_NAME}" + VERSION 1.0.0 + DESCRIPTION "Recommendation ITU-R P.2108, U.S. Reference Implementation" + HOMEPAGE_URL "https://ntia.github.io/propagation-library-wiki/models/P2108" + LANGUAGES "CXX" +) + +########################################### +## SPECIFY MULTI-LANGUAGE WRAPPERS +########################################### +set(DOTNET_WRAPPER_DIR "${PROJECT_SOURCE_DIR}/wrap/dotnet") +set(MATLAB_WRAPPER_DIR "${PROJECT_SOURCE_DIR}/wrap/matlab") +set(PYTHON_WRAPPER_DIR "${PROJECT_SOURCE_DIR}/wrap/python") + +########################################### +## CMAKE OPTIONS AND DEFAULTS +########################################### +# Define options. Defaults to: compile 64-bit library, build docs, run tests +option(BUILD_DOCS "Generate documentation with Doxygen" ON) +option(DOCS_ONLY "Skip all steps except generating documentation" OFF) +option(RUN_TESTS "Run C++ unit tests with Google Test + CTest" ON) +option(COPY_TO_WRAPPERS "Copy compiled library into wrapper submodules" ON) +option(BUILD_32BIT "Build project for x86/32-bit instead of x64/64-bit" OFF) + +########################################### +## SETUP +########################################### +# GoogleTest v1.12.1 requires at least C++11 +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +########################################## +## BUILD/RUN +########################################## +if (NOT DOCS_ONLY) + add_subdirectory(src) # Build the shared library + if (COPY_TO_WRAPPERS) # Copy compiled library to wrappers + add_subdirectory(wrap) + endif () + if (RUN_TESTS) # Build and run unit tests + if (EXISTS "${PROJECT_SOURCE_DIR}/extern/googletest/CMakeLists.txt") + enable_testing() + add_subdirectory(tests) + else () + message(SEND_ERROR + "Unable to build tests. GoogleTest submodule is missing. " + "Run `git submodule init extern/googletest` then " + "`git submodule update` and try again." + ) + endif() + endif () +endif () + +# Generate documentation +if (BUILD_DOCS OR DOCS_ONLY) + add_subdirectory(docs) +endif () diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..8ba9db6 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,192 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 21, + "patch": 0 + }, + "configurePresets": [ + { + "name": "proplib-config-base", + "hidden": true, + "description": "Base configuration preset for ITS PropLib libraries", + "binaryDir": "${sourceDir}/build/${presetName}", + "cacheVariables": { + "CMAKE_BUILD_RPATH_USE_ORIGIN": "ON", + "DOCS_ONLY": "OFF", + "BUILD_32BIT": "OFF", + "RUN_TESTS": "ON" + } + }, + { + "name": "proplib-config-release-base", + "hidden": true, + "inherits": "proplib-config-base", + "description": "Base 'Release' configuration preset for ITS PropLib libraries", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "BUILD_DOCS": "ON" + } + }, + { + "name": "proplib-config-debug-base", + "hidden": true, + "inherits": "proplib-config-base", + "description": "Base 'Debug' configuration preset for ITS PropLib libraries", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "BUILD_DOCS": "OFF" + } + }, + { + "name": "debug64", + "displayName": "Debug, 64-bit", + "description": "Build library and tests with debug options, skip building docs", + "inherits": "proplib-config-debug-base" + }, + { + "name": "release64", + "displayName": "Release, 64-bit", + "description": "Build library and tests with release options, and build docs", + "inherits": "proplib-config-release-base" + }, + { + "name": "debug32", + "displayName": "Debug, 32-bit", + "description": "Build library and tests with debug options, skip building docs", + "inherits": "proplib-config-debug-base", + "cacheVariables": { + "BUILD_32BIT": "ON" + } + }, + { + "name": "release32", + "displayName": "Release, 32-bit", + "description": "Build library and tests with release options, and build docs", + "inherits": "proplib-config-release-base", + "cacheVariables": { + "BUILD_32BIT": "ON" + } + }, + { + "name": "docsOnly", + "displayName": "Doxygen only", + "description": "Do not build the library; only build Doxygen docs.", + "inherits": "proplib-config-base", + "cacheVariables": { + "BUILD_DOCS": "ON", + "DOCS_ONLY": "ON", + "RUN_TESTS": "OFF" + } + } + ], + "buildPresets": [ + { + "name": "proplib-build-base", + "hidden": true, + "description": "Base build preset for ITS PropLib libraries" + }, + { + "name": "proplib-build-release-base", + "hidden": true, + "inherits": "proplib-build-base", + "configuration": "Release", + "description": "Base 'Release' build preset for ITS PropLib libraries", + "cleanFirst": true + }, + { + "name": "proplib-build-debug-base", + "hidden": true, + "inherits": "proplib-build-base", + "configuration": "Debug", + "description": "Base 'Debug' build preset for ITS PropLib libraries", + "verbose": true + }, + { + "name": "debug64", + "inherits": "proplib-build-debug-base", + "displayName": "Build Debug, 64-bit", + "description": "Build 64-bit library and tests with debug options, skip building docs", + "configurePreset": "debug64" + }, + { + "name": "release64", + "inherits": "proplib-build-release-base", + "displayName": "Build Release, 64-bit", + "description": "Build 64-bit library and tests with release options, and build docs", + "configurePreset": "release64" + }, + { + "name": "debug32", + "inherits": "proplib-build-debug-base", + "displayName": "Build Debug, 32-bit", + "description": "Build 32-bit library and tests with debug options, skip building docs", + "configurePreset": "debug32" + }, + { + "name": "release32", + "inherits": "proplib-build-release-base", + "displayName": "Build Release, 32-bit", + "description": "Build 32-bit library and tests with release options, and build docs", + "configurePreset": "release32" + }, + { + "name": "docsOnly", + "inherits": "proplib-build-base", + "displayName": "Build Doxygen Docs Only", + "description": "Do not build the library; only build Doxygen docs.", + "configurePreset": "docsOnly" + } + ], + "testPresets": [ + { + "name": "proplib-test-base", + "hidden": true, + "description": "Base test preset for ITS PropLib libraries", + "output": { + "shortProgress": true, + "outputOnFailure": true + } + }, + { + "name": "proplib-test-debug-base", + "hidden": true, + "inherits": "proplib-test-base", + "description": "Base 'Debug' test preset for ITS PropLib libraries" + }, + { + "name": "proplib-test-release-base", + "hidden": true, + "inherits": "proplib-test-base", + "description": "Base 'Release' test preset for ITS PropLib libraries" + }, + { + "name": "debug64", + "inherits": "proplib-test-debug-base", + "displayName": "Test Debug, 64-bit", + "description": "Build 64-bit library and tests with debug options, skip building docs", + "configurePreset": "debug64" + }, + { + "name": "release64", + "inherits": "proplib-test-release-base", + "displayName": "Test Release, 64-bit", + "description": "Build 64-bit library and tests with release options, and build docs", + "configurePreset": "release64" + }, + { + "name": "debug32", + "inherits": "proplib-test-debug-base", + "displayName": "Test Debug, 32-bit", + "description": "Build 32-bit library and tests with debug options, skip building docs", + "configurePreset": "debug32" + }, + { + "name": "release32", + "inherits": "proplib-test-release-base", + "displayName": "Test Release, 32-bit", + "description": "Build 32-bit library and tests with release options, and build docs", + "configurePreset": "release32" + } + ] +} \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ce335c0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,267 @@ +# NTIA/ITS Propagation Library Contribution Guide + +Thank you for your interest in contributing to this open source software. On this +page you will get an overview of the contribution workflow from opening an issue, +creating a PR, reviewing, +and merging the PR. This page also includes some information about the project +structures, development workflows, and code styles which are used throughout the +ITS Propagation Library. + +If you are instead interested in usage documentation, please refer to the +[Propagation Library Wiki](https://ntia.github.io/propagation-library-wiki). + +## Contents + +- [Found a Bug?](#found-a-bug) +- [Background for New Contributors](#background-for-new-contributors) +- [Notes on Code Style](#notes-on-code-style) +- [Project Structure and CMake](#project-structure-and-cmake) +- [Documenting Code](#documenting-code) + +## Found a Bug? + +If you spot a problem with this software, +[search if an issue already exists](https://docs.github.com/en/github/searching-for-information-on-github/searching-on-github/searching-issues-and-pull-requests#search-by-the-title-body-or-comments). +If a related issue doesn't exist, we encourage you to open one (even if you +don't plan to contribute a resolution yourself). Issues may be opened for bugs, +documentation errors, or feature requests. + +## Background for new contributors + +The workflow we recommend and describe here follows from best and common +practices in the Git and GitHub ecosystems. We aim to leverage this workflow, +especially the elements of code review and approval, to enable open source +development of robust, trustworthy radio propagation software. Here are some +resources to help you get started with open source contributions: + +- [Set up Git](https://docs.github.com/en/get-started/getting-started-with-git/set-up-git) +- [GitHub flow](https://docs.github.com/en/get-started/using-github/github-flow) +- [Collaborating with pull requests](https://docs.github.com/en/github/collaborating-with-pull-requests) +- [Basic explanation of Git submodules](https://gist.github.com/gitaarik/8735255) +by [**@gitaarik**](https://github.com/gitaarik) + +### Git Submodules + +Software in the ITS Propagation Library is implemented primarily in C++. Each +piece of software has a primary repository which contains the base C++ implementation, +test data and resources, and common files used by the multi-language wrappers. +Interfaces for additional programming languages are provided in separate repositories, +which are linked to the primary repository as [Git submodules](https://gist.github.com/gitaarik/8735255). +When cloning the primary repository, the submodules are not additionally cloned +by default. This can be done with the `git submodule init` command. Initializing +the submodule as part of the parent repository will let you use the build +configuration from the primary repository to compile the C++ source and place it +appropriately for use by the wrapper code. If you choose to independently clone +the wrapper repository, you will likely need to separately download the compiled +library (for example, a DLL from a GitHub release). + +### Contributing on GitHub + +If you'd like to solve an existing issue, add a new feature, or modify this software, +follow these steps when making your changes. + +1. Fork the repository. This allows you to make your changes without affecting the +original project until you're ready to merge them. You can create a fork +[with GitHub Desktop](https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/cloning-and-forking-repositories-from-github-desktop) +or [using the command line](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo#fork-an-example-repository) + +1. Create a working branch and start with your changes! Commit changes +incrementally to your fork. See the sections below for details about unit tests, +code style, and documentation. + +1. When you're done making changes, create a pull request, also known as a PR. +In your PR, please include a meaningful description of the changes you've made. +If your PR solves an issue, +[link to it](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue)! +Once you submit your PR, a maintainer will review your changes. We may ask questions +or request additional changes which must be addressed before the PR can be merged. + +When your PR is approved and merged, your changes will be a part of the main +branch of the repository. A new release may or may not be immediately created, +depending on the changes made. If a new release is not immediately made, your +changes will be packaged into the next release. + +## Notes on Code Style + +- In general, variables follow the naming convention in which a single underscore +denotes a subscript (pseudo-LaTeX format), where a double underscore is followed +by the units, i.e. `h_1__meter`. +- Variables are named to match their corresponding mathematical variables in the +underlying text, when applicable. +- Wherever possible, equation numbers are provided. It is assumed that a user +reviewing this source code would have a copy of the relevant text available +as a primary reference. +- _For base/C++ repositories_, a `.clang-format` file is included in the root directory. +Most IDEs support this type of file, which can and should be used to apply uniform +code styling to C++ source and header files. +- _For Python wrapper repositories_, a `.pre-commit-config.yaml` file is included +in the root directory. This file implements multiple hooks for the [pre-commit](https://pre-commit.com/) +tool, which apply automated formatting to files when they are committed to Git. +It is recommended to use this tool to autoformat Python code when checked in. + +## Project Structure and CMake + +Software in the ITS Propagation Library is primarily implemented in C++, then +wrapped with interfaces exposing the C++ library to users of other languages. The +primary repository for each software package uses [CMake](https://cmake.org/) to +handle cross-platform C++ build configuration, C++ unit tests, and generation of +API documentation (with [Doxygen](https://www.doxygen.nl/)). Many IDEs support CMake +integration in some form or fashion, and it is recommended that you familiarize yourself +with any such functionality of your chosen IDE. + +This section shows a typical project structure for a primary (i.e., non-wrapper) +repository. For details about wrapper repositories, refer to their own README files. + +```bash +docs/ + CMakeLists.txt # Doxygen configuration +extern/ + ... # External Git submodules/dependencies +include/ + / # Include namespace folder, e.g. "ITS.Propagation.ITM" + .h # Library header files go here, e.g. "ITM.h" and "ErrorCodes.h" +src/ + .cpp # Source files go here, e.g. "LongleyRice.cpp" and "FreeSpaceLoss.cpp" + CMakeLists.txt # Configures cross-platform build +tests/ + data/ + .csv # Testing data goes here. Does not have to be CSV. + .cpp # Unit tests, usually one test file per source file. + .h # Any headers used by tests go here as well. + CMakeLists.txt # CTest+GTest config. Must add names of test files here. +wrap/ + dotnet/ # C#/.NET wrapper submodule. Should contain CMakeLists.txt + matlab/ # MATLAB wrapper submodule. Should contain CMakeLists.txt + python/ # Python wrapper submodule. Should contain CMakeLists.txt +CMakeLists.txt # Top-level CMakeLists.txt: project metadata and options +CMakePresets.json # Presets for CMake, e.g. "release64", "debug64", etc. +... +``` + +As you can see, multiple `CMakeLists.txt` files exist within the project. Each +one contains configurations relevant to the directory where it is stored. For +example, the `tests/CMakeLists.txt` file configures unit tests using CMake. + +When modifying or extending this software, ensure that unit tests are added to +cover your new code. In general, each C++ file in `src/` has a corresponding C++ +file in `tests/` which implements unit tests. If you've added a new file in `tests/`, +make sure to add that file to the executable in `tests/CMakeLists.txt`. + +To compile the software, from the cloned repository, run: + +```bash +cmake -S . -B build +cmake --build build +``` + +After compiling the library, you can run unit tests as follows. First, change your +working directory to the `build` directory, then run: + +```bash +ctest +``` + +The included `CMakePresets.json` provides presets for common CMake configurations. +The "Release" configurations will compile the software with optimizations, build +documentation, and configure unit tests. The "Debug" configurations will skip building +the documentation, which is useful for rapid development and testing. Additionally, +the Debug configuration will attempt to pass debug flags to the compiler. + +### Supported Platforms and Build Options + +The provided `CMakeLists.txt` and `CMakePresets.json` files aim to be flexible +for development from the platform of your choosing. The approach taken is to make +few assumptions about your toolchain to implicitly enable cross-platform and +multi-environment development as much as possible. However, we cannot guarantee +that all compilers, tools, and platforms will work without requiring some additional +configuration which is not documented here. If you find an issue or would like to +see a change to support your chosen platform or tools, open an issue or create a +pull request! + +## Documenting Code + +### C++ Base Libraries + +The C++ source code is documented with Doxygen. A GitHub Action is configured to +build and deploy the documentation using GitHub Pages. This action will ensure +that any new code has been accompanied by Doxygen-formatted documentation. Code +will not be merged until and unless it is completely documented using Doxygen, +and the GitHub action successfully generates the documentation site. Below is an +example showing the expected documentation formats. + +```cpp +#define PI 3.1415 /**< Inline doxygen format, e.g. for macros or struct members */ + +/******************************************************************************* + * This is a brief description of the function. + * + * This is an optional, longer description of the function. It can include + * LaTeX formatting, for example: this function doubles its input @f$ x @f$ and + * returns a value @f$ y @f$ with @f$ y = 2x @f$. + * + * @param[in] x The input and its expected units + * @return The result @f$ y = 2x @f$ + ******************************************************************************/ +double doubleTheInput(double x) +{ + return 2 * x; +} +``` + +### Python Wrappers + +The Python wrapper code is documented in the [Sphinx](https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html) +format. It is recommended to include docstrings for all primary functions, classes, +or structures provided by the Python wrapper. Further, function signatures should +include [type annotation](https://docs.python.org/3/library/typing.html) for inputs +and returned values. Inline or other comments should be included to explain other +variables or functionalities of the code. Below is an example showing the recommended +documentation format. + +```python + +CONSTANT_EXPOSED_BY_MODULE = 42 # A brief comment could explain what this is + +def double_the_input(x: float) -> float: + """This is a brief description of the function. + + This is an optional, longer description of the function. + It can span multiple lines. + + :param x: The input value, and its expected units. + :return: The result y = 2*x + """ + return 2 * x +``` + +### C#/.NET Wrappers + +In C#/.NET, documentation comments are written in XML format and are used to +generate documentation through tools like Visual Studio. Use `` tags to +provide brief descriptions of classes, constants, functions, etc. Functions should +include `` and `` elements for all inputs and outputs. An example +of this documentation style is shown below. + +```csharp +/// +/// Represents a class that contains constants and methods related to calculations. +/// +public class CalculationUtils +{ + /// + /// A constant value exposed by the module. + /// + public const int CONSTANT_EXPOSED_BY_MODULE = 42; + + /// + /// Doubles the input value. + /// + /// The input value to be doubled. + /// The doubled value of the input. + public double DoubleTheInput(double x) + { + // Brief comment explaining what this function does. + return 2 * x; + } +} +``` diff --git a/GitHubRepoPublicReleaseApproval.md b/GitHubRepoPublicReleaseApproval.md new file mode 100644 index 0000000..defb6c7 --- /dev/null +++ b/GitHubRepoPublicReleaseApproval.md @@ -0,0 +1,41 @@ +# GitHub Repository Public Release Approval + +**Project Name:** NTIA/OSM Research and Development + +**Software Name:** Recommendation ITU-R P.2108 - U.S. Reference Implementation + +The project identified above, which is contained within the repository this +document is stored in, has met the following criteria for public release: + +1. [x] The project, including the test criteria, meets the requirements defined +in the ITS Software Development Publication Policy for making a repository public. +The major pre-established criteria for publication are listed below, and the check +mark next to each attests that the criterion has been met. + * [x] The repository contains unit tests and test data, and the software has + been tested against these. + * [x] The C++ header files required for this software are located in a folder + which is named for their location within the ITS Propagation Library namespace, + e.g. `include/ITS.Propagation.ITM`. + * [x] The repository contains a top-level `CMakeLists.txt` which can be used + to compile the software on Windows, Linux, and macOS. + * [x] A GitHub action is provided in `.github/workflows` which compiles and + tests the software on each of these operating systems. + * [x] Any wrappers which provide bindings for this software in languages other + than C++ are included as Git submodules. + * [x] The repository includes the ITS Propagation Library `.clang-format` file, + and all C++ source code has been auto-formatted using ClangFormat and this file. + * [x] The repository includes the appropriate `LICENSE.md` file +2. [x] Any test data necessary for the code to function is included in this GitHub +repository. +3. [x] The README.md file is complete. +4. [x] The project complies with the ITS Code Style Guide or an appropriate style +guide as agreed to by the sponsor, project lead, or Supervising Division Chief. +5. [x] Approved disclaimer and licensing language has been included. + +In order to complete this approval, please create a new branch, upload and commit +your version of this Markdown document to that branch, then create a pull request +for that branch. The following must login to GitHub and approve that pull request +before the pull request can be merged and this repo made public: + +* Project Lead +* Supervising Division Chief or Release Authority diff --git a/README.md b/README.md index 34eb571..5723fee 100644 --- a/README.md +++ b/README.md @@ -1,165 +1,92 @@ -# Recommendation ITU-R P.2108-1 - U.S. Reference Implementation # - -Persistent Identifier: [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7114033.svg)](https://doi.org/10.5281/zenodo.7114033) +# Recommendation ITU-R P.2108 - U.S. Reference Implementation # + + +[![Unit Tests Status][gh-actions-test-badge]][gh-actions-test-link] +[![C++ API Reference][gh-actions-docs-badge]][gh-actions-docs-link] +![GitHub Release][gh-releases-badge] +![GitHub Issues][gh-issues-badge] + + +[gh-actions-test-link]: https://github.com/NTIA/p2108/actions/workflows/ctest.yml +[gh-actions-test-badge]: https://github.com/NTIA/p2108/actions/workflows/ctest.yml/badge.svg?branch=main +[gh-actions-docs-link]: https://github.com/NTIA/p2108/actions/workflows/doxygen.yml +[gh-actions-docs-badge]: https://github.com/NTIA/p2108/actions/workflows/doxygen.yml/badge.svg?branch=main +[gh-releases-badge]: https://img.shields.io/github/v/release/NTIA/P2108 +[gh-issues-badge]: https://img.shields.io/github/issues/NTIA/P2108 + + This code repository contains the U.S. Reference Software Implementation of Recommendation ITU-R P.2108. This Recommendation contains three methods for the -prediction of clutter loss: [Height Gain Terminal Correction Model](README.md#height-gain-terminal-correction-model), -[Terrestrial Statistical Model](README.md#terrestrial-statistical-model), -[Aeronautical Statistical Model](README.md#aeronautical-statistical-model). -The software implements Section 3 of Annex 1 of the Recommendation. - -## Height Gain Terminal Correction Model ## - -The height gain terminal correction model is described in Section 3.1. -This end-point clutter method gives the median loss due to different terminal -surroundings for terrestrial paths for frequencies between 0.3 to 3 GHz. This -model can be applied to both transmitting and receiving ends of the path. - -### Inputs (Height Gain Terminal Correction Model) ### - -| Variable | Type | Units | Limits | Description | -|----------------|--------|-------|----------------------|-------------------------------| -| `f__ghz` | double | GHz | 0.3 <= `f__ghz` <= 3 | Frequency | -| `h__meter` | double | meter | 0 <= `h__meter` | Antenna height | -| `w_s__meter` | double | meter | 0 < `w_s__meter` | Street width | -| `R__meter` | double | meter | 0 < `R__meter` | Representative clutter height | -| `clutter_type` | int | | | Clutter type
  • 1 = Water/sea
  • 2 = Open/rural
  • 3 = Suburban
  • 4 = Urban
  • 5 = Trees/forest
  • 6 = Dense urban
| - -Where site-specific values for the representative clutter height are not available, -the following default values are recommended. - -| Clutter Type | `R__meter` | -|--------------|:----------:| -| Water/sea | 10 | -| Open/rural | 10 | -| Suburban | 10 | -| Urban | 15 | -| Trees/forest | 15 | -| Dense urban | 20 | - -### Outputs (Height Gain Terminal Correction Model) ### - -| Variable | Type | Units | Description | -|------------|--------|-------|-------------| -| `A_h__db` | double | dB | Additional loss (clutter loss) | - -### Return Codes (Height Gain Terminal Correction Model) ### - -Possible return codes, including the corresponding defined constant name as -defined in [`Errors.h`](include/Errors.h): - -| Value | Const Name | Description | -|-------|---------------------------|----------------------------------------------------| -| 0 | `SUCCESS` | Successful execution | -| 3100 | `ERROR31__FREQUENCY` | Frequency must be between 0.3 and 3 GHz, inclusive | -| 3101 | `ERROR31__ANTENNA_HEIGHT` | Antenna height must be >= 0 meters | -| 3102 | `ERROR31__STREET_WIDTH` | Street width must be > 0 meters | -| 3103 | `ERROR31__CLUTTER_HEIGHT` | Representative clutter height must be > 0 meters | -| 3104 | `ERROR31__CLUTTER_TYPE` | Invalid value for clutter type | - -## Terrestrial Statistical Model ## +prediction of clutter loss: the Height Gain Terminal Correction Model, the +Terrestrial Statistical Model, and the Aeronautical Statistical Model. The software +implements Section 3 of Annex 1 of the Recommendation. -The statistical clutter loss model for terrestrial paths as described in Section -3.2. This model is valid for urban and suburban clutter environments. For paths -between 0.25 and 1 km, this model can only be applied to one end of the path. -For paths greater than 1 km, the model can be applied to both terminals, if desired. +## Getting Started ## -### Inputs (Terrestrial Statistical Model) ### +To get started using this model, refer to +[its page on the **NTIA/ITS Propagation Library Wiki**](https://ntia.github.io/propagation-library-wiki/models/P2108/). +There, you will find installation instructions, usage information, and code +examples for all supported languages. -| Variable | Type | Units | Limits | Description | -|----------|--------|-------|---------------------|---------------| -| `f__ghz` | double | GHz | 2 <= `f__ghz` <= 67 | Frequency | -| `d__km` | double | km | 0.25 <= `d__km` | Path distance | -| `p` | double | % | 0 < `p` < 100 | Percentage of locations clutter loss not exceeded | +If you're a developer and would like to contribute to or extend this repository, +you will find comprehensive documentation of this C++ code +[here](https://ntia.github.io/P2108), and a guide for contributors +[here](CONTRIBUTING.md). -### Outputs (Terrestrial Statistical Model) ### - -| Variable | Type | Units | Description | -|------------|--------|-------|-------------| -| `L_ctt__db` | double | dB | Clutter loss | - -### Return Codes (Terrestrial Statistical Model) ### - -Possible return codes, including the corresponding defined constant name as -defined in [`Errors.h`](include/Errors.h): - -| Value | Const Name | Description | -|-------|-----------------------|---------------------------------------------------| -| 0 | `SUCCESS` | Successful execution | -| 3200 | `ERROR32__FREQUENCY` | Frequency must be between 2 and 67 GHz, inclusive | -| 3201 | `ERROR32__DISTANCE` | Path distance must be >= 0.25 km | -| 3202 | `ERROR32__PERCENTAGE` | Percentage must be between 0 and 100 | - -## Aeronautical Statistical Model ## - -The Earth-space and aeronautical statistical clutter loss model as described in -Section 3.3. This model is applicable when one end of the path is within man-made -clutter and the other end is a satellite, aeroplane, or other platform above the -Earth. This model is valid for urban and suburban clutter environments. - -### Inputs (Aeronautical Statistical Model) ### - -| Variable | Type | Units | Limits | Description | -|--------------|--------|-------|-------------------------|-----------------| -| `f__ghz` | double | GHz | 10 <= `f__ghz` <= 100 | Frequency | -| `theta__deg` | double | deg | 0 <= `theta__deg` <= 90 | Elevation angle | -| `p` | double | % | 0 < `p` < 100 | Percentage of locations clutter loss not exceeded | - -### Outputs (Aeronautical Statistical Model) ### - -| Variable | Type | Units | Description | -|------------|--------|-------|-------------| -| `L_ces__db` | double | dB | Clutter loss | - -### Return Codes (Aeronautical Statistical Model) ### +## Configure and Build ## -Possible return codes, including the corresponding defined constant name as defined -in [`Errors.h`](include/Errors.h): +The software is designed to be built into a DLL (or corresponding `.so` or `.dylib` +library for non-Windows systems). A CMake build configuration and presets are +provided for cross-platform builds, which can be carried out, for example, by: -| Value | Const Name | Description | -|-------|-----------------------|----------------------------------------------------------| -| 0 | `SUCCESS` | Successful execution | -| 3300 | `ERROR33__FREQUENCY` | Frequency must be between 10 and 100 GHz, inclusive | -| 3301 | `ERROR33__THETA` | Elevation angle must be between 0 and 100 GHz, inclusive | -| 3302 | `ERROR33__PERCENTAGE` | Percentage must be between 0 and 100, inclusive | +```cmd +# From this repository's root directory, try one of the following command pairs: -## Example Values ## +# "Release" configurations compile the library, build docs, and configure tests: +cmake --preset release64 +cmake --build --preset release64 -The [Study Group Clutter Excel Workbook](https://www.itu.int/en/ITU-R/study-groups/rsg3/ionotropospheric/Clutter%20and%20BEL%20workbook_V2.xlsx) -contains an extensive set of validation example values. +# "Debug" configurations skip building the docs: +cmake --preset debug64 +cmake --build --preset debug64 -## Notes on Code Style ## +# "DocsOnly" configurations only build the docs: +cmake --preset docsOnly +cmake --build --preset docsOnly +``` -* In general, variables follow the naming convention in which a single underscore -denotes a subscript (pseudo-LaTeX format), where a double underscore is followed -by the units, i.e. `h_1__meter`. -* Variables are named to match their corresponding mathematical variables in the -underlying Recommendation text. -* Wherever possible, equation numbers are provided. It is assumed that a user -reviewing this source code would have a copy of the Recommendation's text available -as a primary reference. +Note that this repository makes use of several +[Git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) +to reference dependencies used for running unit tests and building documentation. +In order to do either, ensure the required submodules are cloned by running: -## Configure and Build ## +```cmd +# From this repository's root directory +git submodule init extern/googletest # Required to run tests +git submodule init extern/doxygen-awesome-css # Required to build docs +git submodule update # Clones the initialized submodules +``` -### C++ Software ### +## Running Tests ## -The software is designed to be built into a DLL (or corresponding library for -non-Windows systems). The source code can be built for any OS that supports the -standard C++ libraries. A Visual Studio 2019 project file is provided for Windows -users to support the build process and configuration. +If you've configured tests when building the project, for example by using one of +the "Release" or "Debug" CMake presets, you can run the included unit tests as follows: -### C#/.NET Wrapper Software ### +```cmd +ctest --preset release64 +``` -The .NET support of P.2108 consists of a simple pass-through wrapper around the -native DLL. It is compiled to target .NET Framework 4.8.1. Distribution and updates -are provided through the published [NuGet package](https://github.com/NTIA/p2108/packages). +Additionally, the [Study Group Clutter Excel Workbook](https://www.itu.int/en/ITU-R/study-groups/rsg3/ionotropospheric/Clutter%20and%20BEL%20workbook_V2.xlsx) +contains an extensive set of example values which are useful as validation cases. ## References ## +* [ITS Propagation Library Wiki](https://ntia.github.io/propagation-library-wiki) +* [`ITS.ITU.PSeries.P2108` C++ API Reference](https://ntia.github.io/P2108) * [Recommendation ITU-R P.2108](https://www.itu.int/rec/R-REC-P.2108/en) * [Report ITU-R P.2402](https://www.itu.int/pub/R-REP-P.2402) ## Contact ## -For questions, contact Billy Kozma, +For technical questions, contact . diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt new file mode 100644 index 0000000..1125dc1 --- /dev/null +++ b/docs/CMakeLists.txt @@ -0,0 +1,66 @@ +########################################### +## FIND DOXYGEN AND DOXYGEN-AWESOME-CSS +########################################### +# Doxygen >=1.11.0 is required to properly render the header +set(MINIMUM_DOXYGEN_VERSION "1.11") +find_package(Doxygen ${MINIMUM_DOXYGEN_VERSION} REQUIRED doxygen) +# find_package will cause an error and exit if package is not found + +# Ensure doxygen-awesome-css submodule has been initialized +set(EXTRA_STYLESHEET "${PROJECT_SOURCE_DIR}/extern/doxygen-awesome-css/doxygen-awesome.css") +if (NOT EXISTS ${EXTRA_STYLESHEET}) + message(FATAL_ERROR + "External Doxygen stylesheet is missing! " + "Run `git submodule init extern/doxygen-awesome-css`, then " + "`git submodule update` and try again." + ) +endif () + +########################################### +## CONFIGURE DOXYGEN +########################################### +set(DOCS_DIR "${PROJECT_SOURCE_DIR}/docs") +set(DOXYGEN_ALIASES "libname=${LIB_NAME}") # Used to populate library name on main page +set(DOXYGEN_PROJECT_NAME "${CMAKE_PROJECT_NAME}") +set(DOXYGEN_BUILTIN_STL_SUPPORT "YES") +set(DOXYGEN_DISABLE_INDEX "NO") +set(DOXYGEN_EXCLUDE "${PROJECT_SOURCE_DIR}/tests/*") +set(DOXYGEN_FULL_SIDEBAR "NO") +set(DOXYGEN_GENERATE_LATEX "NO") +set(DOXYGEN_GENERATE_TREEVIEW "NO") +set(DOXYGEN_HTML_COLORSTYLE "LIGHT") # Required for doxygen-awesome-css +set(DOXYGEN_HTML_EXTRA_FILES + "${DOCS_DIR}/images/ITSlogoOnly400.png" + "${DOCS_DIR}/images/favicon-16x16.png" + "${DOCS_DIR}/images/favicon-32x32.png" + "${DOCS_DIR}/images/apple-touch-icon.png") +set(DOXYGEN_HTML_EXTRA_STYLESHEET "${EXTRA_STYLESHEET}" "${DOCS_DIR}/doxy_custom.css") +set(DOXYGEN_HTML_FOOTER "${DOCS_DIR}/doxy_footer.html") +set(DOXYGEN_HTML_HEADER "${DOCS_DIR}/doxy_header.html") +set(DOXYGEN_JAVADOC_AUTOBRIEF "YES") +set(DOXYGEN_JAVADOC_BANNER "YES") +set(DOXYGEN_OUTPUT_DIRECTORY "${DOCS_DIR}") +set(DOXYGEN_PREDEFINED "DOXYGEN_SHOULD_SKIP") +set(DOXYGEN_PROJECT_BRIEF "Part of the NTIA/ITS Propagation Library") +set(DOXYGEN_PROJECT_LOGO "${DOCS_DIR}/images/ntia-logo-300px.png") +set(DOXYGEN_REPEAT_BRIEF "YES") +set(DOXYGEN_SHOW_INCLUDE_FILES "NO") +set(DOXYGEN_USE_MATHJAX "YES") +set(DOXYGEN_USE_MDFILE_AS_MAINPAGE "${DOCS_DIR}/doxy_mainpage.md") +set(DOXYGEN_WARN_AS_ERROR "YES") +set(DOXYGEN_WARN_IF_UNDOC_ENUM_VAL "YES") +set(DOXYGEN_WARN_NO_PARAMDOC "YES") + +# Doxygen docs are a developer, not user, reference. +# Therefore, document private and internal code +set(DOXYGEN_EXTRACT_PRIVATE "YES") +set(DOXYGEN_INTERNAL_DOCS "YES") + +doxygen_add_docs( + "${CMAKE_PROJECT_NAME}-Docs" + "${PROJECT_SOURCE_DIR}/src" + "${PROJECT_SOURCE_DIR}/include" + "${DOCS_DIR}/doxy_mainpage.md" + ALL + COMMENT "Generate HTML documentation with Doxygen" +) \ No newline at end of file diff --git a/docs/doxy_custom.css b/docs/doxy_custom.css new file mode 100644 index 0000000..d2ccfca --- /dev/null +++ b/docs/doxy_custom.css @@ -0,0 +1,44 @@ +.footer-content { + display: flex; + flex-wrap: wrap; /* Allow items to wrap to the next line */ + justify-content: space-between; /* Center the flex items horizontally */ + max-width: 1040px; /* Optional: Set a max-width for the container */ + margin: 0 auto; /* Auto margin horizontally centers the container */ + padding: 0px; /* Optional: Add padding around the container */ + box-sizing: border-box; /* Include padding and border in width calculation */ +} + +.footer-column-left, +.footer-column-right { + width: calc( + 50% - 10px + ); /* Each column takes up 50% of the container width minus padding */ + box-sizing: border-box; /* Include padding and border in width calculation */ + padding: 20px; /* Example padding for columns */ + + h2, + p { + margin-bottom: 0; + margin-top: 0; + } +} + +.footer-column-right { + text-align: right; /* Align text to the right within elements in the right column */ + h2 { + text-align: right; + margin-right: 0; + } +} + +/* Media query for mobile devices */ +@media (max-width: 768px) { + .footer-content { + flex-direction: column; /* Stack items vertically on smaller screens */ + } + .footer-column-left, + .footer-column-right { + width: 100%; /* Each column takes up 100% of the container width (stacked vertically) */ + padding: 20px; /* Reset padding for mobile layout */ + } +} diff --git a/docs/doxy_footer.html b/docs/doxy_footer.html new file mode 100644 index 0000000..6bb41ea --- /dev/null +++ b/docs/doxy_footer.html @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + diff --git a/docs/doxy_header.html b/docs/doxy_header.html new file mode 100644 index 0000000..abbf188 --- /dev/null +++ b/docs/doxy_header.html @@ -0,0 +1,113 @@ + + + + + + + + +$projectname API Reference: $title +$title + + + + + + + + + + + + + + +$treeview +$search +$mathjax +$darkmode + +$extrastylesheet + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
$projectname $projectnumber +
+
$projectbrief
+
+
$projectbrief
+
$searchbox
$searchbox
+
+ + + \ No newline at end of file diff --git a/docs/doxy_mainpage.md b/docs/doxy_mainpage.md new file mode 100644 index 0000000..18415f8 --- /dev/null +++ b/docs/doxy_mainpage.md @@ -0,0 +1,32 @@ +# Main Page + +This website is an information-oriented API reference document for the @libname +C++ library, a part of the NTIA/ITS Propagation Library. This site is primarily +useful for developers wishing to contribute to this library or take it as a dependency. + +**For most users, the best place to start is the** +[**NTIA/ITS Propagation Library Wiki**](https://ntia.github.io/propagation-library-wiki). + +On the wiki, you'll find installation instructions, usage guides, and code examples +for this and other software within the NTIA/ITS Propagation Library. Further, the +wiki includes instructions for using this library from other software languages, +including Python, MATLAB, and C#/.NET. + +## Site Navigation + +Please use the navigation menu and search functionality to explore this reference +documentation. The "Files" navigation menu includes the following notable options: + +- [File List](files.html) provides a browsable overview of the source code directories. +- [File Members - All](globals.html) lists all documents file members alphabetically. + +Additional pages listed under "File Members" allow for browsing based on member types, +e.g. classes, functions, etc. + +## Generating this Documentation + +This site is generated with [Doxygen](https://www.doxygen.nl/), which is configured +in the source project using [CMake](https://cmake.org/). The documentation is generated +by default when building the project in its release configuration. Additionally, +the documentation can be generated without compiling the source project by using +the `DOCS_ONLY` CMake option. diff --git a/docs/images/ITSlogoOnly400.png b/docs/images/ITSlogoOnly400.png new file mode 100644 index 0000000..45a7ba3 Binary files /dev/null and b/docs/images/ITSlogoOnly400.png differ diff --git a/docs/images/apple-touch-icon.png b/docs/images/apple-touch-icon.png new file mode 100644 index 0000000..d6371ec Binary files /dev/null and b/docs/images/apple-touch-icon.png differ diff --git a/docs/images/favicon-16x16.png b/docs/images/favicon-16x16.png new file mode 100644 index 0000000..cc0687f Binary files /dev/null and b/docs/images/favicon-16x16.png differ diff --git a/docs/images/favicon-32x32.png b/docs/images/favicon-32x32.png new file mode 100644 index 0000000..db8b740 Binary files /dev/null and b/docs/images/favicon-32x32.png differ diff --git a/docs/images/ntia-logo-300px.png b/docs/images/ntia-logo-300px.png new file mode 100644 index 0000000..4e22859 Binary files /dev/null and b/docs/images/ntia-logo-300px.png differ diff --git a/dotnet/ITS.ITU.PSeries.P2108/ITS.ITU.PSeries.P2108.csproj b/dotnet/ITS.ITU.PSeries.P2108/ITS.ITU.PSeries.P2108.csproj deleted file mode 100644 index 517e36a..0000000 --- a/dotnet/ITS.ITU.PSeries.P2108/ITS.ITU.PSeries.P2108.csproj +++ /dev/null @@ -1,50 +0,0 @@ - - - - - Debug - AnyCPU - {6AA0C335-F019-4818-97B6-7B32BBE190E4} - Library - Properties - ITS.ITU.PSeries - ITS.ITU.PSeries.P2108 - v4.8.1 - 512 - true - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - bin\Release\ITS.ITU.PSeries.P2108.xml - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dotnet/ITS.ITU.PSeries.P2108/P2108.cs b/dotnet/ITS.ITU.PSeries.P2108/P2108.cs deleted file mode 100644 index e560cc1..0000000 --- a/dotnet/ITS.ITU.PSeries.P2108/P2108.cs +++ /dev/null @@ -1,137 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace ITS.ITU.PSeries -{ - /// - /// Recommendation ITU-R P.2108-1 - /// - public static class P2108 - { - private const string P2108_x86_DLL_NAME = "p2108_x86.dll"; - private const string P2108_x64_DLL_NAME = "p2108_x64.dll"; - - /// - /// Clutter types for Height Gain Terminal Correction Model - /// - public enum ClutterType : int - { - /// - /// Water/sea - /// - WaterSea = 1, - - /// - /// Open/rural - /// - OpenRural = 2, - - /// - /// Suburban - /// - Suburban = 3, - - /// - /// Urban - /// - Urban = 4, - - /// - /// Trees/forest - /// - TreesForest = 5, - - /// - /// Dense urban - /// - DenseUrban = 6 - } - - #region 32-Bit P/Invoke Definitions - - [DllImport(P2108_x86_DLL_NAME, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "AeronauticalStatisticalModel")] - private static extern int AeronauticalStatisticalModel_x86(double f__ghz, double theta__deg, double p, out double L_ces__db); - - [DllImport(P2108_x86_DLL_NAME, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "HeightGainTerminalCorrectionModel")] - private static extern int HeightGainTerminalCorrectionModel_x86(double f__ghz, double h__meter, double w_s__meter, double R__meter, int clutter_type, out double A_h__db); - - [DllImport(P2108_x86_DLL_NAME, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "TerrestrialStatisticalModel")] - private static extern int TerrestrialStatisticalModel_x86(double f__ghz, double d__km, double p, out double L_ctt__db); - - #endregion - - #region 64-Bit P/Invoke Definitions - - [DllImport(P2108_x64_DLL_NAME, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "AeronauticalStatisticalModel")] - private static extern int AeronauticalStatisticalModel_x64(double f__ghz, double theta__deg, double p, out double L_ces__db); - - [DllImport(P2108_x64_DLL_NAME, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "HeightGainTerminalCorrectionModel")] - private static extern int HeightGainTerminalCorrectionModel_x64(double f__ghz, double h__meter, double w_s__meter, double R__meter, int clutter_type, out double A_h__db); - - [DllImport(P2108_x64_DLL_NAME, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "TerrestrialStatisticalModel")] - private static extern int TerrestrialStatisticalModel_x64(double f__ghz, double d__km, double p, out double L_ctt__db); - - #endregion - - private delegate int AeronauticalStatisticalModelDelegate(double f__ghz, double theta__deg, double p, out double L_ces__db); - private delegate int HeightGainTerminalCorrectionModelDelegate(double f__ghz, double h__meter, double w_s__meter, double R__meter, int clutter_type, out double A_h__db); - private delegate int TerrestrialStatisticalModelDelegate(double f__ghz, double d__km, double p, out double L_ctt__db); - - private static readonly AeronauticalStatisticalModelDelegate AeronauticalStatisticalModel_Invoke; - private static readonly HeightGainTerminalCorrectionModelDelegate HeightGainTerminalCorrectionModel_Invoke; - private static readonly TerrestrialStatisticalModelDelegate TerrestrialStatisticalModel_Invoke; - - static P2108() - { - // set the binding to the correct native DLL architecture - - if (Environment.Is64BitProcess) - { - AeronauticalStatisticalModel_Invoke = AeronauticalStatisticalModel_x64; - HeightGainTerminalCorrectionModel_Invoke = HeightGainTerminalCorrectionModel_x64; - TerrestrialStatisticalModel_Invoke = TerrestrialStatisticalModel_x64; - } - else - { - AeronauticalStatisticalModel_Invoke = AeronauticalStatisticalModel_x86; - HeightGainTerminalCorrectionModel_Invoke = HeightGainTerminalCorrectionModel_x86; - TerrestrialStatisticalModel_Invoke = TerrestrialStatisticalModel_x86; - } - } - - /// - /// The Earth-space and aeronautical statistical clutter loss model as described in Section 3.3. - /// - /// Frequency, in GHz - /// Elevation angle, in degrees - /// Percentage of locations, in % - /// Additional loss (clutter loss), in dB - /// Error code - public static int AeronauticalStatisticalModel(double f__ghz, double theta__deg, double p, out double L_ces__db) - => AeronauticalStatisticalModel_Invoke(f__ghz, theta__deg, p, out L_ces__db); - - /// - /// Height gain terminal correction model as described in Section 3.1. - /// - /// Frequency, in GHz - /// Antenna height, in meters - /// Street width, in meters - /// Representative clutter height, in meters - /// Clutter type - /// Additional loss (clutter loss), in dB - /// Error code - public static int HeightGainTerminalCorrectionModel(double f__ghz, double h__meter, double w_s__meter, double R__meter, ClutterType clutter_type, out double A_h__db) - => HeightGainTerminalCorrectionModel_Invoke(f__ghz, h__meter, w_s__meter, R__meter, (int)clutter_type, out A_h__db); - - /// - /// Statistical clutter loss model for terrestrial paths as described in Section 3.2. - /// - /// Frequency, in GHz - /// Path distance, in km - /// Percentage of locations, in % - /// Additional loss (clutter loss), in dB - /// Error code - public static int TerrestrialStatisticalModel(double f__ghz, double d__km, double p, out double L_ctt__db) - => TerrestrialStatisticalModel_Invoke(f__ghz, d__km, p, out L_ctt__db); - } -} diff --git a/dotnet/ITS.ITU.PSeries.P2108/Properties/AssemblyInfo.cs b/dotnet/ITS.ITU.PSeries.P2108/Properties/AssemblyInfo.cs deleted file mode 100644 index 60f50ac..0000000 --- a/dotnet/ITS.ITU.PSeries.P2108/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Recommendation ITU-R P.2108-1")] -[assembly: AssemblyDescription("Recommendation ITU-R P.2108-1")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("The Institute for Telecommunication Sciences")] -[assembly: AssemblyProduct("Recommendation ITU-R P.2108-1")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("6aa0c335-f019-4818-97b6-7b32bbe190e4")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/dotnet/UnitTests/BVT.cs b/dotnet/UnitTests/BVT.cs deleted file mode 100644 index 042a4af..0000000 --- a/dotnet/UnitTests/BVT.cs +++ /dev/null @@ -1,69 +0,0 @@ -using ITS.ITU.PSeries; -using Xunit; - -namespace UnitTests -{ - public class BVT - { - const double EPSILON = 0.1; - - /// - /// Tests for Sec 3.1 Height Gain Terminal Correction clutter model - /// - /// Frequency, in GHz - /// Antenna height, in meters - /// Street width, in meters - /// Representative clutter height, in meters - /// Clutter type - /// Return code - /// Additional loss (clutter loss), in dB - [Theory] - [MemberData(nameof(TestData.HeightGainTerminalCorrectionModelTestData), MemberType = typeof(TestData))] - public void HeightGainTerminalCorrectionModelTest(double f__ghz, - double h__meter, double w_s__meter, double R__meter, - P2108.ClutterType clutter_type, int rtn, double A_h__db) - { - var r = P2108.HeightGainTerminalCorrectionModel(f__ghz, h__meter, w_s__meter, R__meter, clutter_type, out double A__db); - - Assert.Equal(rtn, r); - Assert.Equal(A_h__db, A__db, EPSILON); - } - - /// - /// Tests for Sec 3.2 Terrestrial Statistical clutter model - /// Frequency, in GHz - /// Path distance, in km - /// Percentage of locations, in % - /// Return code - /// Additional loss (clutter loss), in dB - [Theory] - [MemberData(nameof(TestData.TerrestrialStatisticalModelTestData), MemberType = typeof(TestData))] - public void TerrestrialStatisticalModelTest(double f__ghz, - double d__km, double p, int rtn, double L_ctt__db) - { - var r = P2108.TerrestrialStatisticalModel(f__ghz, d__km, p, out double L__db); - - Assert.Equal(rtn, r); - Assert.Equal(L_ctt__db, L__db, EPSILON); - } - - /// - /// Tests for Sec 3.3 Aeronautical Statistical clutter model - /// - /// Frequency, in GHz - /// Elevation angle, in degrees - /// Percentage of locations, in % - /// Return code - /// Additional loss (clutter loss), in dB - [Theory] - [MemberData(nameof(TestData.AeronauticalStatisticalModelTestData), MemberType = typeof(TestData))] - public void AeronauticalStatisticalModelTest(double f__ghz, - double theta__deg, double p, int rtn, double L_ces__db) - { - var r = P2108.AeronauticalStatisticalModel(f__ghz, theta__deg, p, out double L__db); - - Assert.Equal(rtn, r); - Assert.Equal(L_ces__db, L__db, EPSILON); - } - } -} diff --git a/dotnet/UnitTests/Properties/AssemblyInfo.cs b/dotnet/UnitTests/Properties/AssemblyInfo.cs deleted file mode 100644 index eb90b8c..0000000 --- a/dotnet/UnitTests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("UnitTests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("UnitTests")] -[assembly: AssemblyCopyright("Copyright © 2023")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("1fb5483e-2eed-4e27-a1d4-16646507ec9f")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/dotnet/UnitTests/TestData.cs b/dotnet/UnitTests/TestData.cs deleted file mode 100644 index f3e65c5..0000000 --- a/dotnet/UnitTests/TestData.cs +++ /dev/null @@ -1,108 +0,0 @@ -using ITS.ITU.PSeries; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace UnitTests -{ - public class TestData - { - static readonly string[] _heightGainTestData; - static readonly string[] _terrestrialStatisticalTestData; - static readonly string[] _aeronauticalStatisticalTestData; - - public static IEnumerable HeightGainTerminalCorrectionModelTestData - { - get - { - foreach (var line in _heightGainTestData) - { - var parts = line.Split(','); - - // parse data line - double f__ghz = Convert.ToDouble(parts[0]); - double h__meter = Convert.ToDouble(parts[1]); - double w_s__meter = Convert.ToDouble(parts[2]); - double R__meter = Convert.ToDouble(parts[3]); - P2108.ClutterType clutter_type = (P2108.ClutterType)Convert.ToInt32(parts[4]); - int rtn = Convert.ToInt32(parts[5]); - double A_h__db = Convert.ToDouble(parts[6]); - - yield return new object[] - { - f__ghz, - h__meter, - w_s__meter, - R__meter, - clutter_type, - rtn, - A_h__db - }; - } - } - } - - public static IEnumerable TerrestrialStatisticalModelTestData - { - get - { - foreach (var line in _terrestrialStatisticalTestData) - { - var parts = line.Split(','); - - // parse data line - double f__ghz = Convert.ToDouble(parts[0]); - double d__km = Convert.ToDouble(parts[1]); - double p = Convert.ToDouble(parts[2]); - int rtn = Convert.ToInt32(parts[3]); - double L_ctt__db = Convert.ToDouble(parts[4]); - - yield return new object[] - { - f__ghz, - d__km, - p, - rtn, - L_ctt__db - }; - } - } - } - - public static IEnumerable AeronauticalStatisticalModelTestData - { - get - { - foreach (var line in _aeronauticalStatisticalTestData) - { - var parts = line.Split(','); - - // parse data line - double f__ghz = Convert.ToDouble(parts[0]); - double theta__deg = Convert.ToDouble(parts[1]); - double p = Convert.ToDouble(parts[2]); - int rtn = Convert.ToInt32(parts[3]); - double L_ces__db = Convert.ToDouble(parts[4]); - - yield return new object[] - { - f__ghz, - theta__deg, - p, - rtn, - L_ces__db - }; - } - } - } - - static TestData() - { - // load test data from file - _heightGainTestData = File.ReadAllLines("HeightGainTerminalCorrectionModelTestData.csv").Skip(1).ToArray(); - _terrestrialStatisticalTestData = File.ReadAllLines("TerrestrialStatisticalModelTestData.csv").Skip(1).ToArray(); - _aeronauticalStatisticalTestData = File.ReadAllLines("AeronauticalStatisticalModelTestData.csv").Skip(1).ToArray(); - } - } -} diff --git a/dotnet/UnitTests/Unit-Test-Instructions.txt b/dotnet/UnitTests/Unit-Test-Instructions.txt deleted file mode 100644 index 5ec611b..0000000 --- a/dotnet/UnitTests/Unit-Test-Instructions.txt +++ /dev/null @@ -1,7 +0,0 @@ -Instructions for running unit tests ------------------------------------ -1. Build C++ code in x64 and x86 configuration. -2. Rename C++ DLLs with corresponding suffix '_x64' or '_x86', accordingly. These - are the DLL names that .NET class is looking for. -3. Move C++ DLLs to the Debug directory where the UnitTests project is built. -4. Use Visual Studio Text Explorer to run the tests. \ No newline at end of file diff --git a/dotnet/UnitTests/UnitTests.csproj b/dotnet/UnitTests/UnitTests.csproj deleted file mode 100644 index 7866218..0000000 --- a/dotnet/UnitTests/UnitTests.csproj +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - Debug - AnyCPU - {1FB5483E-2EED-4E27-A1D4-16646507EC9F} - Library - Properties - UnitTests - UnitTests - v4.8.1 - 512 - true - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - ..\packages\xunit.abstractions.2.0.3\lib\net35\xunit.abstractions.dll - - - ..\packages\xunit.assert.2.4.2\lib\netstandard1.1\xunit.assert.dll - - - ..\packages\xunit.extensibility.core.2.4.2\lib\net452\xunit.core.dll - - - ..\packages\xunit.extensibility.execution.2.4.2\lib\net452\xunit.execution.desktop.dll - - - - - - - - - - {6aa0c335-f019-4818-97b6-7b32bbe190e4} - ITS.ITU.PSeries.P2108 - - - - - AeronauticalStatisticalModelTestData.csv - Always - - - HeightGainTerminalCorrectionModelTestData.csv - Always - - - TerrestrialStatisticalModelTestData.csv - Always - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - \ No newline at end of file diff --git a/dotnet/UnitTests/packages.config b/dotnet/UnitTests/packages.config deleted file mode 100644 index 1be12c9..0000000 --- a/dotnet/UnitTests/packages.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/dotnet/nuget/build/net481/p2108.targets b/dotnet/nuget/build/net481/p2108.targets deleted file mode 100644 index 6e0352c..0000000 --- a/dotnet/nuget/build/net481/p2108.targets +++ /dev/null @@ -1,16 +0,0 @@ - - - - - PreserveNewest - p2108_x86.dll - - - - - - PreserveNewest - p2108_x64.dll - - - \ No newline at end of file diff --git a/dotnet/nuget/images/itslogo.png b/dotnet/nuget/images/itslogo.png deleted file mode 100644 index 3d2f319..0000000 Binary files a/dotnet/nuget/images/itslogo.png and /dev/null differ diff --git a/dotnet/nuget/p2108.nuspec b/dotnet/nuget/p2108.nuspec deleted file mode 100644 index 7722383..0000000 --- a/dotnet/nuget/p2108.nuspec +++ /dev/null @@ -1,30 +0,0 @@ - - - - P2108 - 1.0.0 - The Institute for Telecommunication Sciences - The Institute for Telecommunication Sciences - LICENSE.md - https://github.com/NTIA/p2108 - images\itslogo.png - true - Recommendation ITU-R P.2108: The prediction of clutter loss for terrestrial and airborne paths. This software package implements the three clutter prediction methods defined in the Section 3 of Annex 1. - None - ITU SG3 Propagation P2108 P.2108 - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dotnet/p2108_dotnet.sln b/dotnet/p2108_dotnet.sln deleted file mode 100644 index 1d1f703..0000000 --- a/dotnet/p2108_dotnet.sln +++ /dev/null @@ -1,34 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.33516.290 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ITS.ITU.PSeries.P2108", "ITS.ITU.PSeries.P2108\ITS.ITU.PSeries.P2108.csproj", "{6AA0C335-F019-4818-97B6-7B32BBE190E4}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{1FB5483E-2EED-4E27-A1D4-16646507EC9F}" - ProjectSection(ProjectDependencies) = postProject - {6AA0C335-F019-4818-97B6-7B32BBE190E4} = {6AA0C335-F019-4818-97B6-7B32BBE190E4} - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6AA0C335-F019-4818-97B6-7B32BBE190E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6AA0C335-F019-4818-97B6-7B32BBE190E4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6AA0C335-F019-4818-97B6-7B32BBE190E4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6AA0C335-F019-4818-97B6-7B32BBE190E4}.Release|Any CPU.Build.0 = Release|Any CPU - {1FB5483E-2EED-4E27-A1D4-16646507EC9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1FB5483E-2EED-4E27-A1D4-16646507EC9F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1FB5483E-2EED-4E27-A1D4-16646507EC9F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1FB5483E-2EED-4E27-A1D4-16646507EC9F}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {629E6553-3837-4848-B309-2AB6476F731A} - EndGlobalSection -EndGlobal diff --git a/extern/doxygen-awesome-css b/extern/doxygen-awesome-css new file mode 160000 index 0000000..568f56c --- /dev/null +++ b/extern/doxygen-awesome-css @@ -0,0 +1 @@ +Subproject commit 568f56cde6ac78b6dfcc14acd380b2e745c301ea diff --git a/extern/googletest b/extern/googletest new file mode 160000 index 0000000..58d77fa --- /dev/null +++ b/extern/googletest @@ -0,0 +1 @@ +Subproject commit 58d77fa8070e8cec2dc1ed015d66b454c8d78850 diff --git a/include/Consts.h b/include/Consts.h deleted file mode 100644 index c81d127..0000000 --- a/include/Consts.h +++ /dev/null @@ -1,21 +0,0 @@ - -/////////////////////////////////////////////// -// CONSTANTS -// - -// Clutter Types -#define CLUTTER_TYPE__WATER_SEA 1 -#define CLUTTER_TYPE__OPEN_RURAL 2 -#define CLUTTER_TYPE__SUBURBAN 3 -#define CLUTTER_TYPE__URBAN 4 -#define CLUTTER_TYPE__TREES_FOREST 5 -#define CLUTTER_TYPE__DENSE_URBAN 6 - -// Default Clutter Heights -#define DEFAULT_CLUTTER_HEIGHT__WATER_SEA 10 -#define DEFAULT_CLUTTER_HEIGHT__OPEN_RURAL 10 -#define DEFAULT_CLUTTER_HEIGHT__SUBURBAN 10 -#define DEFAULT_CLUTTER_HEIGHT__URBAN 15 -#define DEFAULT_CLUTTER_HEIGHT__TREES_FOREST 15 -#define DEFAULT_CLUTTER_HEIGHT__DENSE_URBAN 20 - diff --git a/include/Errors.h b/include/Errors.h deleted file mode 100644 index 63d9d98..0000000 --- a/include/Errors.h +++ /dev/null @@ -1,32 +0,0 @@ - -/////////////////////////////////////////////// -// GENERAL -// - -#define SUCCESS 0 - -/////////////////////////////////////////////// -// SECTION 3.1 ERROR CODES -// - -#define ERROR31__FREQUENCY 3100 -#define ERROR31__ANTENNA_HEIGHT 3101 -#define ERROR31__STREET_WIDTH 3102 -#define ERROR31__CLUTTER_HEIGHT 3103 -#define ERROR31__CLUTTER_TYPE 3104 - -/////////////////////////////////////////////// -// SECTION 3.2 ERROR CODES -// - -#define ERROR32__FREQUENCY 3200 -#define ERROR32__DISTANCE 3201 -#define ERROR32__PERCENTAGE 3202 - -/////////////////////////////////////////////// -// SECTION 3.3 ERROR CODES -// - -#define ERROR33__FREQUENCY 3300 -#define ERROR33__THETA 3301 -#define ERROR33__PERCENTAGE 3302 \ No newline at end of file diff --git a/include/ITS.ITU.PSeries.P2108/Enums.h b/include/ITS.ITU.PSeries.P2108/Enums.h new file mode 100644 index 0000000..d998982 --- /dev/null +++ b/include/ITS.ITU.PSeries.P2108/Enums.h @@ -0,0 +1,43 @@ +/** @file Enums.h + * Enumerated types used by this software + */ +#ifndef __ITS_ITU_PSERIES_P2108_ENUMS__ +#define __ITS_ITU_PSERIES_P2108_ENUMS__ + +namespace ITS { +namespace ITU { +namespace PSeries { +namespace P2108 { + +/** Clutter type enum, based on Table 3 in Section 3.1 */ +enum class ClutterType { + WATER_SEA = 1, /**< Water/sea clutter type */ + OPEN_RURAL = 2, /**< Open/rural clutter type */ + SUBURBAN = 3, /**< Suburban clutter type */ + URBAN = 4, /**< Urban clutter type */ + TREES_FOREST = 5, /**< Trees/forest clutter type */ + DENSE_URBAN = 6, /**< Dense urban clutter type */ +}; + +/** + * Default values of the representative clutter height @f$ R @f$, + * in meters, by clutter type. + * + * These should be used as inputs to the height gain terminal + * correction model when local information is not available. + */ +enum class RepresentativeClutterHeight { + WATER_SEA = 10, /**< @f$ R @f$ for the trees/forest clutter type */ + OPEN_RURAL = 10, /**< @f$ R @f$ for the open/rural clutter type */ + SUBURBAN = 10, /**< @f$ R @f$ for the suburban clutter type */ + URBAN = 15, /**< @f$ R @f$ for the urban clutter type */ + TREES_FOREST = 15, /**< @f$ R @f$ for the trees/forest clutter type */ + DENSE_URBAN = 20, /**< @f$ R @f$ for the dense urban clutter type */ +}; + +} // namespace P2108 +} // namespace PSeries +} // namespace ITU +} // namespace ITS + +#endif diff --git a/include/ITS.ITU.PSeries.P2108/Errors.h b/include/ITS.ITU.PSeries.P2108/Errors.h new file mode 100644 index 0000000..cc48e3f --- /dev/null +++ b/include/ITS.ITU.PSeries.P2108/Errors.h @@ -0,0 +1,43 @@ +/** @file Errors.h + * Contains return codes used by this software + */ +#ifndef __ITS_ITU_PSERIES_P2108_ERRORS__ +#define __ITS_ITU_PSERIES_P2108_ERRORS__ + +namespace ITS { +namespace ITU { +namespace PSeries { +namespace P2108 { + +/////////////////////////////////////////////// +// RETURN CODES + +// clang-format off + +#define SUCCESS 0 /**< Successful execution */ + +// Section 3.1 Error Codes +#define ERROR31__FREQUENCY 3100 /**< Frequency must be between 0.3 and 3 GHz, inclusive */ +#define ERROR31__ANTENNA_HEIGHT 3101 /**< Antenna height must be @f$ \geq @f$ 0 meters */ +#define ERROR31__STREET_WIDTH 3102 /**< Street width must be @f$ > @f$ 0 meters */ +#define ERROR31__CLUTTER_HEIGHT 3103 /**< Representative clutter height must be @f$ > @f$ 0 meters */ +#define ERROR31__CLUTTER_TYPE 3104 /**< Invalid value for clutter type */ + +// Section 3.2 Error Codes +#define ERROR32__FREQUENCY 3200 /**< Frequency must be between 2 and 67 GHz, inclusive */ +#define ERROR32__DISTANCE 3201 /**< Path distance must be @f$ \geq @f$ 0.25 km */ +#define ERROR32__PERCENTAGE 3202 /**< Percentage must be between 0 and 100 */ + +// Section 3.3 Error Codes +#define ERROR33__FREQUENCY 3300 /**< Frequency must be between 10 and 100 GHz, inclusive */ +#define ERROR33__THETA 3301 /**< Elevation angle must be between 0 and 100 GHz, inclusive */ +#define ERROR33__PERCENTAGE 3302 /**< Percentage must be between 0 and 100, inclusive */ + +// clang-format on + +} // namespace P2108 +} // namespace PSeries +} // namespace ITU +} // namespace ITS + +#endif diff --git a/include/ITS.ITU.PSeries.P2108/P2108.h b/include/ITS.ITU.PSeries.P2108/P2108.h new file mode 100644 index 0000000..a879c29 --- /dev/null +++ b/include/ITS.ITU.PSeries.P2108/P2108.h @@ -0,0 +1,89 @@ +/** @file P2108.h + * Interface header for this library + */ +#ifndef __ITS_ITU_PSERIES_P2108_P2108_H__ +#define __ITS_ITU_PSERIES_P2108_P2108_H__ + +#include "Enums.h" +#include "Errors.h" + +#include // For atan, fmin, log10, pow, sqrt, tan + +namespace ITS { +namespace ITU { +namespace PSeries { +namespace P2108 { + +// Define cross-platform EXPORTED +#ifndef DOXYGEN_SHOULD_SKIP + #ifdef _WIN32 + #define EXPORTED extern "C" __declspec(dllexport) + #else + #define EXPORTED extern "C" + #endif +#endif + +// Bring some commonly-used mathematical functions into the global namespace +// This makes long equations a bit more readable while avoiding total namespace +// chaos. +using std::atan; +using std::fmin; +using std::log10; +using std::pow; +using std::sqrt; +using std::tan; + +//////////////////////////////////////////////////////////////////////////////// +// Constants +#define PI 3.14159265358979323846 /**< Approximate value of @f$ \pi @f$ */ + +//////////////////////////////////////////////////////////////////////////////// +// Public Functions +EXPORTED int AeronauticalStatisticalModel( + const double f__ghz, + const double theta__deg, + const double p, + double &L_ces__db +); +EXPORTED int TerrestrialStatisticalModel( + const double f__ghz, const double d__km, const double p, double &L_ctt__db +); +EXPORTED int HeightGainTerminalCorrectionModel( + const double f__ghz, + const double h__meter, + const double w_s__meter, + const double R__meter, + const ClutterType clutter_type, + double &A_h__db +); + +//////////////////////////////////////////////////////////////////////////////// +// Private Functions +double cot(const double x); +double InverseComplementaryCumulativeDistribution(const double q); +double Equation_2a(const double nu); +double Equation_2b( + const double K_h2, const double h__meter, const double R__meter +); +int Section3p1_InputValidation( + const double f__ghz, + const double h__meter, + const double w_s__meter, + const double R__meter +); +int Section3p2_InputValidation( + const double f__ghz, const double d__km, const double p +); +int Section3p3_InputValidation( + const double f__ghz, const double theta__deg, const double p +); +double TerrestrialStatisticalModelHelper( + const double f__ghz, const double d__km, const double p +); + +} // namespace P2108 +} // namespace PSeries +} // namespace ITU +} // namespace ITS + +#endif \ No newline at end of file diff --git a/include/P2108.h b/include/P2108.h deleted file mode 100644 index 0983b57..0000000 --- a/include/P2108.h +++ /dev/null @@ -1,28 +0,0 @@ -#include "math.h" - -#define DLLEXPORT extern "C" __declspec(dllexport) - -#define PI 3.1415926535897932384 - -/////////////////////////////////////////////// -// FUNCTIONS -// - -// Public Functions -DLLEXPORT int AeronauticalStatisticalModel(double f__ghz, double theta__deg, - double p, double* L_ces__db); -DLLEXPORT int TerrestrialStatisticalModel(double f__ghz, double d__km, - double p, double* L_ctt__db); -DLLEXPORT int HeightGainTerminalCorrectionModel(double f__ghz, double h__meter, - double w_s__meter, double R__meter, int clutter_type, double* A_h__db); - -// Private Functions -double cot(double x); -double InverseComplementaryCumulativeDistribution(double q); -double Equation_2a(double nu); -double Equation_2b(double K_h2, double h__meter, double R__meter); -int Section3p1_InputValidation(double f__ghz, double h__meter, double w_s__meter, - double R__meter); -int Section3p2_InputValidation(double f__ghz, double d__km, double p); -int Section3p3_InputValidation(double f__ghz, double theta__deg, double p); -double TerrestrialStatisticalModelHelper(double f__ghz, double d__km, double p); diff --git a/src/AeronauticalStatisticalModel.cpp b/src/AeronauticalStatisticalModel.cpp index bad7d0d..ef66577 100644 --- a/src/AeronauticalStatisticalModel.cpp +++ b/src/AeronauticalStatisticalModel.cpp @@ -1,57 +1,75 @@ -#include "../include/P2108.h" -#include "../include/Errors.h" +/** @file AeronauticalStatisticalModel.cpp + * Implements the model from ITU-R P.2108 Section 3.3. + */ +#include "ITS.ITU.PSeries.P2108/P2108.h" -/*============================================================================= - | - | Description: The Earth-space and aeronautical statistical clutter loss - | model as described in Section 3.3. This model is applicable - | when one end of the path is within man-made clutter and the - | other end is a satellite, aeroplane, or other platform - | above the Earth. - | - | Input: f__ghz - Frequency, in GHz - | theta__deg - Elevation angle, in degrees - | p - Percentage of locations, in % - | - | Output: L_ces__db - Additional loss (clutter loss), in dB - | - | Returns: error - Error code - | - *===========================================================================*/ -int AeronauticalStatisticalModel(double f__ghz, double theta__deg, double p, - double* L_ces__db) -{ +namespace ITS { +namespace ITU { +namespace PSeries { +namespace P2108 { + +/******************************************************************************* + * The Earth-space and aeronautical statistical clutter loss model as described + * in Section 3.3. + * + * This model is applicable when one end of the path is within man-made clutter + * and the other end is a satellite, aeroplane, or other platform above the + * Earth. + * + * Frequency range: @f$ 10 < f < 100 @f$ (GHz)\n + * Elevation angle range: @f$ 0 < \theta < 90 @f$ (degrees)\n + * Percentage locations range: @f$ 0 < p < 100 @f$ (%) + * + * @param[in] f__ghz Frequency, in GHz + * @param[in] theta__deg Elevation angle, in degrees + * @param[in] p Percentage of locations, in % + * @param[out] L_ces__db Additional loss (clutter loss), in dB + * @return Return code + ******************************************************************************/ +int AeronauticalStatisticalModel( + const double f__ghz, + const double theta__deg, + const double p, + double &L_ces__db +) { int rtn = Section3p3_InputValidation(f__ghz, theta__deg, p); if (rtn != SUCCESS) return rtn; - double A_1 = 0.05; - double K_1 = 93 * pow(f__ghz, 0.175); + const double A_1 = 0.05; + const double K_1 = 93 * pow(f__ghz, 0.175); - double part1 = log(1 - p / 100.0); - double part2 = A_1 * (1 - theta__deg / 90.0) + PI * theta__deg / 180.0; - double part3 = 0.5 * (90.0 - theta__deg) / 90.0; - double part4 = 0.6 * InverseComplementaryCumulativeDistribution(p / 100); + const double part1 = log(1 - p / 100.0); + const double part2 + = A_1 * (1 - theta__deg / 90.0) + PI * theta__deg / 180.0; + const double part3 = 0.5 * (90.0 - theta__deg) / 90.0; + const double part4 + = 0.6 * InverseComplementaryCumulativeDistribution(p / 100); - *L_ces__db = pow(-K_1 * part1 * cot(part2), part3) - 1 - part4; + L_ces__db = pow(-K_1 * part1 * cot(part2), part3) - 1 - part4; return SUCCESS; } -/*============================================================================= - | - | Description: Input validation for the Earth-space and aeronautical - | statistical clutter loss model (Section 3.3). - | - | Input: f__ghz - Frequency, in GHz - | theta__deg - Elevation angle, in degrees - | p - Percentage of locations, in % - | - | Returns: error code or SUCCESS - | - *===========================================================================*/ -int Section3p3_InputValidation(double f__ghz, double theta__deg, double p) -{ +/******************************************************************************* + * Inputs-in-range validation for the Earth-space and aeronautical statistical + * clutter loss model (Section 3.3). + * + * Returns `SUCCESS` if all parameters are valid. Otherwise, invalid inputs will + * return unique error codes. + * + * Frequency range: @f$ 10 < f < 100 @f$ (GHz)\n + * Elevation angle range: @f$ 0 < \theta < 90 @f$ (degrees)\n + * Percentage locations range: @f$ 0 < p < 100 @f$ (%) + * + * @param[in] f__ghz Frequency, in GHz + * @param[in] theta__deg Elevation angle, in degrees + * @param[in] p Percentage of locations, in % + * @return Return code + ******************************************************************************/ +int Section3p3_InputValidation( + const double f__ghz, const double theta__deg, const double p +) { if (f__ghz < 10 || f__ghz > 100) return ERROR33__FREQUENCY; @@ -64,16 +82,19 @@ int Section3p3_InputValidation(double f__ghz, double theta__deg, double p) return SUCCESS; } -/*============================================================================= - | - | Description: Helper function for cotangent operation. - | - | Input: x - Argument - | - | Returns: cot(x) - Cotangent of the argument - | - *===========================================================================*/ -double cot(double x) -{ +/******************************************************************************* + * Helper function to calculate @f$ \cot(x) @f$. + * + * The calculation is implemented simply as @f$ 1 / \cot(x) @f$. + * + * @param[in] x Argument, in radians + * @return Cotangent of the argument, @f$ \cot(x) @f$ + ******************************************************************************/ +double cot(const double x) { return 1 / tan(x); -} \ No newline at end of file +} + +} // namespace P2108 +} // namespace PSeries +} // namespace ITU +} // namespace ITS \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..29cc742 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,57 @@ +########################################### +## BUILD THE LIBRARY +########################################### +# Include source AND header files here so IDEs can find them +set(PROJECT_HEADERS "${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}") +add_library( + ${LIB_NAME} SHARED + "AeronauticalStatisticalModel.cpp" + "HeightGainTerminalCorrectionModel.cpp" + "InverseComplementaryCumulativeDistribution.cpp" + "TerrestrialStatisticalModel.cpp" + "${PROJECT_HEADERS}/Enums.h" + "${PROJECT_HEADERS}/Errors.h" + "${PROJECT_HEADERS}/${LIB_NAME}.h" +) + +# Add the include directory +target_include_directories(${LIB_NAME} PUBLIC "${PROJECT_SOURCE_DIR}/include") + +# Set minimum C++ version to C++11 +target_compile_features(${LIB_NAME} PUBLIC cxx_std_11) + +# Platform-specific configurations +if (WIN32) + set_target_properties(${LIB_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS true) +endif () + +# Set some target metadata +set_target_properties( + ${LIB_NAME} PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} # Include version in .so output filenames + LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" + ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" + RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" +) + +# Architecture-dependent configuration +if (BUILD_32BIT) + set_target_properties( + ${LIB_NAME} PROPERTIES + DEBUG_POSTFIX "x86" + RELEASE_POSTFIX "x86" + ) +else () + set_target_properties( + ${LIB_NAME} PROPERTIES + DEBUG_POSTFIX "x64" + RELEASE_POSTFIX "x64" + ) +endif () + +# Enable Hot Reload for MSVC compilers if supported. +if (POLICY CMP0141) + cmake_policy(SET CMP0141 NEW) + set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") +endif() \ No newline at end of file diff --git a/src/HeightGainTerminalCorrectionModel.cpp b/src/HeightGainTerminalCorrectionModel.cpp index 764383f..f2a0561 100644 --- a/src/HeightGainTerminalCorrectionModel.cpp +++ b/src/HeightGainTerminalCorrectionModel.cpp @@ -1,86 +1,100 @@ -#include "../include/P2108.h" -#include "../include/Errors.h" -#include "../include/Consts.h" - -/*============================================================================= - | - | Description: Height gain terminal correction model as described in - | Section 3.1. This method gives the median loss due to - | different terminal surroundings. This model can be - | applied to both transmitting and receiving ends of the path. - | - | Input: f__ghz - Frequency, in GHz - | h__meter - Antenna height, in meters - | w_s__meter - Street width, in meters - | R__meter - Representative clutter height, in meters - | clutter_type - Clutter type - | - | Output: A_h__db - Additional loss (clutter loss), in dB - | - | Returns: error - Error code - | - *===========================================================================*/ -int HeightGainTerminalCorrectionModel(double f__ghz, double h__meter, - double w_s__meter, double R__meter, int clutter_type, double *A_h__db) -{ - int rtn = Section3p1_InputValidation(f__ghz, h__meter, w_s__meter, R__meter); +/** @file HeightGainTerminalCorrectionModel.cpp + * Implements the model from ITU-R P.2108 Section 3.1. + */ +#include "ITS.ITU.PSeries.P2108/P2108.h" + +namespace ITS { +namespace ITU { +namespace PSeries { +namespace P2108 { + +/******************************************************************************* + * Height gain terminal correction model as described in Section 3.1. + * + * This method gives the median loss due to different terminal surroundings. + * This model can be applied to both transmitting and receiving ends of the + * path. + * + * Frequency range: @f$ 0.03 \leq f \leq 3 @f$ (GHz)\n + * Antenna height range: @f$ 0 \leq h @f$ (m)\n + * Street width range: @f$ 0 < w_s @f$ (m)\n + * Representative clutter height range: @f$ 0 < R @f$ (m) + * + * @param[in] f__ghz Frequency, in GHz + * @param[in] h__meter Antenna height, in meters + * @param[in] w_s__meter Street width, in meters + * @param[in] R__meter Representative clutter height, in meters + * @param[in] clutter_type Clutter type + * @param[out] A_h__db Additional loss (clutter loss), in dB + * @return Return code + ******************************************************************************/ +int HeightGainTerminalCorrectionModel( + const double f__ghz, + const double h__meter, + const double w_s__meter, + const double R__meter, + const ClutterType clutter_type, + double &A_h__db +) { + const int rtn + = Section3p1_InputValidation(f__ghz, h__meter, w_s__meter, R__meter); if (rtn != SUCCESS) return rtn; - if (h__meter >= R__meter) - { - *A_h__db = 0; + if (h__meter >= R__meter) { + A_h__db = 0; return SUCCESS; } - double h_dif__meter = R__meter - h__meter; // Equation (2d) - double theta_clut__deg = atan(h_dif__meter / w_s__meter) * 180.0 / PI; // Equation (2e) - double K_h2 = 21.8 + 6.2 * log10(f__ghz); // Equation (2f) - - switch (clutter_type) - { - case CLUTTER_TYPE__WATER_SEA: - case CLUTTER_TYPE__OPEN_RURAL: - *A_h__db = Equation_2b(K_h2, h__meter, R__meter); - break; - - case CLUTTER_TYPE__SUBURBAN: - case CLUTTER_TYPE__URBAN: - case CLUTTER_TYPE__TREES_FOREST: - case CLUTTER_TYPE__DENSE_URBAN: - { - double K_nu = 0.342 * sqrt(f__ghz); // Equation (2g) - double nu = K_nu * sqrt(h_dif__meter * theta_clut__deg); // Equation (2c) - - *A_h__db = Equation_2a(nu); - break; - } - default: - return ERROR31__CLUTTER_TYPE; + const double h_dif__meter = R__meter - h__meter; // Equation (2d) + const double theta_clut__deg + = atan(h_dif__meter / w_s__meter) * 180.0 / PI; // Equation (2e) + const double K_h2 = 21.8 + 6.2 * log10(f__ghz); // Equation (2f) + + switch (clutter_type) { + case ClutterType::WATER_SEA: + case ClutterType::OPEN_RURAL: + A_h__db = Equation_2b(K_h2, h__meter, R__meter); + break; + + case ClutterType::SUBURBAN: + case ClutterType::URBAN: + case ClutterType::TREES_FOREST: + case ClutterType::DENSE_URBAN: + { + const double K_nu = 0.342 * sqrt(f__ghz); // Equation (2g) + const double nu + = K_nu + * sqrt(h_dif__meter * theta_clut__deg); // Equation (2c) + + A_h__db = Equation_2a(nu); + break; + } + default: + return ERROR31__CLUTTER_TYPE; } return SUCCESS; } -/*============================================================================= - | - | Description: Input validation for the height gain terminal correction - | model (Section 3.1). - | Note: Input parameter 'clutter_type' is validated in - | the main function's switch statement through the use - | of default to simplify code structure. - | - | Input: f__ghz - Frequency, in GHz - | h__meter - Antenna height, in meters - | w_s__meter - Street width, in meters - | R__meter - Representative clutter height, in meters - | - | Returns: error code or SUCCESS - | - *===========================================================================*/ -int Section3p1_InputValidation(double f__ghz, double h__meter, double w_s__meter, - double R__meter) -{ +/******************************************************************************* + * Input validation for the height gain terminal correction model (Section 3.1). + * + * Note: Input parameter 'clutter_type' is validated in the main function's + * switch statement through the use of default to simplify code structure. + * + * @param[in] f__ghz Frequency, in GHz + * @param[in] h__meter Antenna height, in meters + * @param[in] w_s__meter Street width, in meters + * @param[in] R__meter Representative clutter height, in meters + * @return Return code + ******************************************************************************/ +int Section3p1_InputValidation( + const double f__ghz, + const double h__meter, + const double w_s__meter, + const double R__meter +) { if (f__ghz < 0.03 || f__ghz > 3) return ERROR31__FREQUENCY; @@ -96,42 +110,41 @@ int Section3p1_InputValidation(double f__ghz, double h__meter, double w_s__meter return SUCCESS; } -/*============================================================================= - | - | Description: Equation (2a) of Section 3.1 - | - | Input: nu - Dimensionless diffraction parameter - | - | Returns: A_h__db - Additional loss (clutter loss), in dB - | - *===========================================================================*/ -double Equation_2a(double nu) -{ +/******************************************************************************* + * Equation (2a) of Section 3.1 + * + * @param[in] nu Dimensionless diffraction parameter + * @return Additional loss (clutter loss), in dB + ******************************************************************************/ +double Equation_2a(const double nu) { double J_nu__db; if (nu <= -0.78) J_nu__db = 0; else J_nu__db = 6.9 + 20 * log10(sqrt(pow(nu - 0.1, 2) + 1) + nu - 0.1); - double A_h__db = J_nu__db - 6.03; + const double A_h__db = J_nu__db - 6.03; return A_h__db; } -/*============================================================================= - | - | Description: Equation (2b) of Section 3.1 - | - | Input: K_h2 - Intermediate parameter - | h__meter - Antenna height, in meters - | R__meter - Representative clutter height, in meters - | - | Returns: A_h__db - Additional loss (clutter loss), in dB - | - *===========================================================================*/ -double Equation_2b(double K_h2, double h__meter, double R__meter) -{ - double A_h__db = -K_h2 * log10(h__meter / R__meter); +/******************************************************************************* + * Equation (2b) of Section 3.1 + * + * @param[in] K_h2 Intermediate parameter + * @param[in] h__meter Antenna height, in meters + * @param[in] R__meter Representative clutter height, in meters + * @return Additional loss (clutter loss), in dB + ******************************************************************************/ +double Equation_2b( + const double K_h2, const double h__meter, const double R__meter +) { + const double A_h__db = -K_h2 * log10(h__meter / R__meter); return A_h__db; -} \ No newline at end of file +} + +} // namespace P2108 +} // namespace PSeries +} // namespace ITU +} // namespace ITS \ No newline at end of file diff --git a/src/InverseComplementaryCumulativeDistribution.cpp b/src/InverseComplementaryCumulativeDistribution.cpp index a192806..67da705 100644 --- a/src/InverseComplementaryCumulativeDistribution.cpp +++ b/src/InverseComplementaryCumulativeDistribution.cpp @@ -1,35 +1,47 @@ -#include "../include/P2108.h" - -/*============================================================================= - | - | Description: This function computes the inverse complementary - | cumulative distribution function approximation as - | described in Recommendation ITU-R P.1057. This - | approximation is sourced from Formula 26.2.23 in - | Abramowitz & Stegun. This approximation has an error - | of abs(epsilon(p)) < 4.5e-4 - | - | Input: q - Percentage, 0.0 < q < 1.0 - | - | Returns: Q_q - Q(q)^-1 - | - *===========================================================================*/ -double InverseComplementaryCumulativeDistribution(double q) -{ - double C_0 = 2.515517; - double C_1 = 0.802853; - double C_2 = 0.010328; - double D_1 = 1.432788; - double D_2 = 0.189269; - double D_3 = 0.001308; +/** @internal @file InverseComplementaryCumulativeDistribution.cpp + * @brief Implements a function to calculate the inverse CCDF. + */ +#include "ITS.ITU.PSeries.P2108/P2108.h" + +#include + +namespace ITS { +namespace ITU { +namespace PSeries { +namespace P2108 { + +/******************************************************************************* + * Compute the inverse complementary cumulative distribution function + * approximation as described in Recommendation ITU-R P.1057. + * + * This approximation is sourced from Formula 26.2.23 in Abramowitz & Stegun. + * This approximation has an error of @f$ |\epsilon(p)| < 4.5\times 10^{-4} @f$ + * + * @param q Percentage, @f$ 0.0 < q < 1.0 @f$ + * @return Q(q)^-1 + * @throw std::out_of_range if the input is outside the range [0.0, 1.0] + ******************************************************************************/ +double InverseComplementaryCumulativeDistribution(const double q) { + if (q <= 0.0 || q >= 1.0) { + throw std::out_of_range("Input q must be between 0.0 and 1.0"); + } + + // Constants from Abramowitz & Stegun 26.2.23 + constexpr double C_0 = 2.515517; + constexpr double C_1 = 0.802853; + constexpr double C_2 = 0.010328; + constexpr double D_1 = 1.432788; + constexpr double D_2 = 0.189269; + constexpr double D_3 = 0.001308; double x = q; if (q > 0.5) x = 1.0 - x; - double T_x = sqrt(-2.0 * log(x)); + const double T_x = sqrt(-2.0 * log(x)); - double zeta_x = ((C_2 * T_x + C_1) * T_x + C_0) / (((D_3 * T_x + D_2) * T_x + D_1) * T_x + 1.0); + const double zeta_x = ((C_2 * T_x + C_1) * T_x + C_0) + / (((D_3 * T_x + D_2) * T_x + D_1) * T_x + 1.0); double Q_q = T_x - zeta_x; @@ -37,4 +49,9 @@ double InverseComplementaryCumulativeDistribution(double q) Q_q = -Q_q; return Q_q; -} \ No newline at end of file +} + +} // namespace P2108 +} // namespace PSeries +} // namespace ITU +} // namespace ITS \ No newline at end of file diff --git a/src/TerrestrialStatisticalModel.cpp b/src/TerrestrialStatisticalModel.cpp index 4b92b41..fa69369 100644 --- a/src/TerrestrialStatisticalModel.cpp +++ b/src/TerrestrialStatisticalModel.cpp @@ -1,86 +1,98 @@ -#include "../include/P2108.h" -#include "../include/Errors.h" - -/*============================================================================= - | - | Description: Statistical clutter loss model for terrestrial paths as - | described in Section 3.2. This model can be applied - | for urban and suburban clutter loss modelling. - | - | Input: f__ghz - Frequency, in GHz - | d__km - Path distance, in km - | p - Percentage of locations, in % - | - | Output: L_ctt__db - Additional loss (clutter loss), in dB - | - | Returns: error - Error code - | - *===========================================================================*/ -int TerrestrialStatisticalModel(double f__ghz, double d__km, double p, double* L_ctt__db) -{ +/** @file TerrestrialStatisticalModel.cpp + * Implements the model from ITU-R P.2108 Section 3.2. + */ +#include "ITS.ITU.PSeries.P2108/P2108.h" + +namespace ITS { +namespace ITU { +namespace PSeries { +namespace P2108 { + +/******************************************************************************* + * Statistical clutter loss model for terrestrial paths as described in + * Section 3.2. + * + * This model can be applied for urban and suburban clutter loss modeling. + * + * Frequency range: @f$ 0.5 \leq f \leq 67 @f$ (GHz)\n + * Path distance range: @f$ 0.25 \leq d @f$ (km) (must be @f$ \geq 1 @f$ to apply + * the correction at both ends of the path)\n + * Percentage locations range: @f$0 < p < 100 @f$ (%) + * + * @param[in] f__ghz Frequency, in GHz + * @param[in] d__km Path distance, in km + * @param[in] p Percentage of locations, in % + * @param[out] L_ctt__db Additional loss (clutter loss), in dB + * @return Return code + ******************************************************************************/ +int TerrestrialStatisticalModel( + const double f__ghz, const double d__km, const double p, double &L_ctt__db +) { int rtn = Section3p2_InputValidation(f__ghz, d__km, p); if (rtn != SUCCESS) return rtn; // compute clutter loss at 2 km - double L_ctt_2km__db = TerrestrialStatisticalModelHelper(f__ghz, 2, p); + const double L_ctt_2km__db + = TerrestrialStatisticalModelHelper(f__ghz, 2, p); // compute clutter loss at requested distance - double L_ctt_d__db = TerrestrialStatisticalModelHelper(f__ghz, d__km, p); + const double L_ctt_d__db + = TerrestrialStatisticalModelHelper(f__ghz, d__km, p); // "clutter loss must not exceed a maximum value given by [Equation 6]" - *L_ctt__db = fmin(L_ctt_2km__db, L_ctt_d__db); + L_ctt__db = fmin(L_ctt_2km__db, L_ctt_d__db); return SUCCESS; } -/*============================================================================= - | - | Description: Compute the clutter loss - | - | Input: f__ghz - Frequency, in GHz - | d__km - Path distance, in km - | p - Percentage of locations, in % - | - | Returns: L_ctt__db - Clutter loss, in dB - | - *===========================================================================*/ -double TerrestrialStatisticalModelHelper(double f__ghz, double d__km, double p) -{ +/******************************************************************************* + * Compute the clutter loss + * + * @param[in] f__ghz Frequency, in GHz + * @param[in] d__km Path distance, in km + * @param[in] p Percentage of locations, in % + * @return Clutter loss, in dB + ******************************************************************************/ +double TerrestrialStatisticalModelHelper( + const double f__ghz, const double d__km, const double p +) { // Equations 4a and 4b - double sigma_l__db = 4; - double L_l__db = -2 * log10(pow(10, -5 * log10(f__ghz) - 12.5) + pow(10, -16.5)); + const double sigma_l__db = 4; + const double L_l__db + = -2 * log10(pow(10, -5 * log10(f__ghz) - 12.5) + pow(10, -16.5)); // Equations 5a and 5b - double sigma_s__db = 6; - double L_s__db = 32.98 + 23.9 * log10(d__km) + 3 * log10(f__ghz); + const double sigma_s__db = 6; + const double L_s__db = 32.98 + 23.9 * log10(d__km) + 3 * log10(f__ghz); // Equation 3b - double numerator = pow(sigma_l__db, 2) * pow(10, -0.2 * L_l__db) + pow(sigma_s__db, 2) * pow(10, -0.2 * L_s__db); - double denominator = pow(10, -0.2 * L_l__db) + pow(10, -0.2 * L_s__db); - double sigma_cb__db = sqrt(numerator / denominator); + const double numerator = pow(sigma_l__db, 2) * pow(10, -0.2 * L_l__db) + + pow(sigma_s__db, 2) * pow(10, -0.2 * L_s__db); + const double denominator + = pow(10, -0.2 * L_l__db) + pow(10, -0.2 * L_s__db); + const double sigma_cb__db = sqrt(numerator / denominator); // Equation 3a - double L_ctt__db = -5 * log10(pow(10, -0.2 * L_l__db) + pow(10, -0.2 * L_s__db)) + const double L_ctt__db + = -5 * log10(pow(10, -0.2 * L_l__db) + pow(10, -0.2 * L_s__db)) - sigma_cb__db * InverseComplementaryCumulativeDistribution(p / 100); return L_ctt__db; } -/*============================================================================= - | - | Description: Input validation for the statistical clutter loss model - | for terrestrial paths (Section 3.2). - | - | Input: f__ghz - Frequency, in GHz - | d__km - Path distance, in km - | p - Percentage of locations, in % - | - | Returns: error code or SUCCESS - | - *===========================================================================*/ -int Section3p2_InputValidation(double f__ghz, double d__km, double p) -{ +/******************************************************************************* + * Input validation for the statistical clutter loss model for terrestrial paths + * (Section 3.2). + * + * @param[in] f__ghz Frequency, in GHz + * @param[in] d__km Path distance, in km + * @param[in] p Percentage of locations, in % + * @return Return code + ******************************************************************************/ +int Section3p2_InputValidation( + const double f__ghz, const double d__km, const double p +) { if (f__ghz < 0.5 || f__ghz > 67) return ERROR32__FREQUENCY; @@ -91,4 +103,9 @@ int Section3p2_InputValidation(double f__ghz, double d__km, double p) return ERROR32__PERCENTAGE; return SUCCESS; -} \ No newline at end of file +} + +} // namespace P2108 +} // namespace PSeries +} // namespace ITU +} // namespace ITS \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..a505c89 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,31 @@ +############################################ +## CONFIGURE UNIT TESTS +############################################ +set(TEST_NAME "Test${LIB_NAME}") + +# Add all unit test files here +add_executable( + ${TEST_NAME} + "TestAeronauticalStatisticalModel.cpp" + "TestHeightGainTerminalCorrectionModel.cpp" + "TestInverseComplementaryCumulativeDistribution.cpp" + "TestTerrestrialStatisticalModel.cpp" + "TestUtils.cpp" + "TestUtils.h" +) + +########################################### +## SET UP AND DISCOVER TESTS +########################################### +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +add_subdirectory("${PROJECT_SOURCE_DIR}/extern/googletest" "extern/googletest") +include(GoogleTest) +include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) +set_target_properties( + ${TEST_NAME} PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" + ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" + RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" +) +target_link_libraries(${TEST_NAME} ${LIB_NAME} GTest::gtest_main) +gtest_discover_tests(${TEST_NAME}) \ No newline at end of file diff --git a/tests/TestAeronauticalStatisticalModel.cpp b/tests/TestAeronauticalStatisticalModel.cpp new file mode 100644 index 0000000..114e135 --- /dev/null +++ b/tests/TestAeronauticalStatisticalModel.cpp @@ -0,0 +1,84 @@ +#include "TestUtils.h" + +// Test fixture for the unit tests +class AeronauticalStatisticalModelTest: public ::testing::Test { + protected: + void SetUp() override { + // Load test data from CSV + testData = readAeronauticalStatisticalModelTestData( + "AeronauticalStatisticalModelTestData.csv" + ); + } + + // Vector to hold test data + std::vector testData; +}; + +// Test case to verify the AeronauticalStatisticalModel function +TEST_F(AeronauticalStatisticalModelTest, TestAeronauticalStatisticalModel) { + // Ensure test data was loaded + EXPECT_NE(static_cast(testData.size()), 0); + double L_ces__db; + int rtn; + for (const auto &data : testData) { + rtn = AeronauticalStatisticalModel( + data.f__ghz, data.theta__deg, data.p, L_ces__db + ); + EXPECT_EQ(rtn, data.rtn); + if (rtn == SUCCESS) { + EXPECT_NEAR(L_ces__db, data.L_ces__db, ABSTOL__DB); + } + } +} + +TEST(Section3p3_InputValidationTest, Section3p3_FrequencyInvalid) { + EXPECT_EQ( + Section3p3_InputValidation(9, 1, 1), ERROR33__FREQUENCY + ); // Frequency too low + EXPECT_EQ( + Section3p3_InputValidation(101, 1, 1), ERROR33__FREQUENCY + ); // Frequency too high +} + +TEST(Section3p3_InputValidationTest, Section3p3_ThetaInvalid) { + EXPECT_EQ( + Section3p3_InputValidation(20, -1, 1), ERROR33__THETA + ); // Theta negative + EXPECT_EQ( + Section3p3_InputValidation(20, 91, 1), ERROR33__THETA + ); // Theta too large +} + +TEST(Section3p3_InputValidationTest, Section3p3_PercentageInvalid) { + EXPECT_EQ( + Section3p3_InputValidation(20, 1, -1), ERROR33__PERCENTAGE + ); // p < 0 + EXPECT_EQ( + Section3p3_InputValidation(20, 1, 0), ERROR33__PERCENTAGE + ); // p = 0 + EXPECT_EQ( + Section3p3_InputValidation(20, 1, 100), ERROR33__PERCENTAGE + ); // p = 100 + EXPECT_EQ( + Section3p3_InputValidation(20, 1, 101), ERROR33__PERCENTAGE + ); // p > 100 +} + +TEST(Section3p3_InputValidationTest, Section3p3_ValidInputs) { + // Test all edge values plus in-between values + EXPECT_EQ(Section3p3_InputValidation(10, 45, 50), SUCCESS); + EXPECT_EQ(Section3p3_InputValidation(50, 45, 50), SUCCESS); + EXPECT_EQ(Section3p3_InputValidation(100, 45, 50), SUCCESS); + EXPECT_EQ(Section3p3_InputValidation(50, 0, 50), SUCCESS); + EXPECT_EQ(Section3p3_InputValidation(50, 45, 50), SUCCESS); + EXPECT_EQ(Section3p3_InputValidation(50, 90, 50), SUCCESS); + EXPECT_EQ(Section3p3_InputValidation(50, 45, 0.1), SUCCESS); + EXPECT_EQ(Section3p3_InputValidation(50, 45, 50), SUCCESS); + EXPECT_EQ(Section3p3_InputValidation(50, 45, 99.9), SUCCESS); +} + +TEST(Section3p3_Cot, Section3p3_TestCot) { + EXPECT_DOUBLE_EQ(cot(0), std::numeric_limits::infinity()); + EXPECT_NEAR(cot(PI / 2), 0.0, 1e-15); + EXPECT_NEAR(cot(PI / 4), 1.0, 1e-15); +} diff --git a/tests/TestHeightGainTerminalCorrectionModel.cpp b/tests/TestHeightGainTerminalCorrectionModel.cpp new file mode 100644 index 0000000..dc06b0a --- /dev/null +++ b/tests/TestHeightGainTerminalCorrectionModel.cpp @@ -0,0 +1,121 @@ +#include "TestUtils.h" + +#include // For std::numeric_limits::max() + +// Test fixture for the unit tests +class HeightGainTerminalCorrectionModelTest: public ::testing::Test { + protected: + void SetUp() override { + // Load test data from CSV + testData = readHeightGainTerminalCorrectionModelTestData( + "HeightGainTerminalCorrectionModelTestData.csv" + ); + } + + // Vector to hold test data + std::vector testData; +}; + +// Test case to verify the HeightGainTerminalCorrectionModel function +TEST_F( + HeightGainTerminalCorrectionModelTest, TestHeightGainTerminalCorrectionModel +) { + // Ensure test data was loaded + EXPECT_NE(static_cast(testData.size()), 0); + double A_h__db; + int rtn; + for (const auto &data : testData) { + rtn = HeightGainTerminalCorrectionModel( + data.f__ghz, + data.h__meter, + data.w_s__meter, + data.R__meter, + data.clutter_type, + A_h__db + ); + EXPECT_EQ(rtn, data.rtn); + if (rtn == SUCCESS) { + EXPECT_NEAR(A_h__db, data.A_h__db, ABSTOL__DB); + } + } +} + +TEST(Section3p1_InputValidationTest, Section3p1_FrequencyInvalid) { + EXPECT_EQ( + Section3p1_InputValidation(0.02, 1, 1, 1), ERROR31__FREQUENCY + ); // Frequency too low + EXPECT_EQ( + Section3p1_InputValidation(3.01, 1, 1, 1), ERROR31__FREQUENCY + ); // Frequency too high +} + +TEST(Section3p1_InputValidationTest, Section3p1_AntennaHeightInvalid) { + EXPECT_EQ( + Section3p1_InputValidation(1, -1, 1, 1), ERROR31__ANTENNA_HEIGHT + ); // Antenna height negative + EXPECT_EQ( + Section3p1_InputValidation(1, 0, 1, 1), ERROR31__ANTENNA_HEIGHT + ); // Antenna height == 0 +} + +TEST(Section3p1_InputValidationTest, Section3p1_StreetWidthInvalid) { + EXPECT_EQ( + Section3p1_InputValidation(1, 1, -1, 1), ERROR31__STREET_WIDTH + ); // Street width negative + EXPECT_EQ( + Section3p1_InputValidation(1, 1, 0, 1), ERROR31__STREET_WIDTH + ); // Street width == 0 +} + +TEST(Section3p1_InputValidationTest, Section3p1_ClutterHeightInvalid) { + EXPECT_EQ( + Section3p1_InputValidation(1, 1, 1, -1), ERROR31__CLUTTER_HEIGHT + ); // Clutter height negative + EXPECT_EQ( + Section3p1_InputValidation(1, 1, 1, 0), ERROR31__CLUTTER_HEIGHT + ); // Clutter height == 0 +} + +TEST(Section3p1_InputValidationTest, Section3p1_ValidInputs) { + // Test edge values plus in-between value for frequency + EXPECT_EQ(Section3p1_InputValidation(0.03, 1, 1, 1), SUCCESS); + EXPECT_EQ(Section3p1_InputValidation(1, 1, 1, 1), SUCCESS); + EXPECT_EQ(Section3p1_InputValidation(3, 1, 1, 1), SUCCESS); + // Test large positive values for other parameters + EXPECT_EQ( + Section3p1_InputValidation(1, std::numeric_limits::max(), 1, 1), + SUCCESS + ); + EXPECT_EQ( + Section3p1_InputValidation(1, 1, std::numeric_limits::max(), 1), + SUCCESS + ); + EXPECT_EQ( + Section3p1_InputValidation(1, 1, 1, std::numeric_limits::max()), + SUCCESS + ); +} + +TEST(Section3p1_Equation_2aTest, Section3p1_NuLessThanLimit) { + // Eq. 2a should return -6.03 for nu <= -0.78. + // In practice, the parameter nu is never negative. + EXPECT_EQ(Equation_2a(-0.78), -6.03); + EXPECT_EQ(Equation_2a(-1), -6.03); +} + +TEST(Section3p1_Equation_2aTest, Section3p1_NuAboveLimit) { + // Test a few values with separately-computed expected results for this + // equation + EXPECT_NEAR(Equation_2a(-0.77), -5.96059383728336638563, 1e-13); + EXPECT_NEAR(Equation_2a(0), 0.00285220856360571492, 1e-13); + EXPECT_NEAR(Equation_2a(1.5), 10.75438646420360661479, 1e-13); +} + +TEST(Section3p1_Equation_2bTest, TestSection3p1_Equation_2b) { + // Test a few values with separately-computed expected results for this + // equation + EXPECT_DOUBLE_EQ(Equation_2b(1, 10, 5), -log10(2)); + EXPECT_DOUBLE_EQ(Equation_2b(5, 1, 2), -5 * log10(0.5)); + EXPECT_DOUBLE_EQ(Equation_2b(0, 100, 100), 0); + EXPECT_DOUBLE_EQ(Equation_2b(-1, 10, 1), 1); +} diff --git a/tests/TestInverseComplementaryCumulativeDistribution.cpp b/tests/TestInverseComplementaryCumulativeDistribution.cpp new file mode 100644 index 0000000..4418f2b --- /dev/null +++ b/tests/TestInverseComplementaryCumulativeDistribution.cpp @@ -0,0 +1,33 @@ +#include "TestUtils.h" + +#include + +TEST(InverseCCDFTest, TestInverseCCDF) { + EXPECT_DOUBLE_EQ( + InverseComplementaryCumulativeDistribution(0.01), 2.3267853325589658 + ); + EXPECT_DOUBLE_EQ( + InverseComplementaryCumulativeDistribution(0.49), 0.024998347218995187 + ); + EXPECT_DOUBLE_EQ( + InverseComplementaryCumulativeDistribution(0.51), -0.024998347218995187 + ); + EXPECT_DOUBLE_EQ( + InverseComplementaryCumulativeDistribution(0.99), -2.3267853325589658 + ); +} + +TEST(InverseCCDFTest, TestInverseCCDFInputInvalid) { + EXPECT_THROW( + InverseComplementaryCumulativeDistribution(-1.0), std::out_of_range + ); + EXPECT_THROW( + InverseComplementaryCumulativeDistribution(0.0), std::out_of_range + ); + EXPECT_THROW( + InverseComplementaryCumulativeDistribution(1.0), std::out_of_range + ); + EXPECT_THROW( + InverseComplementaryCumulativeDistribution(1.1), std::out_of_range + ); +} \ No newline at end of file diff --git a/tests/TestTerrestrialStatisticalModel.cpp b/tests/TestTerrestrialStatisticalModel.cpp new file mode 100644 index 0000000..fc5ca79 --- /dev/null +++ b/tests/TestTerrestrialStatisticalModel.cpp @@ -0,0 +1,80 @@ +#include "TestUtils.h" + +// Test fixture for the unit tests +class TerrestrialStatisticalModelTest: public ::testing::Test { + protected: + void SetUp() override { + // Load test data from CSV + testData = readTerrestrialStatisticalModelTestData( + "TerrestrialStatisticalModelTestData.csv" + ); + } + + // Vector to hold test data + std::vector testData; +}; + +// Test case to verify the TerrestrialStatisticalModel function +TEST_F(TerrestrialStatisticalModelTest, TestTerrestrialStatisticalModel) { + // Ensure test data was loaded + EXPECT_NE(static_cast(testData.size()), 0); + double L_ctt__db; + int rtn; + for (const auto &data : testData) { + rtn = TerrestrialStatisticalModel( + data.f__ghz, data.d__km, data.p, L_ctt__db + ); + EXPECT_EQ(rtn, data.rtn); + if (rtn == SUCCESS) { + EXPECT_NEAR(L_ctt__db, data.L_ctt__db, ABSTOL__DB); + } + } +} + +TEST(Section3p2_InputValidationTest, Section3p2_FrequencyInvalid) { + EXPECT_EQ( + Section3p2_InputValidation(0.49, 1, 1), ERROR32__FREQUENCY + ); // Frequency too low + EXPECT_EQ( + Section3p2_InputValidation(67.01, 1, 1), ERROR32__FREQUENCY + ); // Frequency too high +} + +TEST(Section3p2_InputValidationTest, Section3p2_DistanceInvalid) { + EXPECT_EQ( + Section3p2_InputValidation(20, 0.249, 1), ERROR32__DISTANCE + ); // Distance too small + EXPECT_EQ( + Section3p2_InputValidation(20, 0, 1), ERROR32__DISTANCE + ); // Distance is zero + EXPECT_EQ( + Section3p2_InputValidation(20, -1, 1), ERROR32__DISTANCE + ); // Distance is negative +} + +TEST(Section3p2_InputValidationTest, Section3p2_PercentageInvalid) { + EXPECT_EQ( + Section3p2_InputValidation(20, 1, -1), ERROR32__PERCENTAGE + ); // p < 0 + EXPECT_EQ( + Section3p2_InputValidation(20, 1, 0), ERROR32__PERCENTAGE + ); // p = 0 + EXPECT_EQ( + Section3p2_InputValidation(20, 1, 100), ERROR32__PERCENTAGE + ); // p = 100 + EXPECT_EQ( + Section3p2_InputValidation(20, 1, 101), ERROR32__PERCENTAGE + ); // p > 100 +} + +TEST(Section3p2_InputValidationTest, Section3p2_ValidInputs) { + // Test all edge values plus in-between values + EXPECT_EQ(Section3p2_InputValidation(0.5, 1, 1), SUCCESS); // f = 0.5 + EXPECT_EQ(Section3p2_InputValidation(30, 1, 1), SUCCESS); // f = 30 + EXPECT_EQ(Section3p2_InputValidation(67, 1, 1), SUCCESS); // f = 67 + EXPECT_EQ(Section3p2_InputValidation(30, 0.25, 1), SUCCESS); // d = 0.25 + EXPECT_EQ(Section3p2_InputValidation(30, 100, 1), SUCCESS); // d = 100 + EXPECT_EQ(Section3p2_InputValidation(30, 1, 0.01), SUCCESS); // p = 0.01 + EXPECT_EQ(Section3p2_InputValidation(30, 1, 50), SUCCESS); // p = 50 + EXPECT_EQ(Section3p2_InputValidation(30, 1, 99.99), SUCCESS); // p = 99.99 +} \ No newline at end of file diff --git a/tests/TestUtils.cpp b/tests/TestUtils.cpp new file mode 100644 index 0000000..f235e4c --- /dev/null +++ b/tests/TestUtils.cpp @@ -0,0 +1,82 @@ +#include "TestUtils.h" + +#include +#include + +void appendDirectorySep(std::string &str) { +#ifdef _WIN32 + str += "\\"; +#else + str += "/"; +#endif +} + +std::string getDataDirectory() { + std::string dataDir(__FILE__); + dataDir.resize(dataDir.find_last_of("/\\")); + appendDirectorySep(dataDir); + dataDir += "data"; + appendDirectorySep(dataDir); + return dataDir; +} + +std::vector + readAeronauticalStatisticalModelTestData(const std::string &filename) { + std::vector testData; + std::string dataDir = getDataDirectory(); + std::ifstream file(dataDir + filename); + std::string line; + // struct to store data from a single line of CSV: + AeronauticalStatisticalModelTestData d; + char c; // single-character representing the comma (delimiter) + while (std::getline(file, line)) { + std::istringstream iss(line); + if (iss >> d.f__ghz >> c >> d.theta__deg >> c >> d.p >> c >> d.rtn >> c + >> d.L_ces__db) { + testData.push_back(d); + } + } + return testData; +} + +std::vector + readHeightGainTerminalCorrectionModelTestData(const std::string &filename) { + std::vector testData; + std::string dataDir = getDataDirectory(); + std::ifstream file(dataDir + filename); + std::string line; + // struct to store data from a single line of CSV: + HeightGainTerminalCorrectionModelTestData d; + char c; // single-character representing the comma (delimiter) + int clutter_type_value; + while (std::getline(file, line)) { + std::istringstream iss(line); + if (iss >> d.f__ghz >> c >> d.h__meter >> c >> d.w_s__meter >> c + >> d.R__meter >> c >> clutter_type_value >> c >> d.rtn >> c + >> d.A_h__db) { + // Convert integer to ClutterType enum + d.clutter_type = static_cast(clutter_type_value); + testData.push_back(d); + } + } + return testData; +} + +std::vector + readTerrestrialStatisticalModelTestData(const std::string &filename) { + std::vector testData; + std::string dataDir = getDataDirectory(); + std::ifstream file(dataDir + filename); + std::string line; + // struct to store data from a single line of CSV: + TerrestrialStatisticalModelTestData d; + char c; // single-character representing the comma (delimiter) + while (std::getline(file, line)) { + std::istringstream iss(line); + if (iss >> d.f__ghz >> c >> d.d__km >> c >> d.p >> c >> d.rtn >> c + >> d.L_ctt__db) { + testData.push_back(d); + } + } + return testData; +} diff --git a/tests/TestUtils.h b/tests/TestUtils.h new file mode 100644 index 0000000..8e5218b --- /dev/null +++ b/tests/TestUtils.h @@ -0,0 +1,50 @@ +#ifndef __ITS_ITU_PSERIES_P2108_TEST_UTILS_H__ +#define __ITS_ITU_PSERIES_P2108_TEST_UTILS_H__ + +#include "ITS.ITU.PSeries.P2108/P2108.h" + +#include +#include +#include + +using namespace ITS::ITU::PSeries::P2108; + +// Absolute tolerance for checking model outputs against test data +#define ABSTOL__DB 0.1 + +struct AeronauticalStatisticalModelTestData { + double f__ghz; + double theta__deg; + double p; + int rtn; + double L_ces__db; +}; + +struct HeightGainTerminalCorrectionModelTestData { + double f__ghz; + double h__meter; + double w_s__meter; + double R__meter; + ClutterType clutter_type; + int rtn; + double A_h__db; +}; + +struct TerrestrialStatisticalModelTestData { + double f__ghz; + double d__km; + double p; + int rtn; + double L_ctt__db; +}; + +std::vector + readAeronauticalStatisticalModelTestData(const std::string &filename); + +std::vector + readHeightGainTerminalCorrectionModelTestData(const std::string &filename); + +std::vector + readTerrestrialStatisticalModelTestData(const std::string &filename); + +#endif diff --git a/AeronauticalStatisticalModelTestData.csv b/tests/data/AeronauticalStatisticalModelTestData.csv similarity index 100% rename from AeronauticalStatisticalModelTestData.csv rename to tests/data/AeronauticalStatisticalModelTestData.csv diff --git a/HeightGainTerminalCorrectionModelTestData.csv b/tests/data/HeightGainTerminalCorrectionModelTestData.csv similarity index 100% rename from HeightGainTerminalCorrectionModelTestData.csv rename to tests/data/HeightGainTerminalCorrectionModelTestData.csv diff --git a/TerrestrialStatisticalModelTestData.csv b/tests/data/TerrestrialStatisticalModelTestData.csv similarity index 100% rename from TerrestrialStatisticalModelTestData.csv rename to tests/data/TerrestrialStatisticalModelTestData.csv diff --git a/win32/p2108.def b/win32/p2108.def deleted file mode 100644 index 233126e..0000000 --- a/win32/p2108.def +++ /dev/null @@ -1,5 +0,0 @@ -LIBRARY -EXPORTS - AeronauticalStatisticalModel - TerrestrialStatisticalModel - HeightGainTerminalCorrectionModel \ No newline at end of file diff --git a/win32/p2108.rc b/win32/p2108.rc deleted file mode 100644 index 8614fde..0000000 --- a/win32/p2108.rc +++ /dev/null @@ -1,99 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,0,0,0 - PRODUCTVERSION 1,0,0,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "CompanyName", "The Institute for Telecommunication Sciences" - VALUE "FileDescription", "Recommendation ITU-R P.2108-1" - VALUE "FileVersion", "1.0.0.0" - VALUE "InternalName", "p2108.dll" - VALUE "OriginalFilename", "p2108.dll" - VALUE "ProductName", "Recommendation ITU-R P.2108-1" - VALUE "ProductVersion", "1.0.0.0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - diff --git a/win32/p2108.sln b/win32/p2108.sln deleted file mode 100644 index a7341f7..0000000 --- a/win32/p2108.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31424.327 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "p2108", "p2108.vcxproj", "{BC9DF51C-EB65-45B2-8A30-3F38576EAA23}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {BC9DF51C-EB65-45B2-8A30-3F38576EAA23}.Debug|x64.ActiveCfg = Debug|x64 - {BC9DF51C-EB65-45B2-8A30-3F38576EAA23}.Debug|x64.Build.0 = Debug|x64 - {BC9DF51C-EB65-45B2-8A30-3F38576EAA23}.Debug|x86.ActiveCfg = Debug|Win32 - {BC9DF51C-EB65-45B2-8A30-3F38576EAA23}.Debug|x86.Build.0 = Debug|Win32 - {BC9DF51C-EB65-45B2-8A30-3F38576EAA23}.Release|x64.ActiveCfg = Release|x64 - {BC9DF51C-EB65-45B2-8A30-3F38576EAA23}.Release|x64.Build.0 = Release|x64 - {BC9DF51C-EB65-45B2-8A30-3F38576EAA23}.Release|x86.ActiveCfg = Release|Win32 - {BC9DF51C-EB65-45B2-8A30-3F38576EAA23}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {D02843E9-EDB9-4B5E-9D81-6DE87670C67C} - EndGlobalSection -EndGlobal diff --git a/win32/p2108.vcxproj b/win32/p2108.vcxproj deleted file mode 100644 index 8ff091c..0000000 --- a/win32/p2108.vcxproj +++ /dev/null @@ -1,194 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - - - - - - - - - - - - - - - - - - - 16.0 - Win32Proj - {bc9df51c-eb65-45b2-8a30-3f38576eaa23} - p2108 - 10.0 - - - - DynamicLibrary - true - v143 - Unicode - - - DynamicLibrary - false - v143 - true - Unicode - - - DynamicLibrary - true - v143 - Unicode - - - DynamicLibrary - false - v143 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - $(SolutionDir)..\bin\x86\$(Configuration)\ - - - false - $(SolutionDir)..\bin\x86\$(Configuration)\ - - - true - $(SolutionDir)..\bin\x64\$(Configuration)\ - - - false - $(SolutionDir)..\bin\x64\$(Configuration)\ - - - - Level3 - true - WIN32;_DEBUG;P2108_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - NotUsing - - - MultiThreadedDebug - StdCall - - - Windows - true - false - p2108.def - - - - - Level3 - true - true - true - WIN32;NDEBUG;P2108_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - NotUsing - - - MultiThreaded - StdCall - - - Windows - true - true - true - false - p2108.def - - - - - Level3 - true - _DEBUG;P2108_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - NotUsing - - - MultiThreadedDebug - StdCall - - - Windows - true - false - p2108.def - - - - - Level3 - true - true - true - NDEBUG;P2108_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - NotUsing - - - MultiThreaded - StdCall - - - Windows - true - true - true - false - p2108.def - - - - - - \ No newline at end of file diff --git a/win32/p2108.vcxproj.filters b/win32/p2108.vcxproj.filters deleted file mode 100644 index 9049f0d..0000000 --- a/win32/p2108.vcxproj.filters +++ /dev/null @@ -1,55 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Resource Files - - - - - Resource Files - - - \ No newline at end of file diff --git a/win32/resource.h b/win32/resource.h deleted file mode 100644 index dbda343..0000000 --- a/win32/resource.h +++ /dev/null @@ -1,14 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by p2108.rc - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 101 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/wrap/CMakeLists.txt b/wrap/CMakeLists.txt new file mode 100644 index 0000000..297d528 --- /dev/null +++ b/wrap/CMakeLists.txt @@ -0,0 +1,28 @@ +########################################### +## COPY COMPILED LIBRARY TO WRAPPERS +########################################### +# Wrapper directories are configured as variables +# in the top-level CMakeLists.txt file. This file +# checks if `CMakeLists.txt` exists in each wrapper +# directory, and if so, adds that subdirectory. + +# C#/.NET +if (EXISTS "${DOTNET_WRAPPER_DIR}/CMakeLists.txt") + add_subdirectory(${DOTNET_WRAPPER_DIR}) +else () + message(STATUS "Skipping copying compiled library to C#/.NET wrapper: submodule not initialized.") +endif () + +# MATLAB +if (EXISTS "${MATLAB_WRAPPER_DIR}/CMakeLists.txt") + add_subdirectory(${MATLAB_WRAPPER_DIR}) +else () + message(STATUS "Skipping copying compiled library to MATLAB wrapper: submodule not initialized") +endif () + +# Python +if (EXISTS "${PYTHON_WRAPPER_DIR}/CMakeLists.txt") + add_subdirectory(${PYTHON_WRAPPER_DIR}) +else () + message(STATUS "Skipping copying compiled library to Python wrapper: submodule not initialized") +endif () \ No newline at end of file