From 184e364d6d593e448f5b263c9dbe3af3f4384303 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 9 Jul 2024 16:05:44 -0400 Subject: [PATCH 001/379] Update .gitignore --- .gitignore | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) 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 From 084ce4ec5f432b526646ee2ccfb06e108982b9b3 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 9 Jul 2024 16:05:52 -0400 Subject: [PATCH 002/379] Update .zenodo.json --- .zenodo.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 From 1d2731990be111f40829f2331fdd9221da4ebbd1 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 9 Jul 2024 16:07:09 -0400 Subject: [PATCH 003/379] Refactor C++ project --- CMakeLists.txt | 69 +++++++ CMakePresets.json | 193 +++++++++++++++++ include/Consts.h | 21 -- include/Errors.h | 32 --- include/ITS.ITU.PSeries.P2108/Enums.h | 33 +++ include/ITS.ITU.PSeries.P2108/Errors.h | 31 +++ include/ITS.ITU.PSeries.P2108/P2108.h | 53 +++++ include/P2108.h | 28 --- src/AeronauticalStatisticalModel.cpp | 86 ++++---- src/CMakeLists.txt | 51 +++++ src/HeightGainTerminalCorrectionModel.cpp | 118 +++++------ ...rseComplementaryCumulativeDistribution.cpp | 38 ++-- src/TerrestrialStatisticalModel.cpp | 73 +++---- tests/CMakeLists.txt | 25 +++ tests/TestAeronauticalStatisticalModel.cpp | 64 ++++++ .../TestHeightGainTerminalCorrectionModel.cpp | 82 ++++++++ ...rseComplementaryCumulativeDistribution.cpp | 17 ++ tests/TestTerrestrialStatisticalModel.cpp | 58 ++++++ tests/TestUtils.cpp | 71 +++++++ tests/TestUtils.h | 45 ++++ .../AeronauticalStatisticalModelTestData.csv | 0 ...ghtGainTerminalCorrectionModelTestData.csv | 0 .../TerrestrialStatisticalModelTestData.csv | 0 win32/p2108.def | 5 - win32/p2108.rc | 99 --------- win32/p2108.sln | 31 --- win32/p2108.vcxproj | 194 ------------------ win32/p2108.vcxproj.filters | 55 ----- win32/resource.h | 14 -- 29 files changed, 946 insertions(+), 640 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 CMakePresets.json delete mode 100644 include/Consts.h delete mode 100644 include/Errors.h create mode 100644 include/ITS.ITU.PSeries.P2108/Enums.h create mode 100644 include/ITS.ITU.PSeries.P2108/Errors.h create mode 100644 include/ITS.ITU.PSeries.P2108/P2108.h delete mode 100644 include/P2108.h create mode 100644 src/CMakeLists.txt create mode 100644 tests/CMakeLists.txt create mode 100644 tests/TestAeronauticalStatisticalModel.cpp create mode 100644 tests/TestHeightGainTerminalCorrectionModel.cpp create mode 100644 tests/TestInverseComplementaryCumulativeDistribution.cpp create mode 100644 tests/TestTerrestrialStatisticalModel.cpp create mode 100644 tests/TestUtils.cpp create mode 100644 tests/TestUtils.h rename AeronauticalStatisticalModelTestData.csv => tests/data/AeronauticalStatisticalModelTestData.csv (100%) rename HeightGainTerminalCorrectionModelTestData.csv => tests/data/HeightGainTerminalCorrectionModelTestData.csv (100%) rename TerrestrialStatisticalModelTestData.csv => tests/data/TerrestrialStatisticalModelTestData.csv (100%) delete mode 100644 win32/p2108.def delete mode 100644 win32/p2108.rc delete mode 100644 win32/p2108.sln delete mode 100644 win32/p2108.vcxproj delete mode 100644 win32/p2108.vcxproj.filters delete mode 100644 win32/resource.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..08bb1e2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,69 @@ +# >=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 AND CONFIGURATION +########################################### +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" +) + +# Configure wrapper locations +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") + +# 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) + +# If not specified, fall back to Debug build type +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug") +endif () + +########################################## +## 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` 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..e3f44b9 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,193 @@ +{ + "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}/bin/${presetName}", + "cacheVariables": { + "CMAKE_ARCHIVE_OUTPUT_DIRECTORY": "${sourceDir}/bin", + "CMAKE_LIBRARY_OUTPUT_DIRECTORY": "${sourceDir}/bin", + "CMAKE_RUNTIME_OUTPUT_DIRECTORY": "${sourceDir}/bin", + "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", + "description": "Base 'Release' build preset for ITS PropLib libraries", + "cleanFirst": true + }, + { + "name": "proplib-build-debug-base", + "hidden": true, + "inherits": "proplib-build-base", + "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/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..79dd610 --- /dev/null +++ b/include/ITS.ITU.PSeries.P2108/Enums.h @@ -0,0 +1,33 @@ +/** @file Enums.h + * Enumerated types used by this software. +*/ +#ifndef __ITS_ITU_PSERIES_P2108_ENUMS__ +#define __ITS_ITU_PSERIES_P2108_ENUMS__ + +/** 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 */ +}; + +#endif \ No newline at end of file diff --git a/include/ITS.ITU.PSeries.P2108/Errors.h b/include/ITS.ITU.PSeries.P2108/Errors.h new file mode 100644 index 0000000..2460e99 --- /dev/null +++ b/include/ITS.ITU.PSeries.P2108/Errors.h @@ -0,0 +1,31 @@ +/** @file Errors.h + * Contains return codes used by this software. +*/ +#ifndef __ITS_ITU_PSERIES_P2108_ERRORS__ +#define __ITS_ITU_PSERIES_P2108_ERRORS__ + +//////////////////////////////////////////////////////////////////////////////// +// General Return Codes +#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 */ + +#endif \ No newline at end of file diff --git a/include/ITS.ITU.PSeries.P2108/P2108.h b/include/ITS.ITU.PSeries.P2108/P2108.h new file mode 100644 index 0000000..833f685 --- /dev/null +++ b/include/ITS.ITU.PSeries.P2108/P2108.h @@ -0,0 +1,53 @@ +/** @file P2108.h + * Interface header for this library +*/ +#ifndef __ITS_ITU_PSERIES_P2108_P2108_H__ +#define __ITS_ITU_PSERIES_P2108_P2108_H__ + +#include // For atan, fmin, log10, pow, sqrt, tan +#include "Enums.h" +#include "Errors.h" + +// 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(double f__ghz, double theta__deg, + double p, double* L_ces__db); +EXPORTED int TerrestrialStatisticalModel(double f__ghz, double d__km, + double p, double* L_ctt__db); +EXPORTED int HeightGainTerminalCorrectionModel(double f__ghz, double h__meter, + double w_s__meter, double R__meter, ClutterType 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); +#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..203db08 100644 --- a/src/AeronauticalStatisticalModel.cpp +++ b/src/AeronauticalStatisticalModel.cpp @@ -1,23 +1,26 @@ -#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 - | - *===========================================================================*/ +/******************************************************************************* + * 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(double f__ghz, double theta__deg, double p, double* L_ces__db) { @@ -38,18 +41,22 @@ int AeronauticalStatisticalModel(double f__ghz, double theta__deg, double p, 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 - | - *===========================================================================*/ +/******************************************************************************* + * 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(double f__ghz, double theta__deg, double p) { if (f__ghz < 10 || f__ghz > 100) @@ -64,15 +71,14 @@ 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 - | - *===========================================================================*/ +/******************************************************************************* + * 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(double x) { return 1 / tan(x); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..b20d4a3 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,51 @@ +########################################### +## 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 +) + +# 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 () + +# Configuration-dependent compile options +# TODO \ No newline at end of file diff --git a/src/HeightGainTerminalCorrectionModel.cpp b/src/HeightGainTerminalCorrectionModel.cpp index 764383f..459a1ac 100644 --- a/src/HeightGainTerminalCorrectionModel.cpp +++ b/src/HeightGainTerminalCorrectionModel.cpp @@ -1,27 +1,25 @@ -#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 - | - *===========================================================================*/ +/** @file HeightGainTerminalCorrectionModel.cpp + * Implements the model from ITU-R P.2108 Section 3.1. +*/ +#include "ITS.ITU.PSeries.P2108/P2108.h" + +/******************************************************************************* + * 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. + * + * @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(double f__ghz, double h__meter, - double w_s__meter, double R__meter, int clutter_type, double *A_h__db) + double w_s__meter, double R__meter, ClutterType clutter_type, double *A_h__db) { int rtn = Section3p1_InputValidation(f__ghz, h__meter, w_s__meter, R__meter); if (rtn != SUCCESS) @@ -39,15 +37,15 @@ int HeightGainTerminalCorrectionModel(double f__ghz, double h__meter, switch (clutter_type) { - case CLUTTER_TYPE__WATER_SEA: - case CLUTTER_TYPE__OPEN_RURAL: + case ClutterType::WATER_SEA: + case ClutterType::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: + case ClutterType::SUBURBAN: + case ClutterType::URBAN: + case ClutterType::TREES_FOREST: + case ClutterType::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) @@ -62,22 +60,18 @@ int HeightGainTerminalCorrectionModel(double f__ghz, double h__meter, 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 - | - *===========================================================================*/ +/******************************************************************************* + * 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(double f__ghz, double h__meter, double w_s__meter, double R__meter) { @@ -96,15 +90,12 @@ 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 - | - *===========================================================================*/ +/******************************************************************************* + * Equation (2a) of Section 3.1 + * + * @param[in] nu Dimensionless diffraction parameter + * @return Additional loss (clutter loss), in dB + ******************************************************************************/ double Equation_2a(double nu) { double J_nu__db; @@ -118,17 +109,14 @@ double Equation_2a(double nu) 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 - | - *===========================================================================*/ +/******************************************************************************* + * 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(double K_h2, double h__meter, double R__meter) { double A_h__db = -K_h2 * log10(h__meter / R__meter); diff --git a/src/InverseComplementaryCumulativeDistribution.cpp b/src/InverseComplementaryCumulativeDistribution.cpp index a192806..d8b529e 100644 --- a/src/InverseComplementaryCumulativeDistribution.cpp +++ b/src/InverseComplementaryCumulativeDistribution.cpp @@ -1,21 +1,27 @@ -#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 - | - *===========================================================================*/ +/** @internal @file InverseComplementaryCumulativeDistribution.cpp + * @brief Implements a function to calculate the inverse CCDF. +*/ +#include +#include "ITS.ITU.PSeries.P2108/P2108.h" + +/******************************************************************************* + * 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(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 double C_0 = 2.515517; double C_1 = 0.802853; double C_2 = 0.010328; diff --git a/src/TerrestrialStatisticalModel.cpp b/src/TerrestrialStatisticalModel.cpp index 4b92b41..cfd9ba2 100644 --- a/src/TerrestrialStatisticalModel.cpp +++ b/src/TerrestrialStatisticalModel.cpp @@ -1,21 +1,20 @@ -#include "../include/P2108.h" -#include "../include/Errors.h" +/** @file TerrestrialStatisticalModel.cpp + * Implements the model from ITU-R P.2108 Section 3.2. +*/ +#include "ITS.ITU.PSeries.P2108/P2108.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 - | - *===========================================================================*/ +/******************************************************************************* + * 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. + * + * @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(double f__ghz, double d__km, double p, double* L_ctt__db) { int rtn = Section3p2_InputValidation(f__ghz, d__km, p); @@ -34,17 +33,14 @@ int TerrestrialStatisticalModel(double f__ghz, double d__km, double p, double* L 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 - | - *===========================================================================*/ +/******************************************************************************* + * 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(double f__ghz, double d__km, double p) { // Equations 4a and 4b @@ -67,18 +63,15 @@ double TerrestrialStatisticalModelHelper(double f__ghz, double d__km, double p) 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 - | - *===========================================================================*/ +/******************************************************************************* + * 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(double f__ghz, double d__km, double p) { if (f__ghz < 0.5 || f__ghz > 67) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..cbc13cb --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,25 @@ +############################################ +## 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}) +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..4f5fb0f --- /dev/null +++ b/tests/TestAeronauticalStatisticalModel.cpp @@ -0,0 +1,64 @@ +#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..f70365c --- /dev/null +++ b/tests/TestHeightGainTerminalCorrectionModel.cpp @@ -0,0 +1,82 @@ +#include // For std::numeric_limits::max() +#include "TestUtils.h" + +// 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..909aa98 --- /dev/null +++ b/tests/TestInverseComplementaryCumulativeDistribution.cpp @@ -0,0 +1,17 @@ +#include +#include "TestUtils.h" + +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..ee75a37 --- /dev/null +++ b/tests/TestTerrestrialStatisticalModel.cpp @@ -0,0 +1,58 @@ +#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..2efd15f --- /dev/null +++ b/tests/TestUtils.cpp @@ -0,0 +1,71 @@ +#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; + AeronauticalStatisticalModelTestData d; // struct to store data from a single line of CSV + 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; + HeightGainTerminalCorrectionModelTestData d; // struct to store data from a single line of CSV + 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; + TerrestrialStatisticalModelTestData d; // struct to store data from a single line of CSV + 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..34eb879 --- /dev/null +++ b/tests/TestUtils.h @@ -0,0 +1,45 @@ +#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 + +// 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 From b96c86f4a2c33c2c3ce9c93d66a92ddd84ef6752 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 9 Jul 2024 16:07:36 -0400 Subject: [PATCH 004/379] Update CITATION.cff New DOI needed after 1.0 release --- CITATION.cff | 48 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index 1c1824d..fe6a76a 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -1,16 +1,48 @@ 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 + 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 + - given-names: Anthony + family-names: Romaniello + name-particle: W. + email: aromaniello@ntia.gov + affiliation: >- + U.S. Department of Commerce, National + Telecommunications and Information Administration, + Institute for Telecommunication Sciences + orcid: 'https://orcid.org/0000-0001-8437-6504' + - name: >- + U.S. Department of Commerce, National + Telecommunications and Information Administration, + Institute for Telecommunications Sciences + address: 325 Broadway + city: Boulder + country: US + post-code: '80305' + region: Colorado + email: code@ntia.gov + website: 'https://its.ntia.gov' + alias: NTIA/ITS +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' From 9a403da5feaaa45a446c0f60180b2425a508e03c Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 9 Jul 2024 16:07:42 -0400 Subject: [PATCH 005/379] Create CONTRIBUTING.md --- CONTRIBUTING.md | 206 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..5f17ddb --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,206 @@ +# 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. + +## 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 cannnot 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 + +**Note:** the following section refers to C++ "base" PropLib libraries _only_. For +other languages, it is recommended to follow best and common practices for documenting +code in that particular language. It is further recommended that checked-in code +meets the same general documentation requirements outlined below, even if there +is no automated documentation check which must pass before code can be checked in. + +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 succesfully 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; +} +``` From 91e4988cedc866b2a9c7a81029c1ff3e28b683a3 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 9 Jul 2024 16:08:07 -0400 Subject: [PATCH 006/379] Remove dotnet wrapper from repository Will soon be added as a Git submodule --- .../ITS.ITU.PSeries.P2108.csproj | 50 ------- dotnet/ITS.ITU.PSeries.P2108/P2108.cs | 137 ------------------ .../Properties/AssemblyInfo.cs | 35 ----- dotnet/UnitTests/BVT.cs | 69 --------- dotnet/UnitTests/Properties/AssemblyInfo.cs | 36 ----- dotnet/UnitTests/TestData.cs | 108 -------------- dotnet/UnitTests/Unit-Test-Instructions.txt | 7 - dotnet/UnitTests/UnitTests.csproj | 102 ------------- dotnet/UnitTests/packages.config | 11 -- dotnet/nuget/build/net481/p2108.targets | 16 -- dotnet/nuget/images/itslogo.png | Bin 12605 -> 0 bytes dotnet/nuget/p2108.nuspec | 30 ---- dotnet/p2108_dotnet.sln | 34 ----- 13 files changed, 635 deletions(-) delete mode 100644 dotnet/ITS.ITU.PSeries.P2108/ITS.ITU.PSeries.P2108.csproj delete mode 100644 dotnet/ITS.ITU.PSeries.P2108/P2108.cs delete mode 100644 dotnet/ITS.ITU.PSeries.P2108/Properties/AssemblyInfo.cs delete mode 100644 dotnet/UnitTests/BVT.cs delete mode 100644 dotnet/UnitTests/Properties/AssemblyInfo.cs delete mode 100644 dotnet/UnitTests/TestData.cs delete mode 100644 dotnet/UnitTests/Unit-Test-Instructions.txt delete mode 100644 dotnet/UnitTests/UnitTests.csproj delete mode 100644 dotnet/UnitTests/packages.config delete mode 100644 dotnet/nuget/build/net481/p2108.targets delete mode 100644 dotnet/nuget/images/itslogo.png delete mode 100644 dotnet/nuget/p2108.nuspec delete mode 100644 dotnet/p2108_dotnet.sln 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 3d2f319b36d661b819f1418c7f27086d46a0f9f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12605 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 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 From 1dd934b77fdfaf4f2c57dbf278a4c7bad52a0942 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 9 Jul 2024 16:08:27 -0400 Subject: [PATCH 007/379] Add files for docs generation with Doxygen --- docs/CMakeLists.txt | 65 ++++++++++++++++++ docs/doxy_custom.css | 44 ++++++++++++ docs/doxy_footer.html | 47 +++++++++++++ docs/doxy_header.html | 113 +++++++++++++++++++++++++++++++ docs/doxy_mainpage.md | 32 +++++++++ docs/images/ITSlogoOnly400.png | Bin 0 -> 14748 bytes docs/images/apple-touch-icon.png | Bin 0 -> 5838 bytes docs/images/favicon-16x16.png | Bin 0 -> 2197 bytes docs/images/favicon-32x32.png | Bin 0 -> 2450 bytes docs/images/ntia-logo-300px.png | Bin 0 -> 9085 bytes 10 files changed, 301 insertions(+) create mode 100644 docs/CMakeLists.txt create mode 100644 docs/doxy_custom.css create mode 100644 docs/doxy_footer.html create mode 100644 docs/doxy_header.html create mode 100644 docs/doxy_mainpage.md create mode 100644 docs/images/ITSlogoOnly400.png create mode 100644 docs/images/apple-touch-icon.png create mode 100644 docs/images/favicon-16x16.png create mode 100644 docs/images/favicon-32x32.png create mode 100644 docs/images/ntia-logo-300px.png diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt new file mode 100644 index 0000000..a5a59a8 --- /dev/null +++ b/docs/CMakeLists.txt @@ -0,0 +1,65 @@ +########################################### +## 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` 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..6f09e9e --- /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 debug 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 0000000000000000000000000000000000000000..45a7ba3fdc8a947949e4eb97376705b2779a64d4 GIT binary patch literal 14748 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 literal 0 HcmV?d00001 diff --git a/docs/images/apple-touch-icon.png b/docs/images/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d6371ec98d7c7f24ddc8ac1570990a92336887dc GIT binary patch literal 5838 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 S^DO#(!)&Bys#~e!9R6SD2Sfz` literal 0 HcmV?d00001 diff --git a/docs/images/favicon-16x16.png b/docs/images/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..cc0687f5808893c9361d0c67cf5b5a374ae61185 GIT binary patch literal 2197 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 Date: Tue, 9 Jul 2024 16:08:34 -0400 Subject: [PATCH 008/379] Create GitHubRepoPublicReleaseApproval.md --- GitHubRepoPublicReleaseApproval.md | 41 ++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 GitHubRepoPublicReleaseApproval.md 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 From dedf0129252f0ad36973424233883e44a3ef2acc Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 9 Jul 2024 16:08:51 -0400 Subject: [PATCH 009/379] Update README.md Need to add updated DOI after v1.0 release --- README.md | 199 +++++++++++++++++------------------------------------- 1 file changed, 61 insertions(+), 138 deletions(-) diff --git a/README.md b/README.md index 34eb571..9715b96 100644 --- a/README.md +++ b/README.md @@ -1,165 +1,88 @@ -# Recommendation ITU-R P.2108-1 - U.S. Reference Implementation # +# Recommendation ITU-R P.2108 - U.S. Reference Implementation # -Persistent Identifier: [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7114033.svg)](https://doi.org/10.5281/zenodo.7114033) + +[![GitHub Actions Unit Tests Status][gh-actions-test-badge]][gh-actions-test-link] +[![C++ API Reference][gh-actions-docs-badge]][gh-actions-docs-link] +![GitHub Release](https://img.shields.io/github/v/release/NTIA/P2108) +![GitHub Issues](https://img.shields.io/github/issues/NTIA/P2108) + + +[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 +[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 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 +``` -### 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 . From bb58ac5a45478f957180d45d2872482b8155b8cc Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 9 Jul 2024 16:09:02 -0400 Subject: [PATCH 010/379] Add CI workflows --- .github/workflows/ctest.yml | 53 ++++++++++++++++++++++++++ .github/workflows/doxygen.yml | 63 +++++++++++++++++++++++++++++++ .github/workflows/release.yml | 70 +++++++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 .github/workflows/ctest.yml create mode 100644 .github/workflows/doxygen.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml new file mode 100644 index 0000000..ea6592a --- /dev/null +++ b/.github/workflows/ctest.yml @@ -0,0 +1,53 @@ +# This action compiles the library and runs all unit tests using an OS and CMake matrix +name: Unit Tests + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + 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 update + + - name: Install CMake + uses: lukka/get-cmake@latest + with: + cmakeVersion: ${{ matrix.cmakeVersion }} + + - name: "CMake: Build and Test (32-bit)" + if: matrix.os == 'windows-2019' + uses: lukka/run-cmake@v10 + with: + configurePreset: release32 + configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF']" + buildPreset: release32 + testPreset: release32 + + - name: "CMake: Build and Test (64-bit)" + if: matrix.os != 'windows-2019' + uses: lukka/run-cmake@v10 + with: + configurePreset: release64 + configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF']" + buildPreset: release64 + testPreset: release64 diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml new file mode 100644 index 0000000..d2a1d21 --- /dev/null +++ b/.github/workflows/doxygen.yml @@ -0,0 +1,63 @@ +# This action builds **AND DEPLOYS** Doxygen documentation to GitHub Pages +name: C++ Docs + +on: + release: + types: ["published"] + 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 + 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 + with: + path: ./docs/html/ + + deploy: + 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 From f992345311b58ca61b0b225243f6c8d10ff1fc7e Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 9 Jul 2024 16:09:40 -0400 Subject: [PATCH 011/379] Add cmake config to copy libraries to wrappers Does nothing if wrappers are not initialized --- wrap/CMakeLists.txt | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 wrap/CMakeLists.txt 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 From b25108b3c67cc85b2925af56e2723189d09a3cce Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 9 Jul 2024 16:12:00 -0400 Subject: [PATCH 012/379] Add submodules --- .gitmodules | 7 +++++++ extern/doxygen-awesome-css | 1 + extern/googletest | 1 + 3 files changed, 9 insertions(+) create mode 100644 .gitmodules create mode 160000 extern/doxygen-awesome-css create mode 160000 extern/googletest 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/extern/doxygen-awesome-css b/extern/doxygen-awesome-css new file mode 160000 index 0000000..28ed396 --- /dev/null +++ b/extern/doxygen-awesome-css @@ -0,0 +1 @@ +Subproject commit 28ed396de19cd3d803bcb483dceefdb6d03b1b2b 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 From 6b2ed081760fd7a24389a94537ef84da9f985e5e Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 9 Jul 2024 16:18:52 -0400 Subject: [PATCH 013/379] Build docs when running tests This ensures docs can be built before actions attempt to build and deploy them --- .github/workflows/ctest.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index ea6592a..dfa96ca 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -1,4 +1,5 @@ # 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: @@ -27,19 +28,24 @@ jobs: - 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 - configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF']" buildPreset: release32 testPreset: release32 @@ -48,6 +54,5 @@ jobs: uses: lukka/run-cmake@v10 with: configurePreset: release64 - configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF']" buildPreset: release64 testPreset: release64 From 14617e31ab140982dbadf22be31ff236559551be Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 9 Jul 2024 16:20:06 -0400 Subject: [PATCH 014/379] Remove outdated comments --- src/CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b20d4a3..297ec4b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -46,6 +46,3 @@ else () RELEASE_POSTFIX "x64" ) endif () - -# Configuration-dependent compile options -# TODO \ No newline at end of file From a43b2a5d1645f8a809f436d715f2480f2caf1c8d Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 9 Jul 2024 17:09:32 -0400 Subject: [PATCH 015/379] Use ITS::ITU::PSeries::P2108 namespace --- include/ITS.ITU.PSeries.P2108/Enums.h | 9 ++++++++- include/ITS.ITU.PSeries.P2108/Errors.h | 9 ++++++++- include/ITS.ITU.PSeries.P2108/P2108.h | 8 ++++++++ src/AeronauticalStatisticalModel.cpp | 9 ++++++++- src/HeightGainTerminalCorrectionModel.cpp | 9 ++++++++- src/InverseComplementaryCumulativeDistribution.cpp | 9 ++++++++- src/TerrestrialStatisticalModel.cpp | 9 ++++++++- tests/TestUtils.h | 2 ++ 8 files changed, 58 insertions(+), 6 deletions(-) diff --git a/include/ITS.ITU.PSeries.P2108/Enums.h b/include/ITS.ITU.PSeries.P2108/Enums.h index 79dd610..f17fd03 100644 --- a/include/ITS.ITU.PSeries.P2108/Enums.h +++ b/include/ITS.ITU.PSeries.P2108/Enums.h @@ -4,6 +4,11 @@ #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 */ @@ -30,4 +35,6 @@ enum class RepresentativeClutterHeight { DENSE_URBAN = 20, /**< @f$ R @f$ for the dense urban clutter type */ }; -#endif \ No newline at end of file +} } } } // End namespace ITS::ITU::PSeries::P2108 + +#endif diff --git a/include/ITS.ITU.PSeries.P2108/Errors.h b/include/ITS.ITU.PSeries.P2108/Errors.h index 2460e99..17e42ce 100644 --- a/include/ITS.ITU.PSeries.P2108/Errors.h +++ b/include/ITS.ITU.PSeries.P2108/Errors.h @@ -4,6 +4,11 @@ #ifndef __ITS_ITU_PSERIES_P2108_ERRORS__ #define __ITS_ITU_PSERIES_P2108_ERRORS__ +namespace ITS { +namespace ITU { +namespace PSeries { +namespace P2108 { + //////////////////////////////////////////////////////////////////////////////// // General Return Codes #define SUCCESS 0 /**< Successful execution */ @@ -28,4 +33,6 @@ #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 */ -#endif \ No newline at end of file +} } } } // End namespace ITS::ITU::PSeries::P2108 + +#endif diff --git a/include/ITS.ITU.PSeries.P2108/P2108.h b/include/ITS.ITU.PSeries.P2108/P2108.h index 833f685..dc2af2f 100644 --- a/include/ITS.ITU.PSeries.P2108/P2108.h +++ b/include/ITS.ITU.PSeries.P2108/P2108.h @@ -8,6 +8,11 @@ #include "Enums.h" #include "Errors.h" +namespace ITS { +namespace ITU { +namespace PSeries { +namespace P2108 { + // Define cross-platform EXPORTED #ifndef DOXYGEN_SHOULD_SKIP # ifdef _WIN32 @@ -50,4 +55,7 @@ int Section3p1_InputValidation(double f__ghz, double h__meter, double w_s__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); + +} } } } // End namespace ITS::ITU::PSeries::P2108 + #endif \ No newline at end of file diff --git a/src/AeronauticalStatisticalModel.cpp b/src/AeronauticalStatisticalModel.cpp index 203db08..a0d65c2 100644 --- a/src/AeronauticalStatisticalModel.cpp +++ b/src/AeronauticalStatisticalModel.cpp @@ -3,6 +3,11 @@ */ #include "ITS.ITU.PSeries.P2108/P2108.h" +namespace ITS { +namespace ITU { +namespace PSeries { +namespace P2108 { + /******************************************************************************* * The Earth-space and aeronautical statistical clutter loss model as described * in Section 3.3. @@ -82,4 +87,6 @@ int Section3p3_InputValidation(double f__ghz, double theta__deg, double p) double cot(double x) { return 1 / tan(x); -} \ No newline at end of file +} + +} } } } // End namespace ITS::ITU::PSeries::P2108 \ No newline at end of file diff --git a/src/HeightGainTerminalCorrectionModel.cpp b/src/HeightGainTerminalCorrectionModel.cpp index 459a1ac..c0f9e62 100644 --- a/src/HeightGainTerminalCorrectionModel.cpp +++ b/src/HeightGainTerminalCorrectionModel.cpp @@ -3,6 +3,11 @@ */ #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. * @@ -122,4 +127,6 @@ double Equation_2b(double K_h2, double h__meter, double R__meter) double A_h__db = -K_h2 * log10(h__meter / R__meter); return A_h__db; -} \ No newline at end of file +} + +} } } } // End namespace ITS::ITU::PSeries::P2108 \ No newline at end of file diff --git a/src/InverseComplementaryCumulativeDistribution.cpp b/src/InverseComplementaryCumulativeDistribution.cpp index d8b529e..95592ac 100644 --- a/src/InverseComplementaryCumulativeDistribution.cpp +++ b/src/InverseComplementaryCumulativeDistribution.cpp @@ -4,6 +4,11 @@ #include #include "ITS.ITU.PSeries.P2108/P2108.h" +namespace ITS { +namespace ITU { +namespace PSeries { +namespace P2108 { + /******************************************************************************* * Compute the inverse complementary cumulative distribution function * approximation as described in Recommendation ITU-R P.1057. @@ -43,4 +48,6 @@ double InverseComplementaryCumulativeDistribution(double q) Q_q = -Q_q; return Q_q; -} \ No newline at end of file +} + +} } } } // End namespace ITS::ITU::PSeries::P2108 \ No newline at end of file diff --git a/src/TerrestrialStatisticalModel.cpp b/src/TerrestrialStatisticalModel.cpp index cfd9ba2..f2452e9 100644 --- a/src/TerrestrialStatisticalModel.cpp +++ b/src/TerrestrialStatisticalModel.cpp @@ -3,6 +3,11 @@ */ #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. @@ -84,4 +89,6 @@ int Section3p2_InputValidation(double f__ghz, double d__km, double p) return ERROR32__PERCENTAGE; return SUCCESS; -} \ No newline at end of file +} + +} } } } // End namespace ITS::ITU::PSeries::P2108 \ No newline at end of file diff --git a/tests/TestUtils.h b/tests/TestUtils.h index 34eb879..694e133 100644 --- a/tests/TestUtils.h +++ b/tests/TestUtils.h @@ -7,6 +7,8 @@ #include #include +using namespace ITS::ITU::PSeries::P2108; + // Absolute tolerance for checking model outputs against test data #define ABSTOL__DB 0.1 From f53c0c4bfe9ccc9d0fb549249a3bb275cb301577 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Wed, 10 Jul 2024 12:40:14 -0400 Subject: [PATCH 016/379] Add clang-format and apply to all C++ files --- .clang-format | 104 ++++++++++++++++++ include/ITS.ITU.PSeries.P2108/Enums.h | 37 ++++--- include/ITS.ITU.PSeries.P2108/Errors.h | 42 ++++--- include/ITS.ITU.PSeries.P2108/P2108.h | 50 ++++++--- src/AeronauticalStatisticalModel.cpp | 29 ++--- src/HeightGainTerminalCorrectionModel.cpp | 90 ++++++++------- ...rseComplementaryCumulativeDistribution.cpp | 22 ++-- src/TerrestrialStatisticalModel.cpp | 36 +++--- tests/TestAeronauticalStatisticalModel.cpp | 48 +++++--- .../TestHeightGainTerminalCorrectionModel.cpp | 79 +++++++++---- ...rseComplementaryCumulativeDistribution.cpp | 36 ++++-- tests/TestTerrestrialStatisticalModel.cpp | 64 +++++++---- tests/TestUtils.cpp | 39 ++++--- tests/TestUtils.h | 11 +- 14 files changed, 475 insertions(+), 212 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..2b4d72a --- /dev/null +++ b/.clang-format @@ -0,0 +1,104 @@ +# NTIA/ITS C++ Clang-Format Style Options +# Updated 7/10/2024 +--- +AccessModifierOffset: -4 +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 +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: true +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/include/ITS.ITU.PSeries.P2108/Enums.h b/include/ITS.ITU.PSeries.P2108/Enums.h index f17fd03..d9301b4 100644 --- a/include/ITS.ITU.PSeries.P2108/Enums.h +++ b/include/ITS.ITU.PSeries.P2108/Enums.h @@ -1,6 +1,6 @@ /** @file Enums.h * Enumerated types used by this software. -*/ + */ #ifndef __ITS_ITU_PSERIES_P2108_ENUMS__ #define __ITS_ITU_PSERIES_P2108_ENUMS__ @@ -11,30 +11,33 @@ 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 */ + 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 */ + 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 */ }; -} } } } // End namespace ITS::ITU::PSeries::P2108 +} // 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 index 17e42ce..0b28e9f 100644 --- a/include/ITS.ITU.PSeries.P2108/Errors.h +++ b/include/ITS.ITU.PSeries.P2108/Errors.h @@ -1,6 +1,6 @@ -/** @file Errors.h +/** @file Errors.h * Contains return codes used by this software. -*/ + */ #ifndef __ITS_ITU_PSERIES_P2108_ERRORS__ #define __ITS_ITU_PSERIES_P2108_ERRORS__ @@ -11,28 +11,40 @@ namespace P2108 { //////////////////////////////////////////////////////////////////////////////// // General Return Codes -#define SUCCESS 0 /**< Successful execution */ +#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 */ +#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 */ +#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 */ +#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 */ -} } } } // End namespace ITS::ITU::PSeries::P2108 +} // 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 index dc2af2f..36bf6a1 100644 --- a/include/ITS.ITU.PSeries.P2108/P2108.h +++ b/include/ITS.ITU.PSeries.P2108/P2108.h @@ -1,13 +1,14 @@ /** @file P2108.h * Interface header for this library -*/ + */ #ifndef __ITS_ITU_PSERIES_P2108_P2108_H__ #define __ITS_ITU_PSERIES_P2108_P2108_H__ -#include // For atan, fmin, log10, pow, sqrt, tan #include "Enums.h" #include "Errors.h" +#include // For atan, fmin, log10, pow, sqrt, tan + namespace ITS { namespace ITU { namespace PSeries { @@ -15,15 +16,16 @@ namespace P2108 { // Define cross-platform EXPORTED #ifndef DOXYGEN_SHOULD_SKIP -# ifdef _WIN32 -# define EXPORTED extern "C" __declspec(dllexport) -# else -# define EXPORTED extern "C" -# endif + #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. +// This makes long equations a bit more readable while avoiding total namespace +// chaos. using std::atan; using std::fmin; using std::log10; @@ -33,16 +35,24 @@ using std::tan; //////////////////////////////////////////////////////////////////////////////// // Constants -#define PI 3.14159265358979323846 /**< Approximate value of @f$ \pi @f$ */ +#define PI 3.14159265358979323846 /**< Approximate value of @f$ \pi @f$ */ //////////////////////////////////////////////////////////////////////////////// // Public Functions -EXPORTED int AeronauticalStatisticalModel(double f__ghz, double theta__deg, - double p, double* L_ces__db); -EXPORTED int TerrestrialStatisticalModel(double f__ghz, double d__km, - double p, double* L_ctt__db); -EXPORTED int HeightGainTerminalCorrectionModel(double f__ghz, double h__meter, - double w_s__meter, double R__meter, ClutterType clutter_type, double* A_h__db); +EXPORTED int AeronauticalStatisticalModel( + double f__ghz, double theta__deg, double p, double *L_ces__db +); +EXPORTED int TerrestrialStatisticalModel( + double f__ghz, double d__km, double p, double *L_ctt__db +); +EXPORTED int HeightGainTerminalCorrectionModel( + double f__ghz, + double h__meter, + double w_s__meter, + double R__meter, + ClutterType clutter_type, + double *A_h__db +); //////////////////////////////////////////////////////////////////////////////// // Private Functions @@ -50,12 +60,16 @@ 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 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); -} } } } // End namespace ITS::ITU::PSeries::P2108 +} // namespace P2108 +} // namespace PSeries +} // namespace ITU +} // namespace ITS #endif \ No newline at end of file diff --git a/src/AeronauticalStatisticalModel.cpp b/src/AeronauticalStatisticalModel.cpp index a0d65c2..6b9568a 100644 --- a/src/AeronauticalStatisticalModel.cpp +++ b/src/AeronauticalStatisticalModel.cpp @@ -1,6 +1,6 @@ /** @file AeronauticalStatisticalModel.cpp * Implements the model from ITU-R P.2108 Section 3.3. -*/ + */ #include "ITS.ITU.PSeries.P2108/P2108.h" namespace ITS { @@ -11,11 +11,11 @@ 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$ (%) @@ -26,9 +26,9 @@ namespace P2108 { * @param[out] L_ces__db Additional loss (clutter loss), in dB * @return Return code ******************************************************************************/ -int AeronauticalStatisticalModel(double f__ghz, double theta__deg, double p, - double* L_ces__db) -{ +int AeronauticalStatisticalModel( + double f__ghz, double theta__deg, double p, double *L_ces__db +) { int rtn = Section3p3_InputValidation(f__ghz, theta__deg, p); if (rtn != SUCCESS) return rtn; @@ -49,10 +49,10 @@ int AeronauticalStatisticalModel(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$ (%) @@ -62,8 +62,7 @@ int AeronauticalStatisticalModel(double f__ghz, double theta__deg, double p, * @param[in] p Percentage of locations, in % * @return Return code ******************************************************************************/ -int Section3p3_InputValidation(double f__ghz, double theta__deg, double p) -{ +int Section3p3_InputValidation(double f__ghz, double theta__deg, double p) { if (f__ghz < 10 || f__ghz > 100) return ERROR33__FREQUENCY; @@ -78,15 +77,17 @@ int Section3p3_InputValidation(double f__ghz, double theta__deg, double p) /******************************************************************************* * 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(double x) -{ +double cot(double x) { return 1 / tan(x); } -} } } } // End namespace ITS::ITU::PSeries::P2108 \ No newline at end of file +} // namespace P2108 +} // namespace PSeries +} // namespace ITU +} // namespace ITS \ No newline at end of file diff --git a/src/HeightGainTerminalCorrectionModel.cpp b/src/HeightGainTerminalCorrectionModel.cpp index c0f9e62..aacb9cf 100644 --- a/src/HeightGainTerminalCorrectionModel.cpp +++ b/src/HeightGainTerminalCorrectionModel.cpp @@ -1,6 +1,6 @@ /** @file HeightGainTerminalCorrectionModel.cpp * Implements the model from ITU-R P.2108 Section 3.1. -*/ + */ #include "ITS.ITU.PSeries.P2108/P2108.h" namespace ITS { @@ -10,7 +10,7 @@ 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. @@ -23,43 +23,50 @@ namespace P2108 { * @param[out] A_h__db Additional loss (clutter loss), in dB * @return Return code ******************************************************************************/ -int HeightGainTerminalCorrectionModel(double f__ghz, double h__meter, - double w_s__meter, double R__meter, ClutterType clutter_type, double *A_h__db) -{ - int rtn = Section3p1_InputValidation(f__ghz, h__meter, w_s__meter, R__meter); +int HeightGainTerminalCorrectionModel( + double f__ghz, + double h__meter, + double w_s__meter, + double R__meter, + ClutterType clutter_type, + double *A_h__db +) { + int rtn + = Section3p1_InputValidation(f__ghz, h__meter, w_s__meter, R__meter); if (rtn != SUCCESS) return rtn; - if (h__meter >= R__meter) - { + 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 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: - { - 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; + 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 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: + { + 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; } return SUCCESS; @@ -67,7 +74,7 @@ int HeightGainTerminalCorrectionModel(double f__ghz, double h__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. * @@ -77,9 +84,9 @@ int HeightGainTerminalCorrectionModel(double f__ghz, double h__meter, * @param[in] R__meter Representative clutter height, in meters * @return Return code ******************************************************************************/ -int Section3p1_InputValidation(double f__ghz, double h__meter, double w_s__meter, - double R__meter) -{ +int Section3p1_InputValidation( + double f__ghz, double h__meter, double w_s__meter, double R__meter +) { if (f__ghz < 0.03 || f__ghz > 3) return ERROR31__FREQUENCY; @@ -101,8 +108,7 @@ int Section3p1_InputValidation(double f__ghz, double h__meter, double w_s__meter * @param[in] nu Dimensionless diffraction parameter * @return Additional loss (clutter loss), in dB ******************************************************************************/ -double Equation_2a(double nu) -{ +double Equation_2a(double nu) { double J_nu__db; if (nu <= -0.78) J_nu__db = 0; @@ -122,11 +128,13 @@ double Equation_2a(double nu) * @param[in] R__meter Representative clutter height, in meters * @return Additional loss (clutter loss), in dB ******************************************************************************/ -double Equation_2b(double K_h2, double h__meter, double R__meter) -{ +double Equation_2b(double K_h2, double h__meter, double R__meter) { double A_h__db = -K_h2 * log10(h__meter / R__meter); return A_h__db; } -} } } } // End namespace ITS::ITU::PSeries::P2108 \ 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 95592ac..f657f64 100644 --- a/src/InverseComplementaryCumulativeDistribution.cpp +++ b/src/InverseComplementaryCumulativeDistribution.cpp @@ -1,9 +1,10 @@ /** @internal @file InverseComplementaryCumulativeDistribution.cpp * @brief Implements a function to calculate the inverse CCDF. -*/ -#include + */ #include "ITS.ITU.PSeries.P2108/P2108.h" +#include + namespace ITS { namespace ITU { namespace PSeries { @@ -12,17 +13,16 @@ 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(double q) -{ - if (q <= 0.0 || q >= 1.0 ) { +double InverseComplementaryCumulativeDistribution(double q) { + if (q <= 0.0 || q >= 1.0) { throw std::out_of_range("Input q must be between 0.0 and 1.0"); } @@ -40,7 +40,8 @@ double InverseComplementaryCumulativeDistribution(double q) 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); + 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; @@ -50,4 +51,7 @@ double InverseComplementaryCumulativeDistribution(double q) return Q_q; } -} } } } // End namespace ITS::ITU::PSeries::P2108 \ 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 f2452e9..d62d56e 100644 --- a/src/TerrestrialStatisticalModel.cpp +++ b/src/TerrestrialStatisticalModel.cpp @@ -1,6 +1,6 @@ /** @file TerrestrialStatisticalModel.cpp * Implements the model from ITU-R P.2108 Section 3.2. -*/ + */ #include "ITS.ITU.PSeries.P2108/P2108.h" namespace ITS { @@ -11,17 +11,18 @@ 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 modelling. - * + * * @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(double f__ghz, double d__km, double p, double* L_ctt__db) -{ +int TerrestrialStatisticalModel( + double f__ghz, double d__km, double p, double *L_ctt__db +) { int rtn = Section3p2_InputValidation(f__ghz, d__km, p); if (rtn != SUCCESS) return rtn; @@ -40,29 +41,32 @@ int TerrestrialStatisticalModel(double f__ghz, double d__km, double p, double* L /******************************************************************************* * 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(double f__ghz, double d__km, double p) -{ +double + TerrestrialStatisticalModelHelper(double f__ghz, double d__km, 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)); + 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); // 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 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); // Equation 3a - double L_ctt__db = -5 * log10(pow(10, -0.2 * L_l__db) + pow(10, -0.2 * L_s__db)) + 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; @@ -71,14 +75,13 @@ double TerrestrialStatisticalModelHelper(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(double f__ghz, double d__km, double p) -{ +int Section3p2_InputValidation(double f__ghz, double d__km, double p) { if (f__ghz < 0.5 || f__ghz > 67) return ERROR32__FREQUENCY; @@ -91,4 +94,7 @@ int Section3p2_InputValidation(double f__ghz, double d__km, double p) return SUCCESS; } -} } } } // End namespace ITS::ITU::PSeries::P2108 \ No newline at end of file +} // namespace P2108 +} // namespace PSeries +} // namespace ITU +} // namespace ITS \ No newline at end of file diff --git a/tests/TestAeronauticalStatisticalModel.cpp b/tests/TestAeronauticalStatisticalModel.cpp index 4f5fb0f..112e10e 100644 --- a/tests/TestAeronauticalStatisticalModel.cpp +++ b/tests/TestAeronauticalStatisticalModel.cpp @@ -1,11 +1,13 @@ #include "TestUtils.h" // Test fixture for the unit tests -class AeronauticalStatisticalModelTest : public ::testing::Test { +class AeronauticalStatisticalModelTest: public ::testing::Test { protected: void SetUp() override { // Load test data from CSV - testData = readAeronauticalStatisticalModelTestData("AeronauticalStatisticalModelTestData.csv"); + testData = readAeronauticalStatisticalModelTestData( + "AeronauticalStatisticalModelTestData.csv" + ); } // Vector to hold test data @@ -18,8 +20,10 @@ TEST_F(AeronauticalStatisticalModelTest, TestAeronauticalStatisticalModel) { 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); + 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); @@ -28,20 +32,36 @@ TEST_F(AeronauticalStatisticalModelTest, TestAeronauticalStatisticalModel) { } 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 + 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 + 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 + 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) { @@ -59,6 +79,6 @@ TEST(Section3p3_InputValidationTest, Section3p3_ValidInputs) { 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); + 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 index f70365c..157abf4 100644 --- a/tests/TestHeightGainTerminalCorrectionModel.cpp +++ b/tests/TestHeightGainTerminalCorrectionModel.cpp @@ -1,12 +1,15 @@ -#include // For std::numeric_limits::max() #include "TestUtils.h" +#include // For std::numeric_limits::max() + // Test fixture for the unit tests -class HeightGainTerminalCorrectionModelTest : public ::testing::Test { +class HeightGainTerminalCorrectionModelTest: public ::testing::Test { protected: void SetUp() override { // Load test data from CSV - testData = readHeightGainTerminalCorrectionModelTestData("HeightGainTerminalCorrectionModelTestData.csv"); + testData = readHeightGainTerminalCorrectionModelTestData( + "HeightGainTerminalCorrectionModelTestData.csv" + ); } // Vector to hold test data @@ -14,13 +17,22 @@ class HeightGainTerminalCorrectionModelTest : public ::testing::Test { }; // Test case to verify the HeightGainTerminalCorrectionModel function -TEST_F(HeightGainTerminalCorrectionModelTest, TestHeightGainTerminalCorrectionModel) { +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); + 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); @@ -29,23 +41,39 @@ TEST_F(HeightGainTerminalCorrectionModelTest, TestHeightGainTerminalCorrectionMo } 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 + 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 + 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 + 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 + 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) { @@ -54,9 +82,18 @@ TEST(Section3p1_InputValidationTest, Section3p1_ValidInputs) { 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); + 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) { @@ -67,16 +104,18 @@ TEST(Section3p1_Equation_2aTest, Section3p1_NuLessThanLimit) { } TEST(Section3p1_Equation_2aTest, Section3p1_NuAboveLimit) { - // Test a few values with separately-computed expected results for this equation + // 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 + // 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(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 index 909aa98..4418f2b 100644 --- a/tests/TestInverseComplementaryCumulativeDistribution.cpp +++ b/tests/TestInverseComplementaryCumulativeDistribution.cpp @@ -1,17 +1,33 @@ -#include #include "TestUtils.h" -TEST(InverseCCDFTest, TestInverseCCDF) { +#include - 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, 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); + 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 index ee75a37..4763dad 100644 --- a/tests/TestTerrestrialStatisticalModel.cpp +++ b/tests/TestTerrestrialStatisticalModel.cpp @@ -1,11 +1,13 @@ #include "TestUtils.h" // Test fixture for the unit tests -class TerrestrialStatisticalModelTest : public ::testing::Test { +class TerrestrialStatisticalModelTest: public ::testing::Test { protected: void SetUp() override { // Load test data from CSV - testData = readTerrestrialStatisticalModelTestData("TerrestrialStatisticalModelTestData.csv"); + testData = readTerrestrialStatisticalModelTestData( + "TerrestrialStatisticalModelTestData.csv" + ); } // Vector to hold test data @@ -18,8 +20,10 @@ TEST_F(TerrestrialStatisticalModelTest, TestTerrestrialStatisticalModel) { 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); + 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); @@ -28,31 +32,49 @@ TEST_F(TerrestrialStatisticalModelTest, TestTerrestrialStatisticalModel) { } 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 + 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 + 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 + 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 + 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 index 2efd15f..172ac20 100644 --- a/tests/TestUtils.cpp +++ b/tests/TestUtils.cpp @@ -1,13 +1,14 @@ #include "TestUtils.h" + #include #include void appendDirectorySep(std::string &str) { - #ifdef _WIN32 - str += "\\"; - #else - str += "/"; - #endif +#ifdef _WIN32 + str += "\\"; +#else + str += "/"; +#endif } std::string getDataDirectory() { @@ -19,33 +20,40 @@ std::string getDataDirectory() { return dataDir; } -std::vector readAeronauticalStatisticalModelTestData(const std::string& filename) { +std::vector + readAeronauticalStatisticalModelTestData(const std::string &filename) { std::vector testData; std::string dataDir = getDataDirectory(); std::ifstream file(dataDir + filename); std::string line; - AeronauticalStatisticalModelTestData d; // struct to store data from a single line of CSV + AeronauticalStatisticalModelTestData + d; // struct to store data from a single line of CSV 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) { + 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 + readHeightGainTerminalCorrectionModelTestData(const std::string &filename) { std::vector testData; std::string dataDir = getDataDirectory(); std::ifstream file(dataDir + filename); std::string line; - HeightGainTerminalCorrectionModelTestData d; // struct to store data from a single line of CSV + HeightGainTerminalCorrectionModelTestData + d; // struct to store data from a single line of CSV 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) { + 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); @@ -54,16 +62,19 @@ std::vector readHeightGainTerminalCor return testData; } -std::vector readTerrestrialStatisticalModelTestData(const std::string &filename) { +std::vector + readTerrestrialStatisticalModelTestData(const std::string &filename) { std::vector testData; std::string dataDir = getDataDirectory(); std::ifstream file(dataDir + filename); std::string line; - TerrestrialStatisticalModelTestData d; // struct to store data from a single line of CSV + TerrestrialStatisticalModelTestData + d; // struct to store data from a single line of CSV 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) { + if (iss >> d.f__ghz >> c >> d.d__km >> c >> d.p >> c >> d.rtn >> c + >> d.L_ctt__db) { testData.push_back(d); } } diff --git a/tests/TestUtils.h b/tests/TestUtils.h index 694e133..00e532d 100644 --- a/tests/TestUtils.h +++ b/tests/TestUtils.h @@ -4,8 +4,8 @@ #include "ITS.ITU.PSeries.P2108/P2108.h" #include -#include #include +#include using namespace ITS::ITU::PSeries::P2108; @@ -38,10 +38,13 @@ struct TerrestrialStatisticalModelTestData { double L_ctt__db; }; -std::vector readAeronauticalStatisticalModelTestData(const std::string& filename); +std::vector + readAeronauticalStatisticalModelTestData(const std::string &filename); -std::vector readHeightGainTerminalCorrectionModelTestData(const std::string& filename); +std::vector + readHeightGainTerminalCorrectionModelTestData(const std::string &filename); -std::vector readTerrestrialStatisticalModelTestData(const std::string &filename); +std::vector + readTerrestrialStatisticalModelTestData(const std::string &filename); #endif From 03c9c33ec4df72d49206aaab5ed937c91ceda571 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Wed, 10 Jul 2024 12:42:17 -0400 Subject: [PATCH 017/379] Add clang-format callout in contributing guidelines --- CONTRIBUTING.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5f17ddb..e710623 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -91,6 +91,9 @@ 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. +- A `.clang-format` file is included in the root of this repository. Most IDEs +support this type of file, which can be used to apply uniform code styling to +C++ source and header files. ## Project Structure and CMake From 2ce39ec27e76386ff538e4365444c0611240a14c Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Wed, 10 Jul 2024 12:49:18 -0400 Subject: [PATCH 018/379] Enable hot reload for MSVC compilers if supported Taken from default VS CMakeLists.txt --- src/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 297ec4b..2c2224d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -46,3 +46,9 @@ else () 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 From 7477469cca218392103dcb4f59a6ff9892e0e00b Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Wed, 10 Jul 2024 13:10:44 -0400 Subject: [PATCH 019/379] Run create release workflow once to test --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c369e5d..5ec2fef 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,6 +5,7 @@ name: Create Release Artifacts on: push: tags: ['v[0-9]+.*'] + branches: ['CrossPlatform-and-Proplib2.0'] workflow_dispatch: permissions: From 85580d421074654d1fe3138131901666087a2a33 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Wed, 10 Jul 2024 13:10:55 -0400 Subject: [PATCH 020/379] Revert temporary testing change --- .github/workflows/release.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5ec2fef..c369e5d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,7 +5,6 @@ name: Create Release Artifacts on: push: tags: ['v[0-9]+.*'] - branches: ['CrossPlatform-and-Proplib2.0'] workflow_dispatch: permissions: From ce4453b7ba52dae018299bb3531a318afc9e9194 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Wed, 10 Jul 2024 15:40:54 -0400 Subject: [PATCH 021/379] Add param ranges in doxygen docs --- src/HeightGainTerminalCorrectionModel.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/HeightGainTerminalCorrectionModel.cpp b/src/HeightGainTerminalCorrectionModel.cpp index aacb9cf..def8549 100644 --- a/src/HeightGainTerminalCorrectionModel.cpp +++ b/src/HeightGainTerminalCorrectionModel.cpp @@ -14,6 +14,11 @@ namespace P2108 { * 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 From d4b03f7be4cbf39bfa681ce4cf4e2b53985463d5 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Wed, 10 Jul 2024 15:41:36 -0400 Subject: [PATCH 022/379] Add parameter ranges in doxygen docs --- src/TerrestrialStatisticalModel.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/TerrestrialStatisticalModel.cpp b/src/TerrestrialStatisticalModel.cpp index d62d56e..cd5278b 100644 --- a/src/TerrestrialStatisticalModel.cpp +++ b/src/TerrestrialStatisticalModel.cpp @@ -12,7 +12,12 @@ 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 modelling. + * 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 From 75ee81865cb6aaf8b87b50296f40a2d79f2a9cd1 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Fri, 12 Jul 2024 17:21:48 -0400 Subject: [PATCH 023/379] WIP progress on contributing guide --- CONTRIBUTING.md | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e710623..717bb81 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -91,9 +91,13 @@ 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. -- A `.clang-format` file is included in the root of this repository. Most IDEs -support this type of file, which can be used to apply uniform code styling to -C++ source and header files. +- _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 commited to Git. +It is recommended to use this tool to autoformat Python code when checked in. ## Project Structure and CMake @@ -176,11 +180,7 @@ pull request! ## Documenting Code -**Note:** the following section refers to C++ "base" PropLib libraries _only_. For -other languages, it is recommended to follow best and common practices for documenting -code in that particular language. It is further recommended that checked-in code -meets the same general documentation requirements outlined below, even if there -is no automated documentation check which must pass before code can be checked in. +### 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 @@ -207,3 +207,11 @@ 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. + +### C#/.NET Wrappers + +### MATLAB Wrappers From a06dda3f98382ffc7eee3d73e5caa46399bfffdf Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Fri, 12 Jul 2024 18:08:27 -0400 Subject: [PATCH 024/379] Add Python and C# documentation guidelines --- CONTRIBUTING.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 717bb81..a3cfde0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -210,8 +210,58 @@ double doubleTheInput(double x) ### Python Wrappers -The Python wrapper code is documented in the [Sphinx](https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html) format. +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 -### MATLAB 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; + } +} +``` From 4859c34a246ad73067350130d6ed293e8a737c59 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 16 Jul 2024 17:15:57 -0400 Subject: [PATCH 025/379] Use updated .clang-format --- .clang-format | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.clang-format b/.clang-format index 2b4d72a..6e19720 100644 --- a/.clang-format +++ b/.clang-format @@ -4,7 +4,6 @@ AccessModifierOffset: -4 AlignAfterOpenBracket: BlockIndent AlignOperands: AlignAfterOperator -AlignTrailingComments: true AllowAllArgumentsOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: Never @@ -75,7 +74,7 @@ ObjCBreakBeforeNestedBlockParam: true ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: true PointerAlignment: Right -ReflowComments: true +ReflowComments: false SortIncludes: true SortUsingDeclarations: true SpaceAfterCStyleCast: false From a9aab9279eeb9c345d773b7b5851ec4f112f3f14 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 16 Jul 2024 17:18:48 -0400 Subject: [PATCH 026/379] clang-format align trailing comments --- .clang-format | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index 6e19720..95a10f1 100644 --- a/.clang-format +++ b/.clang-format @@ -1,9 +1,10 @@ # NTIA/ITS C++ Clang-Format Style Options -# Updated 7/10/2024 +# Updated 7/16/2024 --- AccessModifierOffset: -4 AlignAfterOpenBracket: BlockIndent AlignOperands: AlignAfterOperator +AlignTrailingComments: true AllowAllArgumentsOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: Never From 5e691134b0f2c05407ad4c262ef599b8408351d0 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Thu, 18 Jul 2024 12:58:07 -0400 Subject: [PATCH 027/379] Separate build and output directories Also fix multi-config output directories not being correct for "Release" and "Debug" configurations. Set the output directories specifically as target properties. --- CMakeLists.txt | 5 ----- CMakePresets.json | 7 +++---- src/CMakeLists.txt | 3 +++ tests/CMakeLists.txt | 6 ++++++ 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 08bb1e2..c1e1c4b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,11 +37,6 @@ option(BUILD_32BIT "Build project for x86/32-bit instead of x64/64-bit" OFF) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# If not specified, fall back to Debug build type -if (NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Debug") -endif () - ########################################## ## BUILD/RUN ########################################## diff --git a/CMakePresets.json b/CMakePresets.json index e3f44b9..8ba9db6 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -10,11 +10,8 @@ "name": "proplib-config-base", "hidden": true, "description": "Base configuration preset for ITS PropLib libraries", - "binaryDir": "${sourceDir}/bin/${presetName}", + "binaryDir": "${sourceDir}/build/${presetName}", "cacheVariables": { - "CMAKE_ARCHIVE_OUTPUT_DIRECTORY": "${sourceDir}/bin", - "CMAKE_LIBRARY_OUTPUT_DIRECTORY": "${sourceDir}/bin", - "CMAKE_RUNTIME_OUTPUT_DIRECTORY": "${sourceDir}/bin", "CMAKE_BUILD_RPATH_USE_ORIGIN": "ON", "DOCS_ONLY": "OFF", "BUILD_32BIT": "OFF", @@ -93,6 +90,7 @@ "name": "proplib-build-release-base", "hidden": true, "inherits": "proplib-build-base", + "configuration": "Release", "description": "Base 'Release' build preset for ITS PropLib libraries", "cleanFirst": true }, @@ -100,6 +98,7 @@ "name": "proplib-build-debug-base", "hidden": true, "inherits": "proplib-build-base", + "configuration": "Debug", "description": "Base 'Debug' build preset for ITS PropLib libraries", "verbose": true }, diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2c2224d..29cc742 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,6 +30,9 @@ 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 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cbc13cb..a505c89 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -21,5 +21,11 @@ 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 From 87e42b207ebe64c47224faab782cf15877d8a45c Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Thu, 18 Jul 2024 12:58:20 -0400 Subject: [PATCH 028/379] Const correctness --- include/ITS.ITU.PSeries.P2108/P2108.h | 44 ++++++++++++------- src/AeronauticalStatisticalModel.cpp | 25 +++++++---- src/HeightGainTerminalCorrectionModel.cpp | 33 ++++++++------ ...rseComplementaryCumulativeDistribution.cpp | 22 +++++----- src/TerrestrialStatisticalModel.cpp | 38 +++++++++------- 5 files changed, 97 insertions(+), 65 deletions(-) diff --git a/include/ITS.ITU.PSeries.P2108/P2108.h b/include/ITS.ITU.PSeries.P2108/P2108.h index 36bf6a1..20ec892 100644 --- a/include/ITS.ITU.PSeries.P2108/P2108.h +++ b/include/ITS.ITU.PSeries.P2108/P2108.h @@ -40,32 +40,46 @@ using std::tan; //////////////////////////////////////////////////////////////////////////////// // Public Functions EXPORTED int AeronauticalStatisticalModel( - double f__ghz, double theta__deg, double p, double *L_ces__db + const double f__ghz, + const double theta__deg, + const double p, + double *L_ces__db ); EXPORTED int TerrestrialStatisticalModel( - double f__ghz, double d__km, double p, double *L_ctt__db + const double f__ghz, const double d__km, const double p, double *L_ctt__db ); EXPORTED int HeightGainTerminalCorrectionModel( - double f__ghz, - double h__meter, - double w_s__meter, - double R__meter, - ClutterType clutter_type, + 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(double x); -double InverseComplementaryCumulativeDistribution(double q); -double Equation_2a(double nu); -double Equation_2b(double K_h2, double h__meter, double R__meter); +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( - double f__ghz, double h__meter, double w_s__meter, double R__meter + 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 ); -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); } // namespace P2108 } // namespace PSeries diff --git a/src/AeronauticalStatisticalModel.cpp b/src/AeronauticalStatisticalModel.cpp index 6b9568a..0955dd0 100644 --- a/src/AeronauticalStatisticalModel.cpp +++ b/src/AeronauticalStatisticalModel.cpp @@ -27,19 +27,24 @@ namespace P2108 { * @return Return code ******************************************************************************/ int AeronauticalStatisticalModel( - double f__ghz, double theta__deg, double p, double *L_ces__db + 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; @@ -62,7 +67,9 @@ int AeronauticalStatisticalModel( * @param[in] p Percentage of locations, in % * @return Return code ******************************************************************************/ -int Section3p3_InputValidation(double f__ghz, double theta__deg, double p) { +int Section3p3_InputValidation( + const double f__ghz, const double theta__deg, const double p +) { if (f__ghz < 10 || f__ghz > 100) return ERROR33__FREQUENCY; @@ -83,7 +90,7 @@ int Section3p3_InputValidation(double f__ghz, double theta__deg, double p) { * @param[in] x Argument, in radians * @return Cotangent of the argument, @f$ \cot(x) @f$ ******************************************************************************/ -double cot(double x) { +double cot(const double x) { return 1 / tan(x); } diff --git a/src/HeightGainTerminalCorrectionModel.cpp b/src/HeightGainTerminalCorrectionModel.cpp index def8549..93cc82c 100644 --- a/src/HeightGainTerminalCorrectionModel.cpp +++ b/src/HeightGainTerminalCorrectionModel.cpp @@ -14,7 +14,7 @@ namespace P2108 { * 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 @@ -29,11 +29,11 @@ namespace P2108 { * @return Return code ******************************************************************************/ int HeightGainTerminalCorrectionModel( - double f__ghz, - double h__meter, - double w_s__meter, - double R__meter, - ClutterType clutter_type, + 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 ) { int rtn @@ -46,10 +46,10 @@ int HeightGainTerminalCorrectionModel( return SUCCESS; } - double h_dif__meter = R__meter - h__meter; // Equation (2d) - double theta_clut__deg + 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) - double K_h2 = 21.8 + 6.2 * log10(f__ghz); // Equation (2f) + const double K_h2 = 21.8 + 6.2 * log10(f__ghz); // Equation (2f) switch (clutter_type) { case ClutterType::WATER_SEA: @@ -90,7 +90,10 @@ int HeightGainTerminalCorrectionModel( * @return Return code ******************************************************************************/ int Section3p1_InputValidation( - double f__ghz, double h__meter, double w_s__meter, double R__meter + 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; @@ -113,14 +116,14 @@ int Section3p1_InputValidation( * @param[in] nu Dimensionless diffraction parameter * @return Additional loss (clutter loss), in dB ******************************************************************************/ -double Equation_2a(double nu) { +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; } @@ -133,8 +136,10 @@ double Equation_2a(double nu) { * @param[in] R__meter Representative clutter height, in meters * @return 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); +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; } diff --git a/src/InverseComplementaryCumulativeDistribution.cpp b/src/InverseComplementaryCumulativeDistribution.cpp index f657f64..3dea55f 100644 --- a/src/InverseComplementaryCumulativeDistribution.cpp +++ b/src/InverseComplementaryCumulativeDistribution.cpp @@ -19,29 +19,29 @@ namespace P2108 { * * @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] + * @throw std::out_of_range if the input is outside the range [0.0, 1.0] ******************************************************************************/ -double InverseComplementaryCumulativeDistribution(double q) { +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 - 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; + const double C_0 = 2.515517; + const double C_1 = 0.802853; + const double C_2 = 0.010328; + const double D_1 = 1.432788; + const double D_2 = 0.189269; + const 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; diff --git a/src/TerrestrialStatisticalModel.cpp b/src/TerrestrialStatisticalModel.cpp index cd5278b..c5a29b5 100644 --- a/src/TerrestrialStatisticalModel.cpp +++ b/src/TerrestrialStatisticalModel.cpp @@ -13,7 +13,7 @@ namespace P2108 { * 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 @@ -26,17 +26,19 @@ namespace P2108 { * @return Return code ******************************************************************************/ int TerrestrialStatisticalModel( - double f__ghz, double d__km, double p, double *L_ctt__db + 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); @@ -52,25 +54,27 @@ int TerrestrialStatisticalModel( * @param[in] p Percentage of locations, in % * @return Clutter loss, in dB ******************************************************************************/ -double - TerrestrialStatisticalModelHelper(double f__ghz, double d__km, double p) { +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 + 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 + 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); @@ -86,7 +90,9 @@ double * @param[in] p Percentage of locations, in % * @return Return code ******************************************************************************/ -int Section3p2_InputValidation(double f__ghz, double d__km, double p) { +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; From bd3932c23b230fe6fa0d8bca78fa5421b3030d33 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Thu, 18 Jul 2024 13:05:47 -0400 Subject: [PATCH 029/379] Replace pointer outputs with outputs by reference --- include/ITS.ITU.PSeries.P2108/P2108.h | 6 +++--- src/AeronauticalStatisticalModel.cpp | 4 ++-- src/HeightGainTerminalCorrectionModel.cpp | 8 ++++---- src/TerrestrialStatisticalModel.cpp | 4 ++-- tests/TestAeronauticalStatisticalModel.cpp | 2 +- tests/TestHeightGainTerminalCorrectionModel.cpp | 2 +- tests/TestTerrestrialStatisticalModel.cpp | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/include/ITS.ITU.PSeries.P2108/P2108.h b/include/ITS.ITU.PSeries.P2108/P2108.h index 20ec892..a879c29 100644 --- a/include/ITS.ITU.PSeries.P2108/P2108.h +++ b/include/ITS.ITU.PSeries.P2108/P2108.h @@ -43,10 +43,10 @@ EXPORTED int AeronauticalStatisticalModel( const double f__ghz, const double theta__deg, const double p, - double *L_ces__db + double &L_ces__db ); EXPORTED int TerrestrialStatisticalModel( - const double f__ghz, const double d__km, const double p, double *L_ctt__db + const double f__ghz, const double d__km, const double p, double &L_ctt__db ); EXPORTED int HeightGainTerminalCorrectionModel( const double f__ghz, @@ -54,7 +54,7 @@ EXPORTED int HeightGainTerminalCorrectionModel( const double w_s__meter, const double R__meter, const ClutterType clutter_type, - double *A_h__db + double &A_h__db ); //////////////////////////////////////////////////////////////////////////////// diff --git a/src/AeronauticalStatisticalModel.cpp b/src/AeronauticalStatisticalModel.cpp index 0955dd0..ef66577 100644 --- a/src/AeronauticalStatisticalModel.cpp +++ b/src/AeronauticalStatisticalModel.cpp @@ -30,7 +30,7 @@ int AeronauticalStatisticalModel( const double f__ghz, const double theta__deg, const double p, - double *L_ces__db + double &L_ces__db ) { int rtn = Section3p3_InputValidation(f__ghz, theta__deg, p); if (rtn != SUCCESS) @@ -46,7 +46,7 @@ int AeronauticalStatisticalModel( 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; } diff --git a/src/HeightGainTerminalCorrectionModel.cpp b/src/HeightGainTerminalCorrectionModel.cpp index 93cc82c..32db55f 100644 --- a/src/HeightGainTerminalCorrectionModel.cpp +++ b/src/HeightGainTerminalCorrectionModel.cpp @@ -34,7 +34,7 @@ int HeightGainTerminalCorrectionModel( const double w_s__meter, const double R__meter, const ClutterType clutter_type, - double *A_h__db + double &A_h__db ) { int rtn = Section3p1_InputValidation(f__ghz, h__meter, w_s__meter, R__meter); @@ -42,7 +42,7 @@ int HeightGainTerminalCorrectionModel( return rtn; if (h__meter >= R__meter) { - *A_h__db = 0; + A_h__db = 0; return SUCCESS; } @@ -54,7 +54,7 @@ int HeightGainTerminalCorrectionModel( switch (clutter_type) { case ClutterType::WATER_SEA: case ClutterType::OPEN_RURAL: - *A_h__db = Equation_2b(K_h2, h__meter, R__meter); + A_h__db = Equation_2b(K_h2, h__meter, R__meter); break; case ClutterType::SUBURBAN: @@ -67,7 +67,7 @@ int HeightGainTerminalCorrectionModel( = K_nu * sqrt(h_dif__meter * theta_clut__deg); // Equation (2c) - *A_h__db = Equation_2a(nu); + A_h__db = Equation_2a(nu); break; } default: diff --git a/src/TerrestrialStatisticalModel.cpp b/src/TerrestrialStatisticalModel.cpp index c5a29b5..fa69369 100644 --- a/src/TerrestrialStatisticalModel.cpp +++ b/src/TerrestrialStatisticalModel.cpp @@ -26,7 +26,7 @@ namespace P2108 { * @return Return code ******************************************************************************/ int TerrestrialStatisticalModel( - const double f__ghz, const double d__km, const double p, double *L_ctt__db + 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) @@ -41,7 +41,7 @@ int TerrestrialStatisticalModel( = 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; } diff --git a/tests/TestAeronauticalStatisticalModel.cpp b/tests/TestAeronauticalStatisticalModel.cpp index 112e10e..f868f29 100644 --- a/tests/TestAeronauticalStatisticalModel.cpp +++ b/tests/TestAeronauticalStatisticalModel.cpp @@ -22,7 +22,7 @@ TEST_F(AeronauticalStatisticalModelTest, TestAeronauticalStatisticalModel) { int rtn; for (const auto &data : testData) { rtn = AeronauticalStatisticalModel( - data.f__ghz, data.theta__deg, data.p, &L_ces__db + data.f__ghz, data.theta__deg, data.p, L_ces__db ); EXPECT_EQ(rtn, data.rtn); if (rtn == SUCCESS) { diff --git a/tests/TestHeightGainTerminalCorrectionModel.cpp b/tests/TestHeightGainTerminalCorrectionModel.cpp index 157abf4..c51eeca 100644 --- a/tests/TestHeightGainTerminalCorrectionModel.cpp +++ b/tests/TestHeightGainTerminalCorrectionModel.cpp @@ -31,7 +31,7 @@ TEST_F( data.w_s__meter, data.R__meter, data.clutter_type, - &A_h__db + A_h__db ); EXPECT_EQ(rtn, data.rtn); if (rtn == SUCCESS) { diff --git a/tests/TestTerrestrialStatisticalModel.cpp b/tests/TestTerrestrialStatisticalModel.cpp index 4763dad..a057a2a 100644 --- a/tests/TestTerrestrialStatisticalModel.cpp +++ b/tests/TestTerrestrialStatisticalModel.cpp @@ -22,7 +22,7 @@ TEST_F(TerrestrialStatisticalModelTest, TestTerrestrialStatisticalModel) { int rtn; for (const auto &data : testData) { rtn = TerrestrialStatisticalModel( - data.f__ghz, data.d__km, data.p, &L_ctt__db + data.f__ghz, data.d__km, data.p, L_ctt__db ); EXPECT_EQ(rtn, data.rtn); if (rtn == SUCCESS) { From 871df37b74c221270e05b49cc8762dbddce155f0 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Mon, 22 Jul 2024 11:08:50 -0400 Subject: [PATCH 030/379] Minor grammatical update --- include/ITS.ITU.PSeries.P2108/Enums.h | 2 +- include/ITS.ITU.PSeries.P2108/Errors.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/ITS.ITU.PSeries.P2108/Enums.h b/include/ITS.ITU.PSeries.P2108/Enums.h index d9301b4..d998982 100644 --- a/include/ITS.ITU.PSeries.P2108/Enums.h +++ b/include/ITS.ITU.PSeries.P2108/Enums.h @@ -1,5 +1,5 @@ /** @file Enums.h - * Enumerated types used by this software. + * Enumerated types used by this software */ #ifndef __ITS_ITU_PSERIES_P2108_ENUMS__ #define __ITS_ITU_PSERIES_P2108_ENUMS__ diff --git a/include/ITS.ITU.PSeries.P2108/Errors.h b/include/ITS.ITU.PSeries.P2108/Errors.h index 0b28e9f..175bee8 100644 --- a/include/ITS.ITU.PSeries.P2108/Errors.h +++ b/include/ITS.ITU.PSeries.P2108/Errors.h @@ -1,5 +1,5 @@ /** @file Errors.h - * Contains return codes used by this software. + * Contains return codes used by this software */ #ifndef __ITS_ITU_PSERIES_P2108_ERRORS__ #define __ITS_ITU_PSERIES_P2108_ERRORS__ From bf8d4b76fa4c3db6cc8bc43c2672d5d5df9a12ac Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Fri, 13 Sep 2024 12:10:44 -0400 Subject: [PATCH 031/379] Add const to internal variables --- src/HeightGainTerminalCorrectionModel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/HeightGainTerminalCorrectionModel.cpp b/src/HeightGainTerminalCorrectionModel.cpp index 32db55f..f2a0561 100644 --- a/src/HeightGainTerminalCorrectionModel.cpp +++ b/src/HeightGainTerminalCorrectionModel.cpp @@ -36,7 +36,7 @@ int HeightGainTerminalCorrectionModel( const ClutterType clutter_type, double &A_h__db ) { - int rtn + const int rtn = Section3p1_InputValidation(f__ghz, h__meter, w_s__meter, R__meter); if (rtn != SUCCESS) return rtn; @@ -62,8 +62,8 @@ int HeightGainTerminalCorrectionModel( case ClutterType::TREES_FOREST: case ClutterType::DENSE_URBAN: { - double K_nu = 0.342 * sqrt(f__ghz); // Equation (2g) - double nu + 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) From 5354464aeaa936e1afac1fe1a3d2dc9cc572c407 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Fri, 13 Sep 2024 12:10:50 -0400 Subject: [PATCH 032/379] Use constexpr where applicable --- src/InverseComplementaryCumulativeDistribution.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/InverseComplementaryCumulativeDistribution.cpp b/src/InverseComplementaryCumulativeDistribution.cpp index 3dea55f..67da705 100644 --- a/src/InverseComplementaryCumulativeDistribution.cpp +++ b/src/InverseComplementaryCumulativeDistribution.cpp @@ -27,12 +27,12 @@ double InverseComplementaryCumulativeDistribution(const double q) { } // Constants from Abramowitz & Stegun 26.2.23 - const double C_0 = 2.515517; - const double C_1 = 0.802853; - const double C_2 = 0.010328; - const double D_1 = 1.432788; - const double D_2 = 0.189269; - const double D_3 = 0.001308; + 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) From 9520e742baa73c213c3ba2d51aa8c8a13ef69e26 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 24 Sep 2024 16:52:29 -0400 Subject: [PATCH 033/379] Fix spelling errors --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a3cfde0..ce335c0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -96,7 +96,7 @@ Most IDEs support this type of file, which can and should be used to apply unifo 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 commited to Git. +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 @@ -172,7 +172,7 @@ the Debug configuration will attempt to pass debug flags to the compiler. 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 cannnot guarantee +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 @@ -186,7 +186,7 @@ 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 succesfully generates the documentation site. Below is an +and the GitHub action successfully generates the documentation site. Below is an example showing the expected documentation formats. ```cpp From 30543e6a19513e78c0a29726a85f1d4b4fb1ed64 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 24 Sep 2024 16:54:45 -0400 Subject: [PATCH 034/379] Adjust comment formatting --- tests/TestUtils.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/TestUtils.cpp b/tests/TestUtils.cpp index 172ac20..f235e4c 100644 --- a/tests/TestUtils.cpp +++ b/tests/TestUtils.cpp @@ -26,8 +26,8 @@ std::vector std::string dataDir = getDataDirectory(); std::ifstream file(dataDir + filename); std::string line; - AeronauticalStatisticalModelTestData - d; // struct to store data from a single line of CSV + // 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); @@ -45,8 +45,8 @@ std::vector std::string dataDir = getDataDirectory(); std::ifstream file(dataDir + filename); std::string line; - HeightGainTerminalCorrectionModelTestData - d; // struct to store data from a single line of CSV + // 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)) { @@ -68,8 +68,8 @@ std::vector std::string dataDir = getDataDirectory(); std::ifstream file(dataDir + filename); std::string line; - TerrestrialStatisticalModelTestData - d; // struct to store data from a single line of CSV + // 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); From a2f6314203541c6e123d22b0cec232904be0a385 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 24 Sep 2024 17:21:36 -0400 Subject: [PATCH 035/379] Correct citation information Remove improper use of `name-particle` and incorrect formatting of ORCiD information --- CITATION.cff | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index fe6a76a..9639a70 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -7,22 +7,21 @@ type: software authors: - given-names: William family-names: Kozma - name-suffix: Jr + name-suffix: Jr. email: wkozma@ntia.gov affiliation: >- U.S. Department of Commerce, National Telecommunications and Information Administration, Institute for Telecommunication Sciences - orcid: 'https://orcid.org/0000-0002-7417-4009' - - given-names: Anthony + orcid: '0000-0002-7417-4009' + - given-names: Anthony W. family-names: Romaniello - name-particle: W. email: aromaniello@ntia.gov affiliation: >- U.S. Department of Commerce, National Telecommunications and Information Administration, Institute for Telecommunication Sciences - orcid: 'https://orcid.org/0000-0001-8437-6504' + orcid: '0000-0001-8437-6504' - name: >- U.S. Department of Commerce, National Telecommunications and Information Administration, From 7e3ee23b1027ed9ecbd2f443db6de3754934fe45 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 24 Sep 2024 17:29:00 -0400 Subject: [PATCH 036/379] Towards CFF validation Removed "alias" key which is not valid for "entity" object --- CITATION.cff | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index 9639a70..9bc628c 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -5,35 +5,34 @@ title: >- message: Please cite this software using these metadata. type: software authors: - - given-names: William - family-names: Kozma + - family-names: Kozma + given-names: William name-suffix: Jr. - email: wkozma@ntia.gov affiliation: >- U.S. Department of Commerce, National Telecommunications and Information Administration, Institute for Telecommunication Sciences orcid: '0000-0002-7417-4009' - - given-names: Anthony W. - family-names: Romaniello - email: aromaniello@ntia.gov + 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: '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 - country: US - post-code: '80305' region: Colorado + post-code: '80305' + country: US email: code@ntia.gov website: 'https://its.ntia.gov' - alias: NTIA/ITS repository-code: 'https://github.com/NTIA/p2108' url: 'https://github.com/NTIA/propagation/wiki' repository: 'https://github.com/NTIA/p2108' From b5f2208b960eef612f320cfd850fe49d716e63d9 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Tue, 24 Sep 2024 17:47:30 -0400 Subject: [PATCH 037/379] Revert orcid formatting change cffvalidator does not like this for 1.2.0 --- CITATION.cff | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index 9bc628c..56cd2cf 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -12,7 +12,7 @@ authors: U.S. Department of Commerce, National Telecommunications and Information Administration, Institute for Telecommunication Sciences - orcid: '0000-0002-7417-4009' + orcid: 'https://orcid.org/0000-0002-7417-4009' email: wkozma@ntia.gov - family-names: Romaniello given-names: Anthony W. @@ -20,7 +20,7 @@ authors: U.S. Department of Commerce, National Telecommunications and Information Administration, Institute for Telecommunication Sciences - orcid: '0000-0001-8437-6504' + orcid: 'https://orcid.org/0000-0001-8437-6504' email: aromaniello@ntia.gov - name: >- U.S. Department of Commerce, National From 8d6a1d0526b07dd8dda62a73f51637b1bf81b612 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Wed, 25 Sep 2024 13:45:40 -0400 Subject: [PATCH 038/379] Run ctest action on dev branch PR's as well --- .github/workflows/ctest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index dfa96ca..75f0645 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -4,9 +4,9 @@ name: Unit Tests on: push: - branches: ["main"] + branches: ["main", "dev"] pull_request: - branches: ["main"] + branches: ["main", "dev"] workflow_dispatch: # Define the matrix for different operating systems From ec59ea2507afefdcaa292af6b2c0a7fb6dd63a54 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Wed, 25 Sep 2024 13:46:07 -0400 Subject: [PATCH 039/379] Set Doxygen action to only DEPLOY on release But still run doxygen as a CI test for pull requests to main and dev --- .github/workflows/doxygen.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index d2a1d21..7af9595 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -1,9 +1,13 @@ # 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. @@ -44,10 +48,12 @@ jobs: - 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 From baf10b08e1603341448011f8ff0f5aacc2eb0cb8 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Wed, 25 Sep 2024 13:46:20 -0400 Subject: [PATCH 040/379] Always use main branch Actions badges in readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9715b96..855d4fc 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ [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 +[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 +[gh-actions-docs-badge]: https://github.com/NTIA/p2108/actions/workflows/doxygen.yml/badge.svg?branch=main This code repository contains the U.S. Reference Software Implementation of Recommendation ITU-R P.2108. This Recommendation contains three methods for the From b7acf24016a7fa6f815c72554cbe42c0832dce44 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Wed, 25 Sep 2024 13:48:26 -0400 Subject: [PATCH 041/379] Skip Github pages setup when not deploying --- .github/workflows/doxygen.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index 7af9595..544c7dc 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -34,6 +34,7 @@ jobs: version: "1.11.0" - name: Setup GitHub Pages + if: ${{ github.event_name == 'release' }} id: pages uses: actions/configure-pages@v5 From 0e56fa997a7a992a8d24e04c4e1f239dfc092048 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Thu, 26 Sep 2024 14:02:08 -0400 Subject: [PATCH 042/379] Fix git submodule instructions --- CMakeLists.txt | 3 ++- README.md | 2 +- docs/CMakeLists.txt | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c1e1c4b..490a959 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,7 +52,8 @@ if (NOT DOCS_ONLY) else () message(SEND_ERROR "Unable to build tests. GoogleTest submodule is missing. " - "Run `git submodule init extern/googletest` and try again." + "Run `git submodule init extern/googletest` then " + "`git submodule update` and try again." ) endif() endif () diff --git a/README.md b/README.md index 855d4fc..c22af10 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ In order to do either, ensure the required submodules are cloned by running: # 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 +git submodule update # Clones the initialized submodules ``` ## Running Tests ## diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index a5a59a8..1125dc1 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -11,7 +11,8 @@ set(EXTRA_STYLESHEET "${PROJECT_SOURCE_DIR}/extern/doxygen-awesome-css/doxygen-a if (NOT EXISTS ${EXTRA_STYLESHEET}) message(FATAL_ERROR "External Doxygen stylesheet is missing! " - "Run `git submodule init extern/doxygen-awesome-css` and try again." + "Run `git submodule init extern/doxygen-awesome-css`, then " + "`git submodule update` and try again." ) endif () From 0a6093b513ac587039ea2cc2c8d6f640dd678e3f Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Thu, 26 Sep 2024 17:22:38 -0400 Subject: [PATCH 043/379] update clang-format --- .clang-format | 4 +-- tests/TestAeronauticalStatisticalModel.cpp | 18 +++++----- .../TestHeightGainTerminalCorrectionModel.cpp | 18 +++++----- tests/TestTerrestrialStatisticalModel.cpp | 18 +++++----- tests/TestUtils.h | 34 +++++++++---------- 5 files changed, 46 insertions(+), 46 deletions(-) diff --git a/.clang-format b/.clang-format index 95a10f1..38d450e 100644 --- a/.clang-format +++ b/.clang-format @@ -1,7 +1,6 @@ # NTIA/ITS C++ Clang-Format Style Options -# Updated 7/16/2024 +# Updated 9/25/2024 --- -AccessModifierOffset: -4 AlignAfterOpenBracket: BlockIndent AlignOperands: AlignAfterOperator AlignTrailingComments: true @@ -56,6 +55,7 @@ DerivePointerAlignment: false EmptyLineBeforeAccessModifier: Never FixNamespaceComments: true IncludeBlocks: Regroup +IndentAccessModifiers: true IndentCaseBlocks: true IndentCaseLabels: true IndentExternBlock: Indent diff --git a/tests/TestAeronauticalStatisticalModel.cpp b/tests/TestAeronauticalStatisticalModel.cpp index f868f29..114e135 100644 --- a/tests/TestAeronauticalStatisticalModel.cpp +++ b/tests/TestAeronauticalStatisticalModel.cpp @@ -2,16 +2,16 @@ // Test fixture for the unit tests class AeronauticalStatisticalModelTest: public ::testing::Test { -protected: - void SetUp() override { - // Load test data from CSV - testData = readAeronauticalStatisticalModelTestData( - "AeronauticalStatisticalModelTestData.csv" - ); - } + protected: + void SetUp() override { + // Load test data from CSV + testData = readAeronauticalStatisticalModelTestData( + "AeronauticalStatisticalModelTestData.csv" + ); + } - // Vector to hold test data - std::vector testData; + // Vector to hold test data + std::vector testData; }; // Test case to verify the AeronauticalStatisticalModel function diff --git a/tests/TestHeightGainTerminalCorrectionModel.cpp b/tests/TestHeightGainTerminalCorrectionModel.cpp index c51eeca..dc06b0a 100644 --- a/tests/TestHeightGainTerminalCorrectionModel.cpp +++ b/tests/TestHeightGainTerminalCorrectionModel.cpp @@ -4,16 +4,16 @@ // Test fixture for the unit tests class HeightGainTerminalCorrectionModelTest: public ::testing::Test { -protected: - void SetUp() override { - // Load test data from CSV - testData = readHeightGainTerminalCorrectionModelTestData( - "HeightGainTerminalCorrectionModelTestData.csv" - ); - } + protected: + void SetUp() override { + // Load test data from CSV + testData = readHeightGainTerminalCorrectionModelTestData( + "HeightGainTerminalCorrectionModelTestData.csv" + ); + } - // Vector to hold test data - std::vector testData; + // Vector to hold test data + std::vector testData; }; // Test case to verify the HeightGainTerminalCorrectionModel function diff --git a/tests/TestTerrestrialStatisticalModel.cpp b/tests/TestTerrestrialStatisticalModel.cpp index a057a2a..fc5ca79 100644 --- a/tests/TestTerrestrialStatisticalModel.cpp +++ b/tests/TestTerrestrialStatisticalModel.cpp @@ -2,16 +2,16 @@ // Test fixture for the unit tests class TerrestrialStatisticalModelTest: public ::testing::Test { -protected: - void SetUp() override { - // Load test data from CSV - testData = readTerrestrialStatisticalModelTestData( - "TerrestrialStatisticalModelTestData.csv" - ); - } + protected: + void SetUp() override { + // Load test data from CSV + testData = readTerrestrialStatisticalModelTestData( + "TerrestrialStatisticalModelTestData.csv" + ); + } - // Vector to hold test data - std::vector testData; + // Vector to hold test data + std::vector testData; }; // Test case to verify the TerrestrialStatisticalModel function diff --git a/tests/TestUtils.h b/tests/TestUtils.h index 00e532d..8e5218b 100644 --- a/tests/TestUtils.h +++ b/tests/TestUtils.h @@ -13,29 +13,29 @@ using namespace ITS::ITU::PSeries::P2108; #define ABSTOL__DB 0.1 struct AeronauticalStatisticalModelTestData { - double f__ghz; - double theta__deg; - double p; - int rtn; - double L_ces__db; + 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; + 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; + double f__ghz; + double d__km; + double p; + int rtn; + double L_ctt__db; }; std::vector From 11610f9a48e360d52499a5db65494b98018bfb38 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Thu, 26 Sep 2024 17:22:49 -0400 Subject: [PATCH 044/379] Fix incorrect information in doxy_mainpage.md --- docs/doxy_mainpage.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/doxy_mainpage.md b/docs/doxy_mainpage.md index 6f09e9e..18415f8 100644 --- a/docs/doxy_mainpage.md +++ b/docs/doxy_mainpage.md @@ -27,6 +27,6 @@ e.g. classes, functions, etc. 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 debug configuration. Additionally, the -documentation can be generated without compiling the source project by using the -`DOCS_ONLY` CMake option. +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. From 2474941021d6c26249d375d15429378297ca4656 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Thu, 26 Sep 2024 17:24:48 -0400 Subject: [PATCH 045/379] Use more link macros in README --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c22af10..5723fee 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,20 @@ # Recommendation ITU-R P.2108 - U.S. Reference Implementation # -[![GitHub Actions Unit Tests Status][gh-actions-test-badge]][gh-actions-test-link] +[![Unit Tests Status][gh-actions-test-badge]][gh-actions-test-link] [![C++ API Reference][gh-actions-docs-badge]][gh-actions-docs-link] -![GitHub Release](https://img.shields.io/github/v/release/NTIA/P2108) -![GitHub Issues](https://img.shields.io/github/issues/NTIA/P2108) - +![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 From 75ec95fe5933167c61e215db54cca8355d993a6b Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Thu, 26 Sep 2024 17:25:04 -0400 Subject: [PATCH 046/379] Improve formatting in Errors header Don't let clang-format ruin this one --- include/ITS.ITU.PSeries.P2108/Errors.h | 47 +++++++++++--------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/include/ITS.ITU.PSeries.P2108/Errors.h b/include/ITS.ITU.PSeries.P2108/Errors.h index 175bee8..cc48e3f 100644 --- a/include/ITS.ITU.PSeries.P2108/Errors.h +++ b/include/ITS.ITU.PSeries.P2108/Errors.h @@ -9,38 +9,31 @@ namespace ITU { namespace PSeries { namespace P2108 { -//////////////////////////////////////////////////////////////////////////////// -// General Return Codes -#define SUCCESS 0 /**< Successful execution */ +/////////////////////////////////////////////// +// 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 */ - -//////////////////////////////////////////////////////////////////////////////// +#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 */ +#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 */ +#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 From b3fb1054d1cae9d6fce1d4ef281938f82cf1024a Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Thu, 26 Sep 2024 17:25:50 -0400 Subject: [PATCH 047/379] Update top-level CMakeLists based on template sections --- CMakeLists.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 490a959..b2c22ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.14 FATAL_ERROR) ########################################### -## PROJECT METADATA AND CONFIGURATION +## PROJECT METADATA ########################################### set(LIB_NAME "P2108") # Name of library/target set(LIB_NAMESPACE "ITS.ITU.PSeries") # Namespace for the named library @@ -18,11 +18,16 @@ project( LANGUAGES "CXX" ) -# Configure wrapper locations +########################################### +## 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) From de176e75c0ae06a77ee0d83591dac26845cefc51 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Thu, 26 Sep 2024 17:27:00 -0400 Subject: [PATCH 048/379] fix spacing in clang-format --- .clang-format | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.clang-format b/.clang-format index 38d450e..96c3869 100644 --- a/.clang-format +++ b/.clang-format @@ -21,7 +21,7 @@ BasedOnStyle: WebKit BinPackArguments: false BinPackParameters: false BitFieldColonSpacing: After -BraceWrapping: +BraceWrapping: AfterCaseLabel: false AfterClass: false AfterControlStatement: false @@ -48,7 +48,7 @@ BreakConstructorInitializers: AfterColon BreakStringLiterals: true ColumnLimit: 80 CompactNamespaces: false -ConstructorInitializerIndentWidth : 4 +ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: false @@ -101,4 +101,3 @@ SpacesInSquareBrackets: false Standard: c++14 TabWidth: 4 UseTab: Never -... From b3cb3935b6e8be36da3190782225f00673960408 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Fri, 27 Sep 2024 12:46:57 -0400 Subject: [PATCH 049/379] Set doxygen-awesome-css to v2.3.3 --- extern/doxygen-awesome-css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/doxygen-awesome-css b/extern/doxygen-awesome-css index 28ed396..40e9b25 160000 --- a/extern/doxygen-awesome-css +++ b/extern/doxygen-awesome-css @@ -1 +1 @@ -Subproject commit 28ed396de19cd3d803bcb483dceefdb6d03b1b2b +Subproject commit 40e9b25b6174dd3b472d8868f63323a870dfeeb8 From 3c8c6a1e2f61c096798e9073f334c17a1b9b1e5f Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Fri, 27 Sep 2024 16:21:30 -0400 Subject: [PATCH 050/379] Initial commit --- .clang-format | 103 ++++++++++ .gitignore | 72 +++++++ CMakeLists.txt | 71 +++++++ CMakePresets.json | 192 +++++++++++++++++++ CONTRIBUTING.md | 267 ++++++++++++++++++++++++++ CREATING-REPOSITORIES.md | 311 +++++++++++++++++++++++++++++++ LICENSE.md | 34 ++++ README.md | 98 ++++++++++ app/CMakeLists.txt | 50 +++++ app/Driver.h | 44 +++++ app/Errors.h | 30 +++ app/README.md | 96 ++++++++++ app/Structs.h | 16 ++ app/Utils.cpp | 73 ++++++++ app/main.cpp | 168 +++++++++++++++++ docs/CMakeLists.txt | 66 +++++++ docs/doxy_custom.css | 44 +++++ docs/doxy_footer.html | 47 +++++ docs/doxy_header.html | 113 +++++++++++ docs/doxy_mainpage.md | 32 ++++ docs/images/ITSlogoOnly400.png | Bin 0 -> 14748 bytes docs/images/apple-touch-icon.png | Bin 0 -> 5838 bytes docs/images/favicon-16x16.png | Bin 0 -> 2197 bytes docs/images/favicon-32x32.png | Bin 0 -> 2450 bytes docs/images/ntia-logo-300px.png | Bin 0 -> 9085 bytes src/CMakeLists.txt | 53 ++++++ tests/CMakeLists.txt | 32 ++++ wrap/CMakeLists.txt | 28 +++ 28 files changed, 2040 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 CMakePresets.json create mode 100644 CONTRIBUTING.md create mode 100644 CREATING-REPOSITORIES.md create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 app/CMakeLists.txt create mode 100644 app/Driver.h create mode 100644 app/Errors.h create mode 100644 app/README.md create mode 100644 app/Structs.h create mode 100644 app/Utils.cpp create mode 100644 app/main.cpp create mode 100644 docs/CMakeLists.txt create mode 100644 docs/doxy_custom.css create mode 100644 docs/doxy_footer.html create mode 100644 docs/doxy_header.html create mode 100644 docs/doxy_mainpage.md create mode 100644 docs/images/ITSlogoOnly400.png create mode 100644 docs/images/apple-touch-icon.png create mode 100644 docs/images/favicon-16x16.png create mode 100644 docs/images/favicon-32x32.png create mode 100644 docs/images/ntia-logo-300px.png create mode 100644 src/CMakeLists.txt create mode 100644 tests/CMakeLists.txt create mode 100644 wrap/CMakeLists.txt 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/.gitignore b/.gitignore new file mode 100644 index 0000000..738bc1d --- /dev/null +++ b/.gitignore @@ -0,0 +1,72 @@ +############ +## Windows +############ + +# Windows image file caches +Thumbs.db +*.o + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +# User-specific files +**/.vs +**/bin +**/Debug/ +**/Release/ +**/dotnet/packages +**/dotnet/nuget +*.tlog +*.obj +*.log +*.lastbuildstate +*.manifest +*.users +*.user +*.res +*.opendb +*.db +*.unsuccessfulbuild +*.ipch +*.pdb +*.exp +*.ilk +*.idb +*.opensdf +*.sdf +*.u2d +*.suo +*.aps +**/obj +**/x64 +**/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/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..dd97d52 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,71 @@ +# >=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 +########################################### +# TODO-TEMPLATE: Add project metadata here. See CREATING-REPOSITORIES.md for an example. +set(LIB_NAME "TODO-TEMPLATE: YOUR-LIBRARY") # Name of library/target +set(LIB_NAMESPACE "TODO-TEMPLATE: ITS.YOUR-NAMESPACE") # Namespace for the named library +project( + "${LIB_NAMESPACE}.${LIB_NAME}" + VERSION 1.0.0 + DESCRIPTION "TODO-TEMPLATE: BRIEF DESCRIPTION OF YOUR SOFTWARE" + HOMEPAGE_URL "TODO-TEMPLATE: HOMEPAGE LINK, E.G. TO RELEVANT PAGE ON PROPLIB WIKI" + 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/CREATING-REPOSITORIES.md b/CREATING-REPOSITORIES.md new file mode 100644 index 0000000..ec35374 --- /dev/null +++ b/CREATING-REPOSITORIES.md @@ -0,0 +1,311 @@ +# Guide to Creating a PropLib Repository from this Template Repository + +Sketching a PropLib template repository + +**TODO**: Add high-level text on how to use this repository and document. Mention +searchable `TODO-TEMPLATE` text throughout repository. + +## Top-Level `CMakeLists.txt` + +Fill in project metadata, including the namespace and name of the library. Here +is a complete example of the populated fields for the Irregular Terrain Model (ITM): + +```cmake +set(LIB_NAME "ITM") # Name of library/target +set(LIB_NAMESPACE "ITS.Propagation") # Namespace for the named library +project( + "${LIB_NAMESPACE}.${LIB_NAME}" + VERSION 1.0.0 + DESCRIPTION "The ITS Irregular Terrain Model" + HOMEPAGE_URL "https://ntia.github.io/propagation-library-wiki/models/ITM" + LANGUAGES "CXX" +) +``` + +### Notes on Namespaces + +All ITS Propagation Library software should exist within the `ITS` namespace. +Further, hierarchical namespaces exist within `ITS` for implementations of ITS +propagation models and ITU-R Recommendations. For example, the full namespace for +the ITU-R P.2108 model implementation is `ITS.ITU.PSeries.P2108` and the full +namespace for the ITS Irregular Terrain Model is `ITS.Propagation.ITM`. + +## Top-level `README.md` + +Carefully parse through the provided template and add information related to +your project. "TODO-TEMPLATE" comments are provided to help point out important +places where template information must be replaced. + +## Other Top-level Files + +### `.clang-format` + +This file contains the ITS PropLib clang-format configuration, which should be +used to autoformat all committed C++ code. + +### `CMakePresets.json` + +This file defines various presets for CMake. This includes default release and +debug configurations for both 32- and 64-bit. An additional preset is provided +to build documentation with Doxygen without compiling the code. + +### `CONTRIBUTING.md` and `LICENSE.md` + +These files, respectively, include contribution guidelines for PropLib and the +NTIA software license. You should not need to modify these unless the basic license +is not correct for your code. Ensure the correct license is used in your repository +before publishing it. + +## The `app` Directory + +This directory should be deleted if your library does not include an accompanying +command-line driver. If your library does include a command-line driver, you will +need to add the source and header files inside this `app/` directory, update the +`app/CMakeLists.txt` file, and update the `app/README.md` document as follows. + +Populate the `app/` directory with your command-line driver's source and header files. +Some boilerplate files are provided to help in writing the driver, but these may +be discarded and replaced if desired. + +Modify `app/CMakeLists.txt` so that all of the driver source and header files are +named in the `add_executable()` function arguments. There is no need to add the +source or header files from the library itself, since this will be linked to the +driver at compile time. By default, the driver version number is set to automatically +match the library version (i.e., the one you set in the top-level `CMakeLists.txt` +project metadata). However, you can override this here to get a different version +number for the driver: + +```cmake +# In app/CMakeLists.txt +set(DRIVER_VERSION "1.5.0") +``` + +Next, proceed to develop the command line driver application. A number of `TODO-TEMPLATE` +comments are placed throughout the boilerplate code to assist, but this effort +will be largely unique for each project. Once you're done, be sure to add all your +source and header files to `app/CMakeLists.txt` and then write documentation in `app/README.md`. + +## The `docs` Directory + +This directory includes various files and a `CMakeLists.txt` which are used to +build the Doxygen documentation static website. All of these should work out-of-the-box, +without modification. However, if you wish to alter the Doxygen configuration for +your project, this can be done in `docs/CMakeLists.txt`. If you find a need to change +something in this configuration, consider whether the change is specific to your +project or if it should be made here in the template and applied across the +Propagation Library. + +The included `doxy_mainpage.md` file provides content for the home page of the +Doxygen site. The included file is written such that it is agnostic to the software +being documented, however you may choose to modify this file for your project if +you wish to provide information on the home page which is specific to your project. + +## The `extern` Directory + +This directory holds external components required by the software. Included in +this template are two Git submodules which link to external dependencies: +[doxygen-awesome-css](https://github.com/jothepro/doxygen-awesome-css) and +[googletest](https://github.com/google/googletest). The `doxygen-awesome-css` +module is used to apply custom styling to the Doxygen-generated C++ documentation. +The `googletest` module is recommended for implementing C++ unit tests. + +If your project requires additional external dependencies, it is recommended to +place them here. If it makes sense for your dependency and project, consider adding +external dependencies as Git submodules, to best integrate with the workflow design +of this template repository. + +## The `include` Directory + +This is the location for the C++ library's header files. You must create +a subdirectory in this location with your project's namespace and name. More specifically, +the subdirectory name should be exactly the same as the project name defined in +the top-level `CMakeLists.txt` file, i.e., `"${LIB_NAMESPACE}.${LIB_NAME}"`. Then, +your library's primary interface header should be named the same as the library. +For instance, the library `ITS.Propagation.ITM` will have a structure like this: + +```cmake +# In top-level CMakeLists.txt +set(LIB_NAME "ITM") # Name of library/target +set(LIB_NAMESPACE "ITS.Propagation") # Namespace for the named library +``` + +```cmd +include/ + ITS.Propagation.ITM/ + ITM.h # The primary interface header + Errors.h # Other headers also reside here + ... +``` + +### Cross-Platform "EXPORTED" + +In your library's interface header, you should use a cross-platform compatible +macro to export the functions of the shared library. It is recommended to copy +and use this snippet of code: + +```cpp +// Define cross-platform EXPORTED +#ifndef DOXYGEN_SHOULD_SKIP + #ifdef _WIN32 + #define EXPORTED extern "C" __declspec(dllexport) + #else + #define EXPORTED extern "C" + #endif +#endif +``` + +Example usage of this macro would be as follows. + +```cpp +EXPORTED int ModelEntryPoint(double someParam, double anotherParam, ...); +``` + +### Include Guards + +It is recommended to use the following preprocessor directive at the beginning of +every header file in your project to prevent them from being included multiple +times during compilation. + +```cpp +#pragma once +``` + +## The `src` Directory + +This directory contains the primary source code for the C++ library. The template +includes a `CMakeLists.txt` file in this directory, which you must modify before +being able to compile your project. In this file, you must add the names of all source +files to the `add_library()` arguments. Here is an example project source folder: + +```cmd +include/ + ITS.Propagation.SomeModel/ + SomeModel.h + OtherHeader.h +src/ + CMakeLists.txt + SomeModel.cpp + OtherSourceFile.cpp + AnotherFile.cpp +``` + +and the corresponding correctly-populated `add_library()` call in `src/CMakeLists.txt`: + +```cmake +add_library( + ${LIB_NAME} SHARED + "SomeModel.cpp" + "OtherSourceFile.cpp" + "AnotherFile.cpp" + "${PROJECT_HEADERS}/${LIB_NAME}.h" # Resolves to the SomeModel.h header location! + "${PROJECT_HEADERS}/OtherHeader.h" +) +``` + +Note the use of CMake project metadata variables which you set in the top-level +`CMakeLists.txt`. This provides a shortcut `${PROJECT_HEADERS}` for the subdirectory +you created in the `include/` directory. Note that when new source or header files +are added to the project, you must revist and update this file accordingly. + +The rest of the `src/CMakeLists.txt` file likely does not require modification, but +feel free to review the other settings applied therein and modify them based on +the needs of your project. + +### Documenting Your Code + +The Doxygen configuration used by PropLib projects requires that all source code +be documented using Doxygen comments. The recommended style is Javadoc style, and +an example of documented macros, structs, and functions are provided for reference +here. These example include some embedded $\LaTeX$ formatting, which you may consider +using if you wish to tie variables in your code to mathematical variables in supporting +texts. This can also be used to include nicely-formatted equations in your function +documentation. + +```cpp +#define a_0__meter 6370e3 /**< The actual earth radius, documented inline */ + +/******************************************************************************* + * @f$ Z_0 @f$, the speed of light in vacuum multiplied by the vacuum + * permeability. + * + * This block comment includes a brief definition of the constant as well as + * this longer description. You can use LaTeX and Markdown formatting in both! + ******************************************************************************/ +#define Z_0__ohm 376.730313667 + +/** Valid RF polarizations for use of this model */ +enum class Polarization { + NOT_SET = -1, /**< Invalid initialization value */ + HORIZONTAL = 0, /**< Horizontal polarization */ + VERTICAL = 1 /**< Vertical polarization */ +}; + +/******************************************************************************* + * Compute the free space basic transmission loss equation. + * + * @param[in] d__meter Path distance, in meters + * @param[in] f__mhz Frequency, in MHz + * @return Free space basic transmission loss, in dB + ******************************************************************************/ +double FreeSpaceLoss(const double d__meter, const double f__mhz) { + return 20.0 * (log10(f__mhz) + log10(d__meter)) - 27.5522168; +} +``` + +## The `tests` Directory + +This directory is used to hold unit test source and header files, along with any +data files required by the test suite. It is recommended to use GoogleTest to write +C++ unit tests and test fixtures. It is also recommended to put test data in a +subdirectory `tests/data`. A final recommendation is to create a single test file +for each source file in your project. An example of what this all might look like +is shown below. + +```cmd +src/ + CMakeLists.txt + SomeSource.cpp + AnotherSource.cpp +tests/ + data/ + DataForTesting.csv + CMakeLists.txt + TestSomeSource.cpp # Contains tests and fixtures related to SomeSource.cpp + TestAnotherSource.cpp + TestUtils.cpp # Contains, e.g., utility functions used by multiple unit tests + TestUtils.h # Contains, e.g., macros/includes/structs used by multiple tests +``` + +```cmake +# In tests/CMakeLists.txt +add_executable( + ${TEST_NAME} # Template sets this to "Test${LIB_NAME}" + "TestSomeSource.cpp" + "TestAnotherSource.cpp" + "TestUtils.cpp" + "TestUtils.h" + # Note: no need to include the library source files here! +) +``` + +## The `wrap` Directory + +This directory is meant to contain multi-language wrapper software for the C++ +library. The boilerplate configuration supports adding Python, MATLAB, and C#/.NET +wrappers in this directory as follows: + +```cmd +wrap/ + dotnet/ + matlab/ + python/ + CMakeLists.txt +``` + +The provided boilerplate `CMakeLists.txt` should not need to be modified unless +adding different wrappers than these three. This simple `CMakeLists.txt` file looks +for, and runs if found, `CMakeLists.txt` files in the wrapper submodule directories. +These are primarily used to copy the compiled C++ library into the location expected +by the wrapper modules, but can be used for other purposes as well. Additional +configuration related to looking for wrappers is found in the top-level +`CMakeLists.txt` file. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..add8ca7 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,34 @@ +# SOFTWARE DISCLAIMER / RELEASE + +This software was developed by employees of the National Telecommunications and Information +Administration (NTIA), an agency of the Federal Government and is provided to you +as a public service. Pursuant to Title 15 United States Code Section 105, works +of NTIA employees are not subject to copyright protection within the United States. + +The software is provided by NTIA “AS IS.” NTIA MAKES NO WARRANTY OF ANY KIND, EXPRESS, +IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT AND DATA ACCURACY. NTIA does +not warrant or make any representations regarding the use of the software or the +results thereof, including but not limited to the correctness, accuracy, reliability +or usefulness of the software. + +To the extent that NTIA holds rights in countries other than the United States, +you are hereby granted the non-exclusive irrevocable and unconditional right to +print, publish, prepare derivative works and distribute the NTIA software, in any +medium, or authorize others to do so on your behalf, on a royalty-free basis throughout +the World. + +You may improve, modify, and create derivative works of the software or any portion +of the software, and you may copy and distribute such modifications or works. Modified +works should carry a notice stating that you changed the software and should note +the date and nature of any such change. + +You are solely responsible for determining the appropriateness of using and distributing +the software and you assume all risks associated with its use, including but not +limited to the risks and costs of program errors, compliance with applicable laws, +damage to or loss of data, programs or equipment, and the unavailability or interruption +of operation. This software is not intended to be used in any situation where a failure +could cause risk of injury or damage to property. + +Please provide appropriate acknowledgments of NTIA’s creation of the software in +any copies or derivative works of this software. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0c1dfa9 --- /dev/null +++ b/README.md @@ -0,0 +1,98 @@ +# NTIA/ITS Propagation Library Template Project # + + + + + +![GitHub Release][gh-releases-badge] +![GitHub Issues][gh-issues-badge] + + + + + + +[gh-releases-badge]: https://img.shields.io/github/v/release/NTIA/TODO-TEMPLATE +[gh-issues-badge]: https://img.shields.io/github/issues/NTIA/TODO-TEMPLATE + + + + +This code repository is a template repository for software in the NTIA/ITS +Propagation Library (PropLib). This template is intended for developers wishing +to develop a cross-platform C++ library as part of PropLib. Instructions on how +to use this repository are found in [CREATING-REPOSITORIES.md](./CREATING-REPOSITORIES.md). + +## Getting Started ## + + + +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/TODO-TEMPLATE/). +There, you will find installation instructions, usage information, and code +examples for all supported languages. + +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/TODO-TEMPLATE), and a guide for contributors +[here](CONTRIBUTING.md). + +## Configure and Build ## + +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: + +```cmd +# From this repository's root directory, try one of the following command pairs: + +# "Release" configurations compile the library, build docs, and configure tests: +cmake --preset release64 +cmake --build --preset release64 + +# "Debug" configurations skip building the docs: +cmake --preset debug64 +cmake --build --preset debug64 + +# "DocsOnly" configurations only build the docs: +cmake --preset docsOnly +cmake --build --preset docsOnly +``` + +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: + +```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 +``` + +## Running Tests ## + + + +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: + +```cmd +ctest --preset release64 +``` + +## References ## + + + + +* [ITS Propagation Library Wiki](https://ntia.github.io/propagation-library-wiki) +* [`ITS.TODO-TEMPLATE.THIS-LIBRARY` C++ API Reference](https://ntia.github.io/TODO-TEMPLATE) + +## Contact ## + +For technical questions, contact . diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt new file mode 100644 index 0000000..5eb2e3d --- /dev/null +++ b/app/CMakeLists.txt @@ -0,0 +1,50 @@ +########################################### +## BUILD THE DRIVER +########################################### +set(DRIVER_NAME "${LIB_NAME}Driver") +set(DRIVER_VERSION ${PROJECT_VERSION}) # May be set manually instead +## TODO-TEMPLATE: Include source AND header files here. Do not include +## source/header files already included in `add_library()` in `src/CMakeLists.txt` +add_executable( + ${DRIVER_NAME} + "main.cpp" + "Driver.h" + "Errors.h" + "Structs.h" + "Utils.cpp" +) + +# Link the library to the executable +target_link_libraries(${DRIVER_NAME} ${LIB_NAME}) + +# Add definitions to enable version identification inside the driver +add_definitions(-DLIBRARY_VERSION="${PROJECT_VERSION}") +add_definitions(-DDRIVER_VERSION="${DRIVER_VERSION}") +add_definitions(-DLIBRARY_NAME="${LIB_NAME}") +add_definitions(-DDRIVER_NAME="${DRIVER_NAME}") + +# Set some target metadata +set_target_properties( + ${DRIVER_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( + ${DRIVER_NAME} PROPERTIES + DEBUG_POSTFIX "x86" + RELEASE_POSTFIX "x86" + ) +else () + set_target_properties( + ${DRIVER_NAME} PROPERTIES + DEBUG_POSTFIX "x64" + RELEASE_POSTFIX "x64" + ) +endif () + diff --git a/app/Driver.h b/app/Driver.h new file mode 100644 index 0000000..f8364c4 --- /dev/null +++ b/app/Driver.h @@ -0,0 +1,44 @@ +/** @file Driver.h + * Interface header for this driver executable + */ +#pragma once + +#include "Errors.h" +#include "Structs.h" + +// TODO-TEMPLATE: Include your library's main interface header +// #include "ITS./.h" + +#include // for transform +#include // for tolower +#include // for strlen +#include // for localtime, time, time_t, strftime +#include // for ifstream, ofstream +#include // for setw +#include // for cerr, cout +#include // for string, stoi, stod + +///////////////////////////// +// Macros + +// Make print-to-file statements more concise in driver +#define PRINT << std::endl << std::left << std::setw(25) << +#define SETW13 << std::setw(13) << + +// TODO-TEMPLATE: use the namespace of your library +// using namespace ITS::YourLibraryNamespace::YourLibraryName; + +///////////////////////////// +// Functions + +int ParseArguments(int argc, char **argv, DrvrParams ¶ms); +void Help(); +void Version(); +int ValidateInputs(const DrvrParams ¶ms); +int Validate_RequiredErrMsgHelper(const char *opt, int err); + +// Utils +int ParseInteger(const char *str, int &value); +int ParseDouble(const char *str, double &value); +int ParsingErrorHelper(int err, const char *msg); +std::string GetDatetimeString(); \ No newline at end of file diff --git a/app/Errors.h b/app/Errors.h new file mode 100644 index 0000000..81205f4 --- /dev/null +++ b/app/Errors.h @@ -0,0 +1,30 @@ +/** @file Errors.h + * Defines return codes for the driver + */ +#pragma once + +// TODO-TEMPLATE: If needed, include existing return codes defined by the library +// #include "/Errors.h" + +// clang-format off + +// Define "SUCCESS" macro if not imported already +#ifndef SUCCESS +#define SUCCESS 0 /**< Successful execution */ +#endif + +/** Primary Return Codes (1000-1099) */ +#define DRVR__RETURN_SUCCESS 1000 +#define DRVRERR__INVALID_OPTION 1003 +#define DRVRERR__OPENING_OUTPUT_FILE 1007 + +/** Input File Parsing Errors (1100-1199) */ +#define DRVRERR__PARSE 1100 + +// TODO-TEMPLATE: Add driver error codes and document them in app/README.md + +/** Validation Errors (1200-1299) */ +#define DRVRERR__VALIDATION_IN_FILE 1202 +#define DRVRERR__VALIDATION_OUT_FILE 1203 + +// clang-format on \ No newline at end of file diff --git a/app/README.md b/app/README.md new file mode 100644 index 0000000..e2df3a7 --- /dev/null +++ b/app/README.md @@ -0,0 +1,96 @@ +# Command-line Driver # + +This document explains the use of the included command-line driver. This is a +supplemental software tool to allow a user to call the compiled propagation library +from the command-line using text files to provide inputs and store outputs. + +## Input Files ## + +Inputs to the command-line driver are specified in an ASCII text file using +the common `key,value` format. Each line holds a single `key,value` combination, +with the `key` representing the model input variable name and the `value` representing +its value. An example is included in this repository [here](./data/in.txt). + +## Output Files ## + +After parsing the inputs and running the software, the command-line driver will +generate an output report file containing the results. This file contains a record +of the input parameters, along with the model outputs, and human-readable supporting +documentation. + +## Execution ## + +Executing the command-line driver requires specifying input arguments, defined +in the below table: + +| Flag | Type | Required | Description | +|--------|--------|----------|-----------------------------------------------------------------------| +| `-i` | string | True | File specifying model input parameters in `key,value` format | +| `-o` | string | True | Filename where output results should be written | +| `-t` | string | True | File containing comma-delimited terrain elevation data along the path | +| `-dbg` | N/A | False | If specified, intermediate values will be written to the output file | + +Additional arguments are available to print help text and version information: + +| Flag | Description | +|------|--------------------------------------------------------| +| `-h` | Display help text | +| `-v` | Display version information for the library and driver | + +Input arguments are not case sensitive and do not have to be specified in a certain +order. A generic example of calling the command-line driver on Windows is: + +```cmd +.exe -i -t -o +``` + +### Examples ### + +| Input File | Terrain File | Output File | Arguments | +|---------------------------|-----------------------------------------------------|-----------------------------|----------------------------------------------------| +| [`in.txt`](./data/in.txt) | [`terrain_profile.txt`](./data/terrain_profile.txt) | [`out.txt`](./data/out.txt) | `-i in.txt -t terrain_profile.txt -o out.txt -dbg` | + +## Command-line Driver Errors ## + +In addition to the return codes defined by the library itself, the command-line +driver implements the following return codes. + +### General Errors ### + +| Value | Const Name | Description | +|-------|--------------------------------|--------------------------------------------| +| 1000 | `DRVR__RETURN_SUCCESS` | Successful execution | +| 1003 | `DRVRERR__INVALID_OPTION` | Unknown option specified | +| 1007 | `DRVRERR__OPENING_OUTPUT_FILE` | Failed to open the output file for writing | + +### Input File Parsing Errors ### + +| Value | Const Name | Description | +|-------|-------------------------------|---------------------------------------| +| 1100 | `DRVRERR__PARSE` | General input file parsing error | +| 1101 | `DRVRERR__PARSE_HTX` | Unable to parse transmitter height | +| 1102 | `DRVRERR__PARSE_HRX` | Unable to parse receiver height | +| 1103 | `DRVRERR__PARSE_CLIMATE` | Unable to parse climate | +| 1104 | `DRVRERR__PARSE_N0` | Unable to parse refractivity | +| 1105 | `DRVRERR__PARSE_FREQ` | Unable to parse frequency | +| 1106 | `DRVRERR__PARSE_POL` | Unable to parse polarization | +| 1107 | `DRVRERR__PARSE_EPSILON` | Unable to parse relative permittivity | +| 1108 | `DRVRERR__PARSE_SIGMA` | Unable to parse conductivity | +| 1109 | `DRVRERR__PARSE_MDVAR` | Unable to parse mode of variability | +| 1110 | `DRVRERR__PARSE_TIME` | Unable to parse time | +| 1111 | `DRVRERR__PARSE_LOCATION` | Unable to parse location | +| 1112 | `DRVRERR__PARSE_SITUATION` | Unable to parse situation | +| 1113 | `DRVRERR__PARSE_TSTEP` | Unable to parse terrain step size | +| 1114 | `DRVRERR__PARSE_TERRAIN_FILE` | Unable to parse terrain data file | + +### Validation Errors ### + +Driver validation errors occur when required command line arguments are missing. +These validation errors are distinct from any defined within the library itself, +which may include, e.g., parameter out-of-range errors. + +| Value | Const Name | Description | +|-------|------------------------------------|---------------------------------------| +| 1202 | `DRVRERR__VALIDATION_IN_FILE` | Input parameter file is not specified | +| 1203 | `DRVRERR__VALIDATION_OUT_FILE` | Output file is not specified | +| 1204 | `DRVRERR__VALIDATION_TERRAIN_FILE` | Terrain file is not specified | diff --git a/app/Structs.h b/app/Structs.h new file mode 100644 index 0000000..93bdd79 --- /dev/null +++ b/app/Structs.h @@ -0,0 +1,16 @@ +/** @file Structs.h + * Contains data structures and type macros used by this software +*/ +#pragma once + +#include // For std::string + +///////////////////////////// +// Data Structures + +// TODO-TEMPLATE: Basic struct provided to hold input/output file names and DBG flag +struct DrvrParams { + std::string in_file = ""; // Input file + std::string out_file = ""; // Output file + bool DBG = false; // Dump intermediate values to file? +}; \ No newline at end of file diff --git a/app/Utils.cpp b/app/Utils.cpp new file mode 100644 index 0000000..4f50d68 --- /dev/null +++ b/app/Utils.cpp @@ -0,0 +1,73 @@ +/** @file Utils.cpp + * Implements utility functions for parsing driver inputs from text + */ +#include "Driver.h" + +/******************************************************************************* + * Parse an integer value read from the input parameter file + * + * @param[in] str Input file value as string + * @param[out] value Input file value converted to int + * @return Return code + ******************************************************************************/ +int ParseInteger(const char *str, int &value) { + size_t t; + + try { + value = std::stoi(str, &t, 10); + } catch (...) { + // error parsing the input string value + return DRVRERR__PARSE; + } + + // verify the entire string was parsed, and a trailing char wasn't omitted + if (std::strlen(str) != t) + return DRVRERR__PARSE; + + return SUCCESS; +} + +/******************************************************************************* + * Parse a double value read from the input parameter file + * + * @param[in] str Input file value as string + * @param[out] value Input file value converted to double + * @return Return code + ******************************************************************************/ +int ParseDouble(const char *str, double &value) { + try { + value = std::stod(str); + } catch (...) { + // error parsing the input string value + return DRVRERR__PARSE; + } + + return SUCCESS; +} + +/******************************************************************************* + * Common error handling function + * + * @param[in] err Error parsing code + * @param[in] msg Error message + * @return Error code from input param + ******************************************************************************/ +int ParsingErrorHelper(int err, const char *msg) { + std::cerr << "Driver Error " << err << ": Unable to parse '" << msg + << "' value." << std::endl; + return err; +} + +/****************************************************************************** + * Get a string containing the current date and time information. + * + * @return A localized standard date and time string (locale dependent) + ******************************************************************************/ +std::string GetDatetimeString() { + std::time_t now = std::time(nullptr); + char mbstr[100]; + // Warning: std::localtime may or may not be thread-safe + std::strftime(mbstr, sizeof(mbstr), "%c", std::localtime(&now)); + std::string dtString = mbstr; + return dtString; +} \ No newline at end of file diff --git a/app/main.cpp b/app/main.cpp new file mode 100644 index 0000000..604c40f --- /dev/null +++ b/app/main.cpp @@ -0,0 +1,168 @@ +/** @file main.cpp + * Implements the main function of the executable, and other high-level functions + */ +#include "Driver.h" + +/******************************************************************************* + * Main function of the driver executable + ******************************************************************************/ +int main(int argc, char **argv) { + int rtn; + DrvrParams params; + + // Parse command line arguments + rtn = ParseArguments(argc, argv, params); + if (rtn == DRVR__RETURN_SUCCESS) + return SUCCESS; + if (rtn) { + Help(); + return rtn; + } + + // validate command line inputs + rtn = ValidateInputs(params); + if (rtn) { + Help(); + return rtn; + } + + // TODO-TEMPLATE: Add driver logic, e.g. validating inputs and calling the model + + // Return driver error code if one was returned + if (rtn >= 1100) + return rtn; + + // Print results to file + std::ofstream fp; + fp.open(params.out_file.c_str()); + if (!fp.is_open()) { + std::cerr << "Error opening output file. Exiting." << std::endl; + return DRVRERR__OPENING_OUTPUT_FILE; + } + fp << std::left << std::setw(25) << "Model" << LIBRARY_NAME; + fp PRINT "Library Version" << "v" << LIBRARY_VERSION; + fp PRINT "Driver Version" << "v" << DRIVER_VERSION; + fp PRINT "Date Generated" << GetDatetimeString(); + fp PRINT "Input Arguments"; + for (int i = 1; i < argc; i++) { + fp << argv[i] << " "; + } + fp << std::endl << std::endl; + // TODO-TEMPLATE populate the rest of the output file + fp.close(); +} + +/******************************************************************************* + * Parse the command line arguments + * + * @param[in] argc Number of arguments + * @param[in] argv Command line arguments + * @param[out] params Structure with user input params + * @return Return code + ******************************************************************************/ +int ParseArguments(int argc, char **argv, DrvrParams ¶ms) { + for (int i = 1; i < argc; i++) { + std::string arg(argv[i]); + std::transform(arg.begin(), arg.end(), arg.begin(), [](const char c) { + return static_cast(std::tolower(c)); + }); + + // TODO-TEMPLATE: Specify behavior based on command line arguments. + // Template code will set in_file and out_file in DrvrParams based on -i + // and -o options. It will also set DrvrParams.DBG based on a -dbg flag + // and will call Version() or Help() respectively if -v or -h is given. + if (arg == "-i") { + params.in_file = argv[i + 1]; + i++; + } else if (arg == "-o") { + params.out_file = argv[i + 1]; + i++; + } else if (arg == "-dbg") { + params.DBG = true; + } else if (arg == "-v") { + Version(); + return DRVR__RETURN_SUCCESS; + } else if (arg == "-h") { + Help(); + return DRVR__RETURN_SUCCESS; + } else { + std::cerr << "Unknown option: " << argv[i] << std::endl; + return DRVRERR__INVALID_OPTION; + } + } + + return SUCCESS; +} + +/******************************************************************************* + * Print help instructions to the terminal + ******************************************************************************/ +void Help() { + // TODO-TEMPLATE: Update driver help message + std::cout << std::endl + << "Usage: .\\ [Options]" << std::endl; + std::cout << "Options (not case sensitive)" << std::endl; + std::cout << "\t-i :: Input file name" << std::endl; + std::cout << "\t-t :: Terrain file name" << std::endl; + std::cout << "\t-o :: Output file name" << std::endl; + std::cout << "\t-dbg :: Dump intermediate values to output file [optional]" + << std::endl; + std::cout << std::endl << "Examples:" << std::endl; + std::cout << "\t[WINDOWS] " << DRIVER_NAME + << ".exe -i inputs.txt -t terrain.txt -o results.txt" + << std::endl; + std::cout << "\t[LINUX] .\\" << DRIVER_NAME + << " -i inputs.txt -t terrain.txt -o results.txt" << std::endl; + std::cout << std::endl; +}; + +/******************************************************************************* + * Print version information to the terminal + ******************************************************************************/ +void Version() { + std::cout << std::setfill('*') << std::setw(55) << "" << std::endl; + std::cout << "Institute for Telecommunications Sciences - Boulder, CO" + << std::endl; + std::cout << "\tDriver Version: " << DRIVER_VERSION << std::endl; + std::cout << "\t" << LIBRARY_NAME << " Version: " << LIBRARY_VERSION + << std::endl; + std::cout << "Time: " << GetDatetimeString() << std::endl; + std::cout << std::setfill('*') << std::setw(55) << "" << std::endl; +} + +/******************************************************************************* + * Validate that required inputs are present for the mode specified by the user. + * + * This function DOES NOT check the validity of the parameter values, only that + * required parameters have been specified by the user + * + * @param[in] params Structure with user input parameters + * @return Return code + ******************************************************************************/ +int ValidateInputs(const DrvrParams ¶ms) { + // TODO-TEMPLATE: Check that required inputs were provided. + // This template code checks that input/output files were given with -i and -o + if (params.in_file.length() == 0) + return Validate_RequiredErrMsgHelper("-i", DRVRERR__VALIDATION_IN_FILE); + + if (params.out_file.length() == 0) + return Validate_RequiredErrMsgHelper( + "-o", DRVRERR__VALIDATION_OUT_FILE + ); + + return SUCCESS; +} + +/******************************************************************************* + * Helper function to format and print error messages encountered during + * validation of input parameters + * + * @param[in] opt Command flag in error + * @param[in] err Error code + * @return Return code + ******************************************************************************/ +int Validate_RequiredErrMsgHelper(const char *opt, int err) { + std::cerr << "Driver Error " << err << ": Option " << opt + << " is required but was not provided" << std::endl; + return err; +} 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 0000000000000000000000000000000000000000..45a7ba3fdc8a947949e4eb97376705b2779a64d4 GIT binary patch literal 14748 zcmbWeRahKBw>CPsyZhko?(T!T1$PY+T!Xv2TkzoS79hAo&_Hm9;BqE=?{A-T@jw5? znTww4?pj)FRn=4P>L^uZSrkMwp6z?webEtZYcx+Kz+8=)OFWY0tuKqJF=Mm>%-#Z=mMq& zGZXc4F*UchbO)MQTH87aQ(SiSQ2=c%gei2mmDrSABrR=h<$PQ%HGGsc&3)|6`7J0! zMSwzH0$>J?mhPrNFGmL_HvunUivQv(0B-+#%t`_LuP*NP!W93ll&+F0P}14e63ETM z!)(sZ&JN_|XJO~&v#|@Xvk7pp0srSm0hZ=!VI`n0CG$VB zz<0tFHty~&0<5f_o}Mh8oGi|+)~xLO{QRtJ9IPB1%-|l(Zr)Drre4fWZj}GwAZ6)h z?rQ7eZtLs>{KwJM%-O?Tm;$Wnf1BXwqNMb{jGf&6$5CLHv3i-hu(GqTu{t{bv+KWl zySb}d{=aJcAA7rLdb?P%s$04_d$^i|$HR*9Kg3|){oe)s>j>6HK*iM-JSe6PQqJZc zj+RdD@>0SS;5RH5wiW^wX53tCR_0dB?B-_d%xvade9U}Y?3~ONd|bSiY?kIcyzEy0 z;q!l@=a81*H0#zGlcV{;nXBVKPB#=(W*2%)z(~bV$2>rLgQkJf^ zUo0(TT%8?(|K(x<+y8@jvwtS?aNp0r6o2ei@`%Aj}c%vHu9UZ?F2y(SP~$6GEQ)c zQbS~o5pSB7`}I-mcO_DEDQoC!%JdW)(;1o}mVeh&ep>O~Pm&!X=RgKrH?;EFCxxi0 zBT7^}DsWHTbcWGjXY1PZhRmLh|8XVCVA4z8bZpx+xjUNkn(VDnvZ~E!0qy#X*xMOg zcUpzaMX}OiI$TKmijMdub3JIOJ^DROq%r<#by$NerbEpOMVBj6ci9Ps8w^Fj+JGFb zt5Gs{M6{1d;S14GB>xiy6k!r*dMp?Py(_ z$L+U0q`#blOA1z-C^`J|WWcq>!F&F@7kpT}3MM@kh#@5q5i2AWJG8TZi!(`~Cb$}& zP;>YJRMn(&l7a*8-|q01OnbQHa~s3jJIi4`P3&`AmZ43xum)E2A#bp*r-K4J@he0D z1!4$Aw3pIg-BeI8dK%EB{^6B#)~&y;ghRXvJJjD>E=QI$>RPTDD20J-L&`-r``b4_ z1@t8b6r`Ay8why?T+C4!zKn)dUwAc&ZAp_;5=AWZ_v-D%q7xT(b;b&BgL$HS z`GWW3D8Z}VJsT5qXv>8X=jsnJ2Wn)pgStBER5v7`}!`!=nDZTPhjcf#9UWUR_&+~ z8Cc%pe#c$&TqE4>_ZPR()t{)j*+bIP81i=|^1cOuCX6@1?_>|901#CY8zYq}#vL35 zPB0-XMOSRqcL5W3|0UiZ+{?}9{N88Dl=s``aq@daTM05Hr-xHH8H!YxudA+drVmCs zI&jvxpJ9o}#&c}J;R6Bx%`rw~ZIRcl zjUz%!V-+fFt^6piNNh6VTjUN8x*s zUV(72cL{JzdcTJp5{am3B@-TEhPOwYrOU)>UrjNvm^r%c<;=h-K)+v`Cqx1Nqzw(D z1p2|W%08Bo{qrMz)qwbQBV-?5P=9CQD;!T)-ysBEu-k*gQSraU9qCB#S|23@J6hx&O< zE@JJ{J_!0$-0b_K=wZS^19{q%cw-6PdfS8KNIHwL>!zS}#kGgc-&~j{eEGG|hTQ0A zMCWwx{kaSru}=X{mKXUzmREryRrrJHJfx^zrJrkkU9(K2RM1)2h!UE$kkq`&_`VgH z8S{%WI|oV-E%x-YN7f`~?Q}1~8d0|42wR{nM`^MjD-wlzC=st6+3RxA4&;V>lmLk5 z2MoV`=!K*vZ${WOOO|e}7{(H^7~4}Ete{7Vd$tH*8C&}E%B_w)Jh`+c6M%4U${2=f zF>&QR=CuO9K${&)!8&*{lO4)xCf`wrUSX>`bLqNgq`+z2@j~M^EMwacwrY?mrm(<( z(2FE{efp_nJf0XT9$U*z9u|>(t00wt*!v*lEJ%UkaS#Z@2vj}Aqrif%%pGIOfWPE* z7lst24VHpTX?jNDm5TTim@VQjAoTHiD^#}!oSe-}0Q z31MDEih?2JKB;nw)6$Bs^}+)?gvIBw#nN&aUX`|1ERsH#9YEiAZSABw$I-rbx};y* zA>#0lp|~IQYTimigoTlBw}|MbX!zx?yoe2 z`QNHH;GhdCe~|hj+u7Uq5F8}Q8e0MMS{(?Obn5{ZJrAF3oSazTq+pvO>w2G0Q2IK~ zt-S7z+p6k%5C8sD1Ddm~O+yyBBDVNokJ8=cbG|I`_Fkj#?F2!=dZWO+WyT|>77E%x z%?2x-7nX`zZ94|NcEVz3Rvh4gk}$W0vsSr23IDz0)al(;zkMuU-ZJ9c$Zm*Q1{P?V zLh1+9?f7skjKv*yO)@CPY58hTnWRr+n4Q;Ar2Xj*Di>;!XCdXYTcqQ=o#c!=6fCLc z4LHaltI7BImFr#q#Ry3*DREg3*6z^E%G&|Iu;Y59NSo=mN1q_!Anl z{plcQH=M|+ziq(Z>MSchbbF%QjbAju44o(=A$p79PVhQ)4pU-fX(r&FGmZ-GO1?OI zB{l0+$)kijc>S8BX3BHuv{XK2=kJO!Wy+Tvp7X`E2RWj)FC9kMA-E7$O1194Se2BN ze!stfzI*xRJqn>f$H!PNj2Ngj2Q&?#i4uQVv&1_u@}*u_T#PD3xAg0s@VNpt^dnVPZlx z+jrWr3}qd*B9ao*#o8C)?J)42miWu6seoG$hi%kx2mWOthZPyU4Tj%uT%k|?f3;T4 z6Kvl#8UE;KrD4j-!1b75Pss7L-_0xT?j?w7=La6A?R!o?EelX*%;hU~tzyIr-N=zR zHQu*aR=1HpPTDJm#H2eyvlGvLmFv3cWw>k$QRU&*|WZMTb^qWEcnVGa2_1zd!yE)vr@*|GR9^vNI6ophZ=o ztsRPOIAh9~X$v7pw=$}zuHW}|?mV>Vx$PJtDD_8z3 z$6@~KiQjm8Kb~+WH6n{*fT7zIHyd~pR#7<+O@VGl$lEEK92z3{9a{`l)F>i6xRko2Jm zsazqP*`WAqwC^9B_%H*VRqa0*X2ZC0+r^XcmW@#Wtu) zndgO3w3&15pZ83*y1naXL|*WPS%!>I&E$0qKf`Q%{z1~&Ra1*f>#}P;7ame{!`PjG zVySqq+v#(9_Y|{?(BsdZ=$9?B69r{E*;-s^)z+x;?#w6jJI>gBPI_bB@2DtZCs~mt zI4Z>x?Xqh-wwVg^UCd?1OoWKV&XWZs81N@eXD3xdnLY=Nc>C|^%54j)?(zs_g3$Bt zq!I(zu#JSkS989H4L8%NZ0-$-HT&;9puiYQ4#)swTrVRG#4m~nwjzbGgMFojGA!1g z#E04xzp7_TyC>~`NdLVonVTvW>{1&={uXnI1R>)(vV&i=WN5PdD$zg^F^8zF<| zjLKr+M?>5R4-XeF0p^W!Gk@x^ozI?W{e!1)Le-b3qBBk1D5e!_TEpyFk5NM!@My|+ zJ&dL3)JUdIhn;7@pmU|$kgdLKk)r&XGl5`yj`2i zKoe_bNsvSnUKFp!#+JvNzrgk_vk6btMGsHaW)aaC_xhMe+fsRO`~*X_8CMNL7n?8V zrIzoe+XY95DkN#Hij_=}!FbDUv_77x6y9_g-Y1(ASM$^-uW{L)^V5tFF*rnWGKuJN zcqbA_!SL=3|nmQgSqbNi>vC)B6CX(s-9o~#iQkot`0e9icN>=bpNrDLCVXj*|3Ju%i zXQPx1)Pcxgmj+_HU0rs3`E*O8=aX`!shc-XBfQXH3z;bWR$jbuOC_ZpE6&AoPL<1YgzCU#Ee#CUr=v60;O2m8{9*Iq3meQ z7eY7xwumyZej;iJu>Sl@T2YVU12ss>CE78zBW)Y5NS>mmY9fp3qHlK^?v)z|3MbsK z{5Jb0b=O4e=5yR`zZ}~J-d`^J76efTpmeV7TdtmN4~P68Hh(~%Gsi41Ye%Bo^-b5p z@W&vrVBCFLKs2v4ZLVzTs$nhg5}{7gl^!e zQiMY58)g-zg9pxv9}qs=-DO9-=8G*5^~rkK>~TcK?jl-@->xD=hevWPH;4NB0Yc!l zwYv}f>izmb$2W-PLq>yY5CZL}NmXudMC5(zq4x(fG3eob44k8cp8?|Z-_-S|Q9Zxb zkzY2J)6_85`@Ci4#{Cnt6bNyr`j%h@Us1v|7KxX85Z_th_$2PBk$+9-0Z=Qz4qa;2 zoiO_Agy`hdKY}E{3w7lbf4r+#TRs5!{&rS6el-IU8wd%$U^!{x*LbxQIX*ZMKzlPY3w7^SEV+(z%Z!ZM5YvMwPCDYldyK;~BGv!^}R*HR$Sp>TA$$kXoD7rqf*M zd%8Rt6EeaE{fNNr9it@s%^ZjpwJKtodK+}cbkcOU*i~up|mwMyLe9F!eDE^R{ zVP#so?b;8_e3`15qv^i<&AlJc$XKlN^OlOnl7OP)C8BA<#~0si@{vWgsyy<=-Ef)#r5U32j3e762UPVTTQyKb+VGP7~z@S2~U5IpV;CmxR~43}8;u4HFMP@Mi@IrrdsoGYeaK3WLua=EpOJ$ zDwEX6J$epa7ovKa(W+q`v1~=G3*m;m$qKhn6x>f{*yFZ#MX`J8pAAu+Lo~3r+Y5=dCnUo|vTOUegpkx6SF*Of@W~ihg@3obGE;M`Y@kh)AN&xlfx=II(G% zZB*jGzHMJEBwLKsoCP6-z+FFq99$SJ%BTSnRFy(VNSllzG_zTMN|59;&!{D0#U%cc z$RTH!C|{vGPucW}ksmHtmiE=xG9O~RM7C$bXGcWhZ}McDL&w!m2LTosU@FF>$alZH zBhQ&MN(d!||2_R>^7G)lzNQyZhIe_eo!x`2t_U&vgu%pt`lYWG!;TuFDiU!i8ac@j z_H`T^5Tam=%_01jcpMWvD2B;sic`z6w@>Q?T~gccPA>OvQaHQeMun2YkV8)DJd-)-{8X&`wVzF^=CWFR&i%rGX_VZsIVE%S*A`u|QDk z`7yypHeexQm!?)^Gl$>X)XI?i?ukTq50pe!z@A%2TDNYxA}Z$pk!WdmXKSz<+3^sC zSMo=Uf-d6AIzh0&B1miQ$tFMjFS&zhRS z&>(2eQV1_mJb*kV1hE+g;kwZH8)C@^6#iOM4zCu72uZ|NB9OJdb) zS0X~t187$&t^$63Db(6qTZvYbj6YH+u)x9T^o1+zswRt$Hx1S!hk#{TN;9pfstY27 zPJLFeF}b7-rOG_{9wtc>ZZdy_f7j+InXN}1x({`OeHMz*;5{Zk{+4r}wANIKbSFq` zH?tEzeil;ZuxJ*&SBJwb`GlEWsRZ&ztXo^0lGJ}D#``Xdh-E#C>5A>H9h(&i{CVA^ z%nnptHy$FOk?oMoJ8UIgr8}YwlKS3a5LCo0fMo_X7T%;+u=oZg5uT4!8)lgiVj589 zD%f}Dko;?)p}?C`muT~m&9k$hl zlx)6y$8+$zz~B|=H3@6(0P?EUevbVa(M z^Rx;sqfQIE%>q`$t*n3!XUTVR%-1Im5=xM_YLb{mKL=Z4==9Vt6Gi=CXJ^n5XU=+& z#MlSygu8)3`J28iH!5sXEjk6NN;)c2BvMgDRx>P2Q1+Dpwp4RX7#WX~C|=0A0I8gN z_BY}FuhM8;nWwFX)Mf2QR<*bu>Bt9%Q6)|SmjZY__#EA#C5({7oxF)GHYRX4_28+9LBkkrEmCSb4pb$d z;z-P&DrqBg!ev?#fdxzRbg{5nlDx4VamZrc3NGBrVPQ(jiJ}FfIczJ^}zY_*D zmBDG>djwHMvIyjK1_qq5udWSUq<)bju8}?g5__ywNzs~2q86r?+jNLP+FeYLm#H*< zt_`vOj>vqQYC;1^=Sc7o_eb@9pMPfe5{Htxc#{M*ZxnryiPV8_i<=DgGK8 zNigJyL~mF~*WNDX3?79tQpAvXptq0Ne>45?OB-m^i&b#ZahM?zb-A3*V?K0J16l(V zlu%g_N`xvxs_c-Bc+2xWcqH#vA#(?WSwmbR&R5_I0|cZ;%@P1X6DnyC2rNV|pwX3! zd6~(G%3_e~1=d7G?2a?%>|`YASD1X5=gOo&7o#btr!*6BVyWWcQJLiB+i@dHr9e63 z8|sIjHH{Zj=~suG{IDN0l2+sz!VF;2NRNs_8hZAoGveY&J(k+s>b2y_yoVlQLXrF{ zaFv*luir&Va>xYnK<~$*VxJUkbfb&EhrW*OXugc|S>d*Y$T{Mrc)FZ|n{yEPHbz$r zfmu*iLe$$~-s?Q~`{-oQZbV&_!GNRYB&bmXyd{>8Vn^qPWZ^YKe;&10wQpLR%QC-q zu*42yJrQ+1lj3s#VL1>Ug;D3#);BkiEdPBFldXb#HvdJAL!C&CNhg7hO%JVE1-R0= z9*lv6jQJiyKpUoR-frZ`|AtZXHSnHi$hL>XWpdSsKS-6)lZKt05`kM@x-1^xoa}3s zb=hnFRY7SqxKkinta7%wFI+MlyP^HFtRm>to!q38Wmwy*V{(JyK1*<2+DC?EvPmaz z8A>gqMqikIF^ogr1ItBq8ofT!7Q>pymVlkjj)G|d#IbnKMz2;?o2pkVt9gQ3_!qOb z+pc?2ffGLf7q=yGv6Bf336GZjI#`3VB{@E*u*fo3<0aAVIVOZ>Ek1i%iSn|&oVDoS z@gb%9#Kg`6%FnlrU^Y*|MNDsH8>K+n*VJAJF|U&9&g#VkxCygXib&58B=HL56e}8l z1tuAZ!SiykU3Vy=S!qY2M~YQb48F$W@)dukNC>OYR^e+o_5BJgTZa5;=ZLl>=zIo~ z&p4BvqaNkMoV{v$s%&o_q;KC6&#_q1tcrrP2qTrGjz`9M;HDWp~|AKhLANPP&!HVx60YnF`CBl5H%D?OCHz8h1y- zESC_PV^doiEjhV+1jrZNwNvXxsVVp3IXyE^$=D=}aB~)4Oa>fwv;HvX7jtqiD4@=V zHPoVnns7BK#G5Q5T#zTHy3M92OUP2do<~90$V(N41uHNHl10=>p-Gt&0kQC5R7IhN zquE&u4N|*?HDSdH?mQ)&X;4tkI`x)hB+C}1w+*UwHLaCG=vOUnc4^3rwc~)~)YaiwW&A3~oQm6JQEVDURUwTmg$@HRCju-k9sel4U=%W|gag5i za6?&A4eixG!C*k1bs|30;<*bg5HcxiaXH$3vBmvkQl2#U65R2HRUGuP0N zAuDL`dazdvES(m2E86a2+}>iUtM|`<)=GAYxe(byV7U{_h`c$Lr3sobP3ZJ%hGo}7Zz0*lsbyQ&8G?|&(eS{wKQ=5^yT1kwUqo7N|5=#plF+T4Y$ zNR0-zh(iB}`-OsPjr+5)j>oIl8)x#;7)JOVK6016{Wz*;J4cV4{mb@gu`nBr=6EfM z+kn203iVGK6h76O$SURzV}^F-{y|&^#k7d|lPMnnfqFlRc_A~_ZdK%d;%4c0;u=w2 zb|Iwl^mMHO$)CUdabXBaUS&xAH~F zoZi-!ke0t2wZT1>4!h*4RTbZ0+RBm$GtT2|1qw9LG3=rKFd-L%B_98IAlx&^`cw>x zj2yZ)Pj{0u6*S~HT`i%wz6yR(2-#_;Wx46#CR8%Kd9Y5Jfv8gd>~-9ApQ=#iRr&ZD zAMc%)8^ZAD9{>;g#esJ8_fW_+QnMzR7}R6VH}MMc+?j)N9)F;O3Qtat)6?q` z&ivpPp7mspZTk?>MgDcmKRB$l94GsF<$|xdtBkHR4BKq>3F#xZsG`O+zdp@z82+tO zR#aO4JxgMpV^h;Za(QMN%EZ(L@AmE4z`N(DUT|n6tQi-VWI$;|fo>IP0`s^OOBp~z zL~2d;dKr&Ao?vB6wHfe2(q-hjVEmE%!Ep@;nj*Ey zqgp#Cfv$zWF zd^+?Gyg))S2*kI|ZqQ-Knr&xAnE{r+ANU8H9CB;_0|@#=qS15)y1#UhIhXjNz%(k!$>XV7 z>_1+C&vSc!16;?cGx~G+-I{g#xh+@LZw@WiZN=k%N^0TgxkZpm{;`z77Gul&xEewm z0^<*SUS*D(bC#Y$H?V878yKsNk6{Rj%DWje38_2z0v-(i&^UO~?uS#ExLWYB@`8`f z)<@$MK0Tm=4^l4mZRJ>rAfY#QIK^I-hc3FZSO*)|YCAAL)DJ%Fce!9rhFO8h*{VW0 z8j%=F->Yl;$NP)N!)Rz@X@w@Hp}Z9YVi(T2X5e$0&L@PvkGIQ#AlJ|;O&9CeD{S$B zW{X1?HG||Ga4A6W?Tn}~ZB;|DuS#G@v-2e!eEoo85)%=>Ema-A`6#m2`_rK*xTI@6 zq1vvBfq{_`5qkq{mr%uj~_(5PNCojL?X{elp-oKLbsqgIvs88 z8~a|cF)lO05B=pzIbV(fva_>y_ES_2R-A2Yc2*R=EzHi(Lk7Oz7W@NBS&|686$A)m zbS*6{5li8+7>;?5h~Uc;iUtT}oV^jTr!gM1a7TU)y5oe@NgGtLr)uFFH@_ z)KwyiqZ?};H^wc^bixCrj=&IhP>yqN$Noe+G4vkmbUG&evxm$ir1&}6z#g3FqxkrU z?#GjVG61q%BL)Ig-2Y7ZPSSG_MVmjyoG>~nv!}CEEH(-U)%zN7Ju1XU0XcOCVPTFe zy*RTpSS-i@a%^EYf~@X^_kX2=T+YgqhLe2{HlP zxBhbCd{fqkkJ%U()_UCTKWCpy--*q=qfi!=MxqB9sb^$Xg=>wARBb)BI9%0krT9TtMd?6c>b zGEOzT_ZbydT7e1^9W{bEMaHELSg(tr4+v}j11w69_rpIVn6}2At=6vnUFf=Q`|Yzy z17&U(Jo0KTy!g?)BI}ZWI3Qe1-PriD>ibHa(a(XaP%P!0mDb{~Xd;V6i>_j%?}o0>=v@A@ zm*D|ocS~zg`sLy;Y^cBMKg>;=z1OhDV_z1(>1xJ>MNuW5JN&V?Cr|O|7nP`Rdxs z8kh2B?@)ibdjKDUIl_+Jp@2^-%?oO7hI3>EW9~QHR4!&@cU&6zgq|PB$CZJUE+()k zidK*WAWAIo>UxlBE z=W?ZjIG@jJ>T;(h(xPi%Z>hN=ISWElhbdS(k0>tOm_^RUSa0IHDH8vc3=m&^+4lFG z=6%K^q3Z{Yf>0t8M>_ubyZQNPHE%0fqmLvJC!0mD z?DMP~M-Au!K=KGGa_vm>zlr755?a^eaytN#y7l)r${I5<1p8o*fwPgZ#;_wfC)qMn zLY*S5R?ga0L??&(CN9B+NEpAFweMx~@&;cVL&%-hS|5x*qlr8M-k(?c2IpkSQ2yb+ zhgf?YECZBiK>o6HJ)r4`5_;Zklom}y5GtOs-N4Wot-fj|d9Yt6G#V=K+QNqzH*KF< zM~dt*2bFA-w6^P_s3UL~6=7hEig<^AdVOQrZT@#!4w0zduZgeslG})WGw3b8FtecM z96hqz)o)1MmA~hv3;plc;0~s84ugN-Z9)zNG3gT@K{80<6G4>nF#d$V4vNp}HUsPd z1N5)1DZPa5SIy%CRIq39#YTAyXJe_=)1oD$Yj!PXXq!~SgILs|6Cy}`#;z%B_|wMC z>z#r8*?KZhggo6Rtie~LKl0v=Vu*I&@SOUr1|LW9UyPDl?$|C#1RuI}Dg57y({k>; z-={1Srw+sru>sJ6P@La8GlB`7NdWv&z3p(_wqSjMlNA5Ti`(Z<=J3D^Kjl&3SJdAj z7){V$f~uPMy`$xz$r-Cnx)jiiWbvtR{pd0!Gs7RMjo(ggZ~T|eOcG=gEH1jYz^nXvFfRMO=OEkOs!^qy%kTH^&hrLc&?vAI$Hpu?T%NFY+JEH2 z77Wp3C0hCGaK*TY)4Ug)CUAVEYI|7n6n;6{-Ue4Dou#1p?DZd~Xv4g!TNizQCN~yE znu9yl+*Nu)dgvmukD#maF;odOy9^`aP3U}4afV02t%GRblgXqDGWh0M3s`RxP~wEj zS;OD_+4~g1D=kE5D{$2h6Gbx)pNic5m+{Bs^J<>YYZ;s@J8X~H zdCauI%A9v`2p6XE7Vh_tb<~% zmH8CR)uXYqj~Caxdp`u6OWV?FgL%*NV>G1PV=dZBtWj(EpMFQ9b}yxH3kE2XO{n_E z1?C|7B23r;61-#fxyc}_ft!OjeZl9#hTdq~6Y~7OcXxXUq*&X}GzZ8Zp5hCvMd!cT z1aF=m9<)~iyX*d2vx_v<$MqOLA6p%f$c5~?HH;B)`ukH%8gF%fEXN1n8yO>{ zC81+oQoL^O1q#s2-QeDMBiZX!YwQrBz(sHS4S7s`c-=TM#1cYDSU@((+uejir#-Rk z+t8P-2|L)Hn);K3#Tf|F_BPqP4Ug{;nyet8+=Mp#JzlSJVZpw`P(zo}Q+s=Zk`MfK zFVYwt{9LTUGjLFwm&t^tPL)gM!wtpNiM!bAk!t1RBc@zyUOs)fnh|(g9xh9%bi^Qg z)n5id>hA$j`nN>IAp0`FW-EAZ>z!N&Z4~L1Le9qDA}_hi5F{GusQT4Ga+V8j}lVeaooRcJoc8`?Fj(tKGEqG<5+-fBMv0z2#o|Yo)h3 z?jaUu32K;Mcq(;}NmiH{L0B?GBGu8+QP8)KXDQl!^~?F`^si~}f77UhWGHaD00?nb zqE$QWh~oXi6I%Tv;kvBVJtcVQ-`62`gH#a1Uy};Ae@xT5#K3&ri;{w^#VzD_A9)f- zmc99V>xN(m9Ak=C5=+@cV4!XNnmq(!jPZnl#2h$_f|{x23%AgxWQ-}JT-oQ z!m+Fr+fHkND4qn!KrI-AWdy1*IanBxv*T#D1hiIO@ZI~2;??0(MU6MVDF7F9`-O_RN*XZ;fys0F7OzOO@clT9;{ ztt%0%Le}*tdiKbKSD=^k6Q+}aOloQMIx&V!O5+zT=72GrSs`R;y_6q$k^!|kT>&cd zBff@2kr?$9T2!*7XO@+7%9*A-qN}aGnS`%lx2De(e-^@HP|laDO+3aOcw)TA<6s`tt>7LPTPX^P2Kfz44si*F@qfB6 z-&;F6k4j)k#>#|SWAFg+xo(Edfz<5)IKTk{D?ax$6TWl4Qhn=elF- zgp3b`|2}##9i>XYUF-DlBG_%G!!t4=eIwEbvV_c8EgIW=a11u<;dMXYuH$9R3V3~+ zWvkliuZfUZj;5c0PtKenrUNvn1Hz?tw?3nxfgUztpIRsveb0oSg%E_HQEksi{l$+z ckk|lP?KK}a0k=i}zL%7jR+g%hFbVm807_PkoB#j- literal 0 HcmV?d00001 diff --git a/docs/images/apple-touch-icon.png b/docs/images/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d6371ec98d7c7f24ddc8ac1570990a92336887dc GIT binary patch literal 5838 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 S^DO#(!)&Bys#~e!9R6SD2Sfz` literal 0 HcmV?d00001 diff --git a/docs/images/favicon-16x16.png b/docs/images/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..cc0687f5808893c9361d0c67cf5b5a374ae61185 GIT binary patch literal 2197 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,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") +endif() \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..f3d6465 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,32 @@ +############################################ +## CONFIGURE UNIT TESTS +############################################ +set(TEST_NAME "Test${LIB_NAME}") + +## TODO-TEMPLATE: Include source AND header files here. +## TODO-TEMPLATE: See CREATING-REPOSITORIES.md for examples. +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/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 From fc52cc4644e165aeff9ca9255f6f2c3dc1373c34 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Fri, 27 Sep 2024 16:26:46 -0400 Subject: [PATCH 051/379] Add googletest and doxygen-awesome-css submodules doxygen-awesome-css is checkout out to v2.3.3 tag --- .gitmodules | 7 +++++++ extern/doxygen-awesome-css | 1 + extern/googletest | 1 + 3 files changed, 9 insertions(+) create mode 100644 .gitmodules create mode 160000 extern/doxygen-awesome-css create mode 160000 extern/googletest diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..2e325a1 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,7 @@ +[submodule "extern/googletest"] + path = extern/googletest + url = https://github.com/google/googletest + branch = v1.12.x +[submodule "extern/doxygen-awesome-css"] + path = extern/doxygen-awesome-css + url = https://github.com/jothepro/doxygen-awesome-css diff --git a/extern/doxygen-awesome-css b/extern/doxygen-awesome-css new file mode 160000 index 0000000..40e9b25 --- /dev/null +++ b/extern/doxygen-awesome-css @@ -0,0 +1 @@ +Subproject commit 40e9b25b6174dd3b472d8868f63323a870dfeeb8 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 From 6e77c0bbe2e843d8208fe1295f7ce80a9bbf9665 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello Date: Fri, 27 Sep 2024 17:31:41 -0400 Subject: [PATCH 052/379] Add info about merging template changes to downstream --- CREATING-REPOSITORIES.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CREATING-REPOSITORIES.md b/CREATING-REPOSITORIES.md index ec35374..75e3212 100644 --- a/CREATING-REPOSITORIES.md +++ b/CREATING-REPOSITORIES.md @@ -5,6 +5,27 @@ Sketching a PropLib template repository **TODO**: Add high-level text on how to use this repository and document. Mention searchable `TODO-TEMPLATE` text throughout repository. +## Merging Template Updates into Downstream Repositories + +When changes are made at the template level, it is possible to merge them into +downstream repositories created from the template using the following steps. This +appears to be only possible from the CLI, and not from GitHub Desktop. + +```cmd +# In the downstream repository where you wish to merge the changes: + +# Add the template repository as a remote +git remote add proplib-template https://github.com/NTIA/proplib-template + +# Update the changes +git fetch --all + +# Merge the updated template branch into the current branch +git merge proplib-template/[branch to merge] --allow-unrelated-histories + +# Then resolve any merge conflicts and finish the merge commit! +``` + ## Top-Level `CMakeLists.txt` Fill in project metadata, including the namespace and name of the library. Here From 23b21f88b11ab64fa62f055064db5dfa2397812b Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 30 Sep 2024 14:38:54 -0400 Subject: [PATCH 053/379] Update doxygen-awesome-css to v2.3.4 --- extern/doxygen-awesome-css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/doxygen-awesome-css b/extern/doxygen-awesome-css index 40e9b25..568f56c 160000 --- a/extern/doxygen-awesome-css +++ b/extern/doxygen-awesome-css @@ -1 +1 @@ -Subproject commit 40e9b25b6174dd3b472d8868f63323a870dfeeb8 +Subproject commit 568f56cde6ac78b6dfcc14acd380b2e745c301ea From bdb3c2c3faa023769604707b87f3f21762a0debf Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 30 Sep 2024 14:47:02 -0400 Subject: [PATCH 054/379] Update doxygen-awesome-css to v2.3.4 --- extern/doxygen-awesome-css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/doxygen-awesome-css b/extern/doxygen-awesome-css index 40e9b25..568f56c 160000 --- a/extern/doxygen-awesome-css +++ b/extern/doxygen-awesome-css @@ -1 +1 @@ -Subproject commit 40e9b25b6174dd3b472d8868f63323a870dfeeb8 +Subproject commit 568f56cde6ac78b6dfcc14acd380b2e745c301ea From a25851979d01824eb805a528a321ce1f3ed2f373 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 8 Oct 2024 20:56:21 -0400 Subject: [PATCH 055/379] Update examples to recommend constexpr instead of macros --- CONTRIBUTING.md | 2 +- CREATING-REPOSITORIES.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ce335c0..1693ac9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -190,7 +190,7 @@ 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 */ +constexpr double = PI 3.1415; /**< Inline format, e.g. for constants or struct members */ /******************************************************************************* * This is a brief description of the function. diff --git a/CREATING-REPOSITORIES.md b/CREATING-REPOSITORIES.md index 75e3212..916491e 100644 --- a/CREATING-REPOSITORIES.md +++ b/CREATING-REPOSITORIES.md @@ -243,7 +243,7 @@ texts. This can also be used to include nicely-formatted equations in your funct documentation. ```cpp -#define a_0__meter 6370e3 /**< The actual earth radius, documented inline */ +constexpr double a_0__meter = 6370e3; /**< The actual earth radius, documented inline */ /******************************************************************************* * @f$ Z_0 @f$, the speed of light in vacuum multiplied by the vacuum @@ -252,7 +252,7 @@ documentation. * This block comment includes a brief definition of the constant as well as * this longer description. You can use LaTeX and Markdown formatting in both! ******************************************************************************/ -#define Z_0__ohm 376.730313667 +constexpr double Z_0__ohm = 376.730313667; /** Valid RF polarizations for use of this model */ enum class Polarization { From 2d8a04d87f2a9b86fa61286172027120e40c707c Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 10 Oct 2024 17:41:15 -0400 Subject: [PATCH 056/379] Delete CREATING-REPOSITORIES.md Moved contents to GitHub wiki --- CREATING-REPOSITORIES.md | 332 --------------------------------------- 1 file changed, 332 deletions(-) delete mode 100644 CREATING-REPOSITORIES.md diff --git a/CREATING-REPOSITORIES.md b/CREATING-REPOSITORIES.md deleted file mode 100644 index 916491e..0000000 --- a/CREATING-REPOSITORIES.md +++ /dev/null @@ -1,332 +0,0 @@ -# Guide to Creating a PropLib Repository from this Template Repository - -Sketching a PropLib template repository - -**TODO**: Add high-level text on how to use this repository and document. Mention -searchable `TODO-TEMPLATE` text throughout repository. - -## Merging Template Updates into Downstream Repositories - -When changes are made at the template level, it is possible to merge them into -downstream repositories created from the template using the following steps. This -appears to be only possible from the CLI, and not from GitHub Desktop. - -```cmd -# In the downstream repository where you wish to merge the changes: - -# Add the template repository as a remote -git remote add proplib-template https://github.com/NTIA/proplib-template - -# Update the changes -git fetch --all - -# Merge the updated template branch into the current branch -git merge proplib-template/[branch to merge] --allow-unrelated-histories - -# Then resolve any merge conflicts and finish the merge commit! -``` - -## Top-Level `CMakeLists.txt` - -Fill in project metadata, including the namespace and name of the library. Here -is a complete example of the populated fields for the Irregular Terrain Model (ITM): - -```cmake -set(LIB_NAME "ITM") # Name of library/target -set(LIB_NAMESPACE "ITS.Propagation") # Namespace for the named library -project( - "${LIB_NAMESPACE}.${LIB_NAME}" - VERSION 1.0.0 - DESCRIPTION "The ITS Irregular Terrain Model" - HOMEPAGE_URL "https://ntia.github.io/propagation-library-wiki/models/ITM" - LANGUAGES "CXX" -) -``` - -### Notes on Namespaces - -All ITS Propagation Library software should exist within the `ITS` namespace. -Further, hierarchical namespaces exist within `ITS` for implementations of ITS -propagation models and ITU-R Recommendations. For example, the full namespace for -the ITU-R P.2108 model implementation is `ITS.ITU.PSeries.P2108` and the full -namespace for the ITS Irregular Terrain Model is `ITS.Propagation.ITM`. - -## Top-level `README.md` - -Carefully parse through the provided template and add information related to -your project. "TODO-TEMPLATE" comments are provided to help point out important -places where template information must be replaced. - -## Other Top-level Files - -### `.clang-format` - -This file contains the ITS PropLib clang-format configuration, which should be -used to autoformat all committed C++ code. - -### `CMakePresets.json` - -This file defines various presets for CMake. This includes default release and -debug configurations for both 32- and 64-bit. An additional preset is provided -to build documentation with Doxygen without compiling the code. - -### `CONTRIBUTING.md` and `LICENSE.md` - -These files, respectively, include contribution guidelines for PropLib and the -NTIA software license. You should not need to modify these unless the basic license -is not correct for your code. Ensure the correct license is used in your repository -before publishing it. - -## The `app` Directory - -This directory should be deleted if your library does not include an accompanying -command-line driver. If your library does include a command-line driver, you will -need to add the source and header files inside this `app/` directory, update the -`app/CMakeLists.txt` file, and update the `app/README.md` document as follows. - -Populate the `app/` directory with your command-line driver's source and header files. -Some boilerplate files are provided to help in writing the driver, but these may -be discarded and replaced if desired. - -Modify `app/CMakeLists.txt` so that all of the driver source and header files are -named in the `add_executable()` function arguments. There is no need to add the -source or header files from the library itself, since this will be linked to the -driver at compile time. By default, the driver version number is set to automatically -match the library version (i.e., the one you set in the top-level `CMakeLists.txt` -project metadata). However, you can override this here to get a different version -number for the driver: - -```cmake -# In app/CMakeLists.txt -set(DRIVER_VERSION "1.5.0") -``` - -Next, proceed to develop the command line driver application. A number of `TODO-TEMPLATE` -comments are placed throughout the boilerplate code to assist, but this effort -will be largely unique for each project. Once you're done, be sure to add all your -source and header files to `app/CMakeLists.txt` and then write documentation in `app/README.md`. - -## The `docs` Directory - -This directory includes various files and a `CMakeLists.txt` which are used to -build the Doxygen documentation static website. All of these should work out-of-the-box, -without modification. However, if you wish to alter the Doxygen configuration for -your project, this can be done in `docs/CMakeLists.txt`. If you find a need to change -something in this configuration, consider whether the change is specific to your -project or if it should be made here in the template and applied across the -Propagation Library. - -The included `doxy_mainpage.md` file provides content for the home page of the -Doxygen site. The included file is written such that it is agnostic to the software -being documented, however you may choose to modify this file for your project if -you wish to provide information on the home page which is specific to your project. - -## The `extern` Directory - -This directory holds external components required by the software. Included in -this template are two Git submodules which link to external dependencies: -[doxygen-awesome-css](https://github.com/jothepro/doxygen-awesome-css) and -[googletest](https://github.com/google/googletest). The `doxygen-awesome-css` -module is used to apply custom styling to the Doxygen-generated C++ documentation. -The `googletest` module is recommended for implementing C++ unit tests. - -If your project requires additional external dependencies, it is recommended to -place them here. If it makes sense for your dependency and project, consider adding -external dependencies as Git submodules, to best integrate with the workflow design -of this template repository. - -## The `include` Directory - -This is the location for the C++ library's header files. You must create -a subdirectory in this location with your project's namespace and name. More specifically, -the subdirectory name should be exactly the same as the project name defined in -the top-level `CMakeLists.txt` file, i.e., `"${LIB_NAMESPACE}.${LIB_NAME}"`. Then, -your library's primary interface header should be named the same as the library. -For instance, the library `ITS.Propagation.ITM` will have a structure like this: - -```cmake -# In top-level CMakeLists.txt -set(LIB_NAME "ITM") # Name of library/target -set(LIB_NAMESPACE "ITS.Propagation") # Namespace for the named library -``` - -```cmd -include/ - ITS.Propagation.ITM/ - ITM.h # The primary interface header - Errors.h # Other headers also reside here - ... -``` - -### Cross-Platform "EXPORTED" - -In your library's interface header, you should use a cross-platform compatible -macro to export the functions of the shared library. It is recommended to copy -and use this snippet of code: - -```cpp -// Define cross-platform EXPORTED -#ifndef DOXYGEN_SHOULD_SKIP - #ifdef _WIN32 - #define EXPORTED extern "C" __declspec(dllexport) - #else - #define EXPORTED extern "C" - #endif -#endif -``` - -Example usage of this macro would be as follows. - -```cpp -EXPORTED int ModelEntryPoint(double someParam, double anotherParam, ...); -``` - -### Include Guards - -It is recommended to use the following preprocessor directive at the beginning of -every header file in your project to prevent them from being included multiple -times during compilation. - -```cpp -#pragma once -``` - -## The `src` Directory - -This directory contains the primary source code for the C++ library. The template -includes a `CMakeLists.txt` file in this directory, which you must modify before -being able to compile your project. In this file, you must add the names of all source -files to the `add_library()` arguments. Here is an example project source folder: - -```cmd -include/ - ITS.Propagation.SomeModel/ - SomeModel.h - OtherHeader.h -src/ - CMakeLists.txt - SomeModel.cpp - OtherSourceFile.cpp - AnotherFile.cpp -``` - -and the corresponding correctly-populated `add_library()` call in `src/CMakeLists.txt`: - -```cmake -add_library( - ${LIB_NAME} SHARED - "SomeModel.cpp" - "OtherSourceFile.cpp" - "AnotherFile.cpp" - "${PROJECT_HEADERS}/${LIB_NAME}.h" # Resolves to the SomeModel.h header location! - "${PROJECT_HEADERS}/OtherHeader.h" -) -``` - -Note the use of CMake project metadata variables which you set in the top-level -`CMakeLists.txt`. This provides a shortcut `${PROJECT_HEADERS}` for the subdirectory -you created in the `include/` directory. Note that when new source or header files -are added to the project, you must revist and update this file accordingly. - -The rest of the `src/CMakeLists.txt` file likely does not require modification, but -feel free to review the other settings applied therein and modify them based on -the needs of your project. - -### Documenting Your Code - -The Doxygen configuration used by PropLib projects requires that all source code -be documented using Doxygen comments. The recommended style is Javadoc style, and -an example of documented macros, structs, and functions are provided for reference -here. These example include some embedded $\LaTeX$ formatting, which you may consider -using if you wish to tie variables in your code to mathematical variables in supporting -texts. This can also be used to include nicely-formatted equations in your function -documentation. - -```cpp -constexpr double a_0__meter = 6370e3; /**< The actual earth radius, documented inline */ - -/******************************************************************************* - * @f$ Z_0 @f$, the speed of light in vacuum multiplied by the vacuum - * permeability. - * - * This block comment includes a brief definition of the constant as well as - * this longer description. You can use LaTeX and Markdown formatting in both! - ******************************************************************************/ -constexpr double Z_0__ohm = 376.730313667; - -/** Valid RF polarizations for use of this model */ -enum class Polarization { - NOT_SET = -1, /**< Invalid initialization value */ - HORIZONTAL = 0, /**< Horizontal polarization */ - VERTICAL = 1 /**< Vertical polarization */ -}; - -/******************************************************************************* - * Compute the free space basic transmission loss equation. - * - * @param[in] d__meter Path distance, in meters - * @param[in] f__mhz Frequency, in MHz - * @return Free space basic transmission loss, in dB - ******************************************************************************/ -double FreeSpaceLoss(const double d__meter, const double f__mhz) { - return 20.0 * (log10(f__mhz) + log10(d__meter)) - 27.5522168; -} -``` - -## The `tests` Directory - -This directory is used to hold unit test source and header files, along with any -data files required by the test suite. It is recommended to use GoogleTest to write -C++ unit tests and test fixtures. It is also recommended to put test data in a -subdirectory `tests/data`. A final recommendation is to create a single test file -for each source file in your project. An example of what this all might look like -is shown below. - -```cmd -src/ - CMakeLists.txt - SomeSource.cpp - AnotherSource.cpp -tests/ - data/ - DataForTesting.csv - CMakeLists.txt - TestSomeSource.cpp # Contains tests and fixtures related to SomeSource.cpp - TestAnotherSource.cpp - TestUtils.cpp # Contains, e.g., utility functions used by multiple unit tests - TestUtils.h # Contains, e.g., macros/includes/structs used by multiple tests -``` - -```cmake -# In tests/CMakeLists.txt -add_executable( - ${TEST_NAME} # Template sets this to "Test${LIB_NAME}" - "TestSomeSource.cpp" - "TestAnotherSource.cpp" - "TestUtils.cpp" - "TestUtils.h" - # Note: no need to include the library source files here! -) -``` - -## The `wrap` Directory - -This directory is meant to contain multi-language wrapper software for the C++ -library. The boilerplate configuration supports adding Python, MATLAB, and C#/.NET -wrappers in this directory as follows: - -```cmd -wrap/ - dotnet/ - matlab/ - python/ - CMakeLists.txt -``` - -The provided boilerplate `CMakeLists.txt` should not need to be modified unless -adding different wrappers than these three. This simple `CMakeLists.txt` file looks -for, and runs if found, `CMakeLists.txt` files in the wrapper submodule directories. -These are primarily used to copy the compiled C++ library into the location expected -by the wrapper modules, but can be used for other purposes as well. Additional -configuration related to looking for wrappers is found in the top-level -`CMakeLists.txt` file. From f1f34aa31fe011e2f6c3e7e8511a0d29f12ca5b7 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 11 Oct 2024 13:58:50 -0400 Subject: [PATCH 057/379] Add CLI app to cmake configuration --- CMakeLists.txt | 6 +++++- CMakePresets.json | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dd97d52..61eca21 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,8 +29,9 @@ 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 +# Define options. Defaults to: compile 64-bit library and driver, build docs, run tests option(BUILD_DOCS "Generate documentation with Doxygen" ON) +option(BUILD_DRIVER "Build driver executable" 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) @@ -63,6 +64,9 @@ if (NOT DOCS_ONLY) ) endif() endif () + if (BUILD_DRIVER) + add_subdirectory(app) + endif () endif () # Generate documentation diff --git a/CMakePresets.json b/CMakePresets.json index 8ba9db6..1a9525e 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -25,7 +25,8 @@ "description": "Base 'Release' configuration preset for ITS PropLib libraries", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release", - "BUILD_DOCS": "ON" + "BUILD_DOCS": "ON", + "BUILD_DRIVER": "ON" } }, { @@ -35,7 +36,8 @@ "description": "Base 'Debug' configuration preset for ITS PropLib libraries", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", - "BUILD_DOCS": "OFF" + "BUILD_DOCS": "OFF", + "BUILD_DRIVER": "OFF" } }, { From 674e8082bcc5dd2afade68993fd6c226d714c0b9 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 11 Oct 2024 13:59:32 -0400 Subject: [PATCH 058/379] Update CLI app readme for templating --- app/README.md | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/app/README.md b/app/README.md index e2df3a7..921a392 100644 --- a/app/README.md +++ b/app/README.md @@ -10,6 +10,7 @@ Inputs to the command-line driver are specified in an ASCII text file using the common `key,value` format. Each line holds a single `key,value` combination, with the `key` representing the model input variable name and the `value` representing its value. An example is included in this repository [here](./data/in.txt). +(TODO-TEMPLATE update the above example data info/link) ## Output Files ## @@ -23,6 +24,7 @@ documentation. Executing the command-line driver requires specifying input arguments, defined in the below table: +(TODO-TEMPLATE review and update the flags below) | Flag | Type | Required | Description | |--------|--------|----------|-----------------------------------------------------------------------| | `-i` | string | True | File specifying model input parameters in `key,value` format | @@ -40,18 +42,21 @@ Additional arguments are available to print help text and version information: Input arguments are not case sensitive and do not have to be specified in a certain order. A generic example of calling the command-line driver on Windows is: +(TODO-TEMPLATE: update example driver command below) ```cmd .exe -i -t -o ``` ### Examples ### +(TODO-TEMPLATE: Provide all included examples in the table below) | Input File | Terrain File | Output File | Arguments | |---------------------------|-----------------------------------------------------|-----------------------------|----------------------------------------------------| | [`in.txt`](./data/in.txt) | [`terrain_profile.txt`](./data/terrain_profile.txt) | [`out.txt`](./data/out.txt) | `-i in.txt -t terrain_profile.txt -o out.txt -dbg` | ## Command-line Driver Errors ## +(TODO-TEMPLATE: Update the below tables with your driver errors) In addition to the return codes defined by the library itself, the command-line driver implements the following return codes. @@ -69,19 +74,7 @@ driver implements the following return codes. |-------|-------------------------------|---------------------------------------| | 1100 | `DRVRERR__PARSE` | General input file parsing error | | 1101 | `DRVRERR__PARSE_HTX` | Unable to parse transmitter height | -| 1102 | `DRVRERR__PARSE_HRX` | Unable to parse receiver height | -| 1103 | `DRVRERR__PARSE_CLIMATE` | Unable to parse climate | -| 1104 | `DRVRERR__PARSE_N0` | Unable to parse refractivity | -| 1105 | `DRVRERR__PARSE_FREQ` | Unable to parse frequency | -| 1106 | `DRVRERR__PARSE_POL` | Unable to parse polarization | -| 1107 | `DRVRERR__PARSE_EPSILON` | Unable to parse relative permittivity | -| 1108 | `DRVRERR__PARSE_SIGMA` | Unable to parse conductivity | -| 1109 | `DRVRERR__PARSE_MDVAR` | Unable to parse mode of variability | -| 1110 | `DRVRERR__PARSE_TIME` | Unable to parse time | -| 1111 | `DRVRERR__PARSE_LOCATION` | Unable to parse location | -| 1112 | `DRVRERR__PARSE_SITUATION` | Unable to parse situation | -| 1113 | `DRVRERR__PARSE_TSTEP` | Unable to parse terrain step size | -| 1114 | `DRVRERR__PARSE_TERRAIN_FILE` | Unable to parse terrain data file | + ### Validation Errors ### @@ -93,4 +86,3 @@ which may include, e.g., parameter out-of-range errors. |-------|------------------------------------|---------------------------------------| | 1202 | `DRVRERR__VALIDATION_IN_FILE` | Input parameter file is not specified | | 1203 | `DRVRERR__VALIDATION_OUT_FILE` | Output file is not specified | -| 1204 | `DRVRERR__VALIDATION_TERRAIN_FILE` | Terrain file is not specified | From 04b396aa653bd767ca609ab90da26479d61204b2 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 11 Oct 2024 13:59:57 -0400 Subject: [PATCH 059/379] Initial add CLI app --- app/AeronauticalStatisticalModel.cpp | 95 +++++++++++++++++ app/CMakeLists.txt | 10 +- app/Driver.h | 12 ++- app/Errors.h | 11 +- app/HeightGainTerminalCorrectionModel.cpp | 121 ++++++++++++++++++++++ app/Labels.h | 25 +++++ app/README.md | 12 ++- app/Reporting.cpp | 48 +++++++++ app/Structs.h | 21 +++- app/Tags.h | 25 +++++ app/TerrestrialStatisticalModel.cpp | 97 +++++++++++++++++ app/data/i_asm.txt | 3 + app/data/i_hgtcm.txt | 5 + app/data/i_tsm.txt | 3 + 14 files changed, 471 insertions(+), 17 deletions(-) create mode 100644 app/AeronauticalStatisticalModel.cpp create mode 100644 app/HeightGainTerminalCorrectionModel.cpp create mode 100644 app/Labels.h create mode 100644 app/Reporting.cpp create mode 100644 app/Tags.h create mode 100644 app/TerrestrialStatisticalModel.cpp create mode 100644 app/data/i_asm.txt create mode 100644 app/data/i_hgtcm.txt create mode 100644 app/data/i_tsm.txt diff --git a/app/AeronauticalStatisticalModel.cpp b/app/AeronauticalStatisticalModel.cpp new file mode 100644 index 0000000..206a27c --- /dev/null +++ b/app/AeronauticalStatisticalModel.cpp @@ -0,0 +1,95 @@ +/** @file AeronauticalStatisticalModel.cpp + * Implements top-level functions for running the Aeronautical Statistical Model. + */ +#include "Driver.h" +#include "Tags.h" + + +/******************************************************************************* + * Top-level control function for Aeronautical Statistical Model operation + * + * @param[in] params Driver input parameter struct + * @param[out] asm_params Aeronautical Statistical Model input parameter struct + * @param[out] L_ces__db Basic transmission loss, in dB + * @return Return code + ******************************************************************************/ +int CallAeronauticalStatisticalModel( + const DrvrParams ¶ms, + ASMParams &asm_params, + std::vector &L_ces__db +) { + // Parse input file and populate asm_params struct + int rtn = ParseASMInputFile(params.in_file, asm_params); + if (rtn != SUCCESS) { + return rtn; + } + + double L_ces; + rtn = AeronauticalStatisticalModel( + asm_params.f__ghz, asm_params.theta__deg, asm_params.p, L_ces + ); + L_ces__db.push_back(L_ces); + + return rtn; +} + +/******************************************************************************* + * Parse Aeronautical Statistical Model input parameter file + * + * @param[in] in_file Path to ASM input parameter file + * @param[out] asm_params ASM input parameter struct + * @return Return code + ******************************************************************************/ +int ParseASMInputFile(const std::string &in_file, ASMParams &asm_params) { + std::ifstream file; + file.open(in_file.c_str()); + std::string line; + + while (std::getline(file, line)) { + size_t i = line.find(","); + + std::string key = line.substr(0, i); + std::string value = line.substr(i + 1); + + std::transform(key.begin(), key.end(), key.begin(), [](const char c) { + return static_cast(std::tolower(c)); + }); + + if (key.compare(TAG__FREQ) == 0) { + if (ParseDouble(value.c_str(), asm_params.f__ghz) + == DRVRERR__PARSE) { + return ParsingErrorHelper(DRVRERR__PARSE_FREQ, TAG__FREQ); + } + } else if (key.compare(TAG__THETA) == 0) { + if (ParseDouble(value.c_str(), asm_params.theta__deg) + == DRVRERR__PARSE) { + return ParsingErrorHelper(DRVRERR__PARSE_THETA, TAG__THETA); + } + } else if (key.compare(TAG__PERCENTAGE) == 0) { + if (ParseDouble(value.c_str(), asm_params.p) == DRVRERR__PARSE) { + return ParsingErrorHelper( + DRVRERR__PARSE_PERCENTAGE, TAG__PERCENTAGE + ); + } + } else { + std::cerr << "Driver Error " << DRVRERR__PARSE; + std::cerr << ": Unknown input parameter '" << key << "'" + << std::endl; + return DRVRERR__PARSE; + } + } + file.close(); + return SUCCESS; +} + +/******************************************************************************* + * Write Aeronautical Statistical Model inputs to the report file + * + * @param[in] fp Output stream, a text file open for writing + * @param[in] asm_params ASM input parameter struct + ******************************************************************************/ +void WriteASMInputs(std::ofstream &fp, const ASMParams ¶ms) { + fp PRINT TAG__FREQ SETW13 params.f__ghz << UNITS__GHZ; + fp PRINT TAG__THETA SETW13 params.theta__deg << UNITS__DEGREES; + fp PRINT TAG__PERCENTAGE SETW13 params.p << UNITS__PERCENT; +} \ No newline at end of file diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 5eb2e3d..c83c2c5 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -3,14 +3,18 @@ ########################################### set(DRIVER_NAME "${LIB_NAME}Driver") set(DRIVER_VERSION ${PROJECT_VERSION}) # May be set manually instead -## TODO-TEMPLATE: Include source AND header files here. Do not include -## source/header files already included in `add_library()` in `src/CMakeLists.txt` add_executable( ${DRIVER_NAME} - "main.cpp" + "AeronauticalStatisticalModel.cpp" "Driver.h" "Errors.h" + "HeightGainTerminalCorrectionModel.cpp" + "Labels.h" + "main.cpp" + "Reporting.cpp" "Structs.h" + "Tags.h" + "TerrestrialStatisticalModel.cpp" "Utils.cpp" ) diff --git a/app/Driver.h b/app/Driver.h index f8364c4..602686f 100644 --- a/app/Driver.h +++ b/app/Driver.h @@ -4,11 +4,9 @@ #pragma once #include "Errors.h" +#include "ITS.ITU.PSeries.P2108/P2108.h" #include "Structs.h" -// TODO-TEMPLATE: Include your library's main interface header -// #include "ITS./.h" - #include // for transform #include // for tolower #include // for strlen @@ -17,6 +15,7 @@ #include // for setw #include // for cerr, cout #include // for string, stoi, stod +#include // for vector ///////////////////////////// // Macros @@ -25,8 +24,7 @@ #define PRINT << std::endl << std::left << std::setw(25) << #define SETW13 << std::setw(13) << -// TODO-TEMPLATE: use the namespace of your library -// using namespace ITS::YourLibraryNamespace::YourLibraryName; +using namespace ITS::ITU::PSeries::P2108; ///////////////////////////// // Functions @@ -37,6 +35,10 @@ void Version(); int ValidateInputs(const DrvrParams ¶ms); int Validate_RequiredErrMsgHelper(const char *opt, int err); +// Reporting +void PrintClutterTypeLabel(std::ofstream &fp, const ClutterType clutter_type); +void PrintLabel(std::ofstream &fp, const char *lbl); + // Utils int ParseInteger(const char *str, int &value); int ParseDouble(const char *str, double &value); diff --git a/app/Errors.h b/app/Errors.h index 81205f4..1e976fe 100644 --- a/app/Errors.h +++ b/app/Errors.h @@ -3,9 +3,6 @@ */ #pragma once -// TODO-TEMPLATE: If needed, include existing return codes defined by the library -// #include "/Errors.h" - // clang-format off // Define "SUCCESS" macro if not imported already @@ -20,6 +17,14 @@ /** Input File Parsing Errors (1100-1199) */ #define DRVRERR__PARSE 1100 +#define DRVRERR__PARSE_FREQ 1101 +#define DRVRERR__PARSE_THETA 1102 +#define DRVRERR__PARSE_PERCENTAGE 1103 +#define DRVRERR__PARSE_HEIGHT 1104 +#define DRVRERR__PARSE_STREET_WIDTH 1105 +#define DRVRERR__PARSE_REPR_HEIGHT 1106 +#define DRVRERR__PARSE_CLUTTER_TYPE 1107 +#define DRVRERR__PARSE_PATH_DIST 1108 // TODO-TEMPLATE: Add driver error codes and document them in app/README.md diff --git a/app/HeightGainTerminalCorrectionModel.cpp b/app/HeightGainTerminalCorrectionModel.cpp new file mode 100644 index 0000000..b0dc1b9 --- /dev/null +++ b/app/HeightGainTerminalCorrectionModel.cpp @@ -0,0 +1,121 @@ +/** @file HeightGainTerminalCorrectionModel.cpp + * Implements functions for running the Height Gain Terminal Correction Model. + */ +#include "Driver.h" +#include "Tags.h" + + +/******************************************************************************* + * Top-level control function for Height Gain Terminal Correction Model + * + * @param[in] params Driver input parameter struct + * @param[out] hgtc_params Height Gain Terminal Correction Model input struct + * @param[out] A_h__db Additional loss (clutter loss), in dB + * @return Return code + ******************************************************************************/ +int CallAeronauticalStatisticalModel( + const DrvrParams ¶ms, + HGTCParams &hgtc_params, + std::vector &A_h__db +) { + // Parse input file and populate hgtc_params struct + int rtn = ParseHGTCInputFile(params.in_file, hgtc_params); + if (rtn != SUCCESS) { + return rtn; + } + + double A_h; + rtn = HeightGainTerminalCorrectionModel( + hgtc_params.f__ghz, + hgtc_params.h__meter, + hgtc_params.w_s__meter, + hgtc_params.R__meter, + hgtc_params.clutter_type, + A_h + ); + A_h__db.push_back(A_h); + + return rtn; +} + +/******************************************************************************* + * Parse Height Gain Terminal Correction Model input parameter file + * + * @param[in] in_file Path to HGTC input parameter file + * @param[out] hgtc_params HGTC input parameter struct + * @return Return code + ******************************************************************************/ +int ParseHGTCInputFile(const std::string &in_file, HGTCParams &hgtc_params) { + std::ifstream file; + file.open(in_file.c_str()); + std::string line; + + while (std::getline(file, line)) { + size_t i = line.find(","); + + std::string key = line.substr(0, i); + std::string value = line.substr(i + 1); + + std::transform(key.begin(), key.end(), key.begin(), [](const char c) { + return static_cast(std::tolower(c)); + }); + + if (key.compare(TAG__FREQ) == 0) { + if (ParseDouble(value.c_str(), hgtc_params.f__ghz) + == DRVRERR__PARSE) { + return ParsingErrorHelper(DRVRERR__PARSE_FREQ, TAG__FREQ); + } + } else if (key.compare(TAG__HEIGHT) == 0) { + if (ParseDouble(value.c_str(), hgtc_params.h__meter) + == DRVRERR__PARSE) { + return ParsingErrorHelper(DRVRERR__PARSE_HEIGHT, TAG__HEIGHT); + } + } else if (key.compare(TAG__STREET_WIDTH) == 0) { + if (ParseDouble(value.c_str(), hgtc_params.w_s__meter) + == DRVRERR__PARSE) { + return ParsingErrorHelper( + DRVRERR__PARSE_STREET_WIDTH, TAG__STREET_WIDTH + ); + } + } else if (key.compare(TAG__REPR_HEIGHT) == 0) { + if (ParseDouble(value.c_str(), hgtc_params.R__meter) + == DRVRERR__PARSE) { + return ParsingErrorHelper( + DRVRERR__PARSE_REPR_HEIGHT, TAG__REPR_HEIGHT + ); + } + } else if (key.compare(TAG__CLUTTER_TYPE) == 0) { + int clutter_type_int; + if (ParseInteger(value.c_str(), clutter_type_int) + == DRVRERR__PARSE) { + return ParsingErrorHelper( + DRVRERR__PARSE_CLUTTER_TYPE, TAG__CLUTTER_TYPE + ); + } + hgtc_params.clutter_type + = static_cast(clutter_type_int); + } else { + std::cerr << "Driver Error " << DRVRERR__PARSE; + std::cerr << ": Unknown input parameter '" << key << "'" + << std::endl; + return DRVRERR__PARSE; + } + } + file.close(); + return SUCCESS; +} + +/******************************************************************************* + * Write Height Gain Terminal Correction Model inputs to the report file + * + * @param[in] fp Output stream, a text file open for writing + * @param[in] hgtc_params HGTC input parameter struct + ******************************************************************************/ +void WriteASMInputs(std::ofstream &fp, const HGTCParams ¶ms) { + fp PRINT TAG__FREQ SETW13 params.f__ghz << UNITS__GHZ; + fp PRINT TAG__HEIGHT SETW13 params.h__meter << UNITS__METER; + fp PRINT TAG__STREET_WIDTH SETW13 params.w_s__meter << UNITS__METER; + fp PRINT TAG__REPR_HEIGHT SETW13 params.R__meter << UNITS__METER; + fp PRINT TAG__CLUTTER_TYPE SETW13 static_cast(params.clutter_type); + PrintClutterTypeLabel(fp, params.clutter_type); +} diff --git a/app/Labels.h b/app/Labels.h new file mode 100644 index 0000000..39e2569 --- /dev/null +++ b/app/Labels.h @@ -0,0 +1,25 @@ +/** @file Labels.h + * Defines text labels for errors, warnings, and enums + */ +#pragma once + +// clang-format off + +/** Labels */ +#define LBL__SUCCESS "Success - No Errors" +#define LBL__SUCCESS_WITH_WARNINGS "Success - but with warnings" +#define LBL__NO_WARNINGS "No Warnings" + +/** Height Gain Terminal Correction Clutter Types */ +#define LBL__CLUTTERTYPE_WATER_SEA "Water/sea clutter type" +#define LBL__CLUTTERTYPE_OPEN_RURAL "Open/rural clutter type" +#define LBL__CLUTTERTYPE_SUBURBAN "Suburban clutter type" +#define LBL__CLUTTERTYPE_URBAN "Urban clutter type" +#define LBL__CLUTTERTYPE_TREES_FOREST "Trees/forest clutter type" +#define LBL__CLUTTERTYPE_DENSE_URBAN "Dense urban clutter type" + +/** Error Definitions */ +#define LBL__ERROR_INVALID_VALUE "Invalid Value" + + +// clang-format on \ No newline at end of file diff --git a/app/README.md b/app/README.md index e2df3a7..5b7fa33 100644 --- a/app/README.md +++ b/app/README.md @@ -1,4 +1,4 @@ -# Command-line Driver # +# Command-line Driver # TODO This document explains the use of the included command-line driver. This is a supplemental software tool to allow a user to call the compiled propagation library @@ -41,14 +41,16 @@ Input arguments are not case sensitive and do not have to be specified in a cert order. A generic example of calling the command-line driver on Windows is: ```cmd -.exe -i -t -o +.exe -i -o ``` ### Examples ### -| Input File | Terrain File | Output File | Arguments | -|---------------------------|-----------------------------------------------------|-----------------------------|----------------------------------------------------| -| [`in.txt`](./data/in.txt) | [`terrain_profile.txt`](./data/terrain_profile.txt) | [`out.txt`](./data/out.txt) | `-i in.txt -t terrain_profile.txt -o out.txt -dbg` | +| Input File | Output File | Arguments | +|-------------------------------------|-------------------------------------|---------------------------------| +| [`i_asm.txt`](./data/i_asm.txt) | [`o_asm.txt`](./data/o_asm.txt) | `-i i_asm.txt -o o_asm.txt` | +| [`i_hgtcm.txt`](./data/i_hgtcm.txt) | [`o_hgtcm.txt`](./data/o_hgtcm.txt) | `-i i_hgtcm.txt -o o_hgtcm.txt` | +| [`i_tsm.txt`](./data/i_tsm.txt) | [`o_tsm.txt`](./data/o_tsm.txt) | `-i i_tsm.txt -o o_tsm.txt` | ## Command-line Driver Errors ## diff --git a/app/Reporting.cpp b/app/Reporting.cpp new file mode 100644 index 0000000..a60e19a --- /dev/null +++ b/app/Reporting.cpp @@ -0,0 +1,48 @@ +/** @file Reporting.cpp + * Implements utility functions for printing driver results + */ +#include "Driver.h" +#include "Labels.h" +#include "Tags.h" + +/******************************************************************************* + * Print text message corresponding to clutter type enum value + * + * @param[in] fp Output stream, a text file open for writing + * @param[in] clutter_type Height Gain Terminal Correction Model clutter type + ******************************************************************************/ +void PrintClutterTypeLabel(std::ofstream &fp, const ClutterType clutter_type) { + switch (clutter_type) { + case ClutterType::WATER_SEA: + PrintLabel(fp, LBL__CLUTTERTYPE_WATER_SEA); + break; + case ClutterType::OPEN_RURAL: + PrintLabel(fp, LBL__CLUTTERTYPE_OPEN_RURAL); + break; + case ClutterType::SUBURBAN: + PrintLabel(fp, LBL__CLUTTERTYPE_SUBURBAN); + break; + case ClutterType::URBAN: + PrintLabel(fp, LBL__CLUTTERTYPE_URBAN); + break; + case ClutterType::TREES_FOREST: + PrintLabel(fp, LBL__CLUTTERTYPE_TREES_FOREST); + break; + case ClutterType::DENSE_URBAN: + PrintLabel(fp, LBL__CLUTTERTYPE_DENSE_URBAN); + break; + default: + PrintLabel(fp, LBL__ERROR_INVALID_VALUE); + break; + } +} + +/******************************************************************************* + * Helper function to standardize printing of text labels to file + * + * @param[in] fp Output stream, a text file open for writing + * @param[in] lbl Text message + ******************************************************************************/ +void PrintLabel(std::ofstream &fp, const char *lbl) { + fp << "[" << lbl << "]"; +} diff --git a/app/Structs.h b/app/Structs.h index 93bdd79..b3c6132 100644 --- a/app/Structs.h +++ b/app/Structs.h @@ -8,9 +8,28 @@ ///////////////////////////// // Data Structures -// TODO-TEMPLATE: Basic struct provided to hold input/output file names and DBG flag struct DrvrParams { std::string in_file = ""; // Input file std::string out_file = ""; // Output file bool DBG = false; // Dump intermediate values to file? +}; + +struct ASMParams { + double f__ghz; + double theta__deg; + double p; +}; + +struct HGTCParams { + double f__ghz; + double h__meter; + double w_s__meter; + double R__meter; + ITS::ITU::PSeries::P2108::ClutterType clutter_type; +}; + +struct TSMParams { + double f__ghz; + double d__km; + double p; }; \ No newline at end of file diff --git a/app/Tags.h b/app/Tags.h new file mode 100644 index 0000000..07e193e --- /dev/null +++ b/app/Tags.h @@ -0,0 +1,25 @@ +/** @file Tags.h + * Defines tags for input variables and macros for printing units + */ +#pragma once + +// clang-format off + +/** Input File Tags */ +#define TAG__FREQ "f__ghz" +#define TAG__THETA "theta__deg" +#define TAG__PERCENTAGE "p" +#define TAG__HEIGHT "h__meter" +#define TAG__STREET_WIDTH "w_s__meter" +#define TAG__REPR_HEIGHT "R__meter" +#define TAG__CLUTTER_TYPE "clutter_type" +#define TAG__PATH_DIST "d__km" + +/** Unit Labels */ +#define UNITS__GHZ "(gigahertz)" +#define UNITS__DEGREES "(degrees)" +#define UNITS__PERCENT "(%)" +#define UNITS__METER "(meters)" +#define UNITS__KM "(kilometers)" + +// clang-format on diff --git a/app/TerrestrialStatisticalModel.cpp b/app/TerrestrialStatisticalModel.cpp new file mode 100644 index 0000000..ed43e30 --- /dev/null +++ b/app/TerrestrialStatisticalModel.cpp @@ -0,0 +1,97 @@ +/** @file TerrestrialStatisticalModel.cpp + * Implements top-level functions for running the Terrestrial Statistical Model. + */ +#include "Driver.h" +#include "Tags.h" + + +/******************************************************************************* + * Top-level control function for Terrestrial Statistical Model operation + * + * @param[in] params Driver input parameter struct + * @param[out] tsm_params Terrestrial Statistical Model input parameter struct + * @param[out] L_ctt__db Additional loss (clutter loss), in dB + * @return Return code + ******************************************************************************/ +int CallTerrestrialStatisticalModel( + const DrvrParams ¶ms, + TSMParams &tsm_params, + std::vector &L_ctt__db +) { + // Parse input file and populate tsm_params struct + int rtn = ParseTSMInputFile(params.in_file, tsm_params); + if (rtn != SUCCESS) { + return rtn; + } + + double L_ctt; + rtn = TerrestrialStatisticalModel( + tsm_params.f__ghz, tsm_params.d__km, tsm_params.p, L_ctt + ); + L_ctt__db.push_back(L_ctt); + + return rtn; +} + +/******************************************************************************* + * Parse Terrestrial Statistical Model input parameter file + * + * @param[in] in_file Path to TSM input parameter file + * @param[out] tsm_params TSM input parameter struct + * @return Return code + ******************************************************************************/ +int ParseTSMInputFile(const std::string &in_file, TSMParams &tsm_params) { + std::ifstream file; + file.open(in_file.c_str()); + std::string line; + + while (std::getline(file, line)) { + size_t i = line.find(","); + + std::string key = line.substr(0, i); + std::string value = line.substr(i + 1); + + std::transform(key.begin(), key.end(), key.begin(), [](const char c) { + return static_cast(std::tolower(c)); + }); + + if (key.compare(TAG__FREQ) == 0) { + if (ParseDouble(value.c_str(), tsm_params.f__ghz) + == DRVRERR__PARSE) { + return ParsingErrorHelper(DRVRERR__PARSE_FREQ, TAG__FREQ); + } + } else if (key.compare(TAG__PATH_DIST) == 0) { + if (ParseDouble(value.c_str(), tsm_params.d__km) + == DRVRERR__PARSE) { + return ParsingErrorHelper( + DRVRERR__PARSE_PATH_DIST, TAG__PATH_DIST + ); + } + } else if (key.compare(TAG__PERCENTAGE) == 0) { + if (ParseDouble(value.c_str(), tsm_params.p) == DRVRERR__PARSE) { + return ParsingErrorHelper( + DRVRERR__PARSE_PERCENTAGE, TAG__PERCENTAGE + ); + } + } else { + std::cerr << "Driver Error " << DRVRERR__PARSE; + std::cerr << ": Unknown input parameter '" << key << "'" + << std::endl; + return DRVRERR__PARSE; + } + } + file.close(); + return SUCCESS; +} + +/******************************************************************************* + * Write Terrestrial Statistical Model inputs to the report file + * + * @param[in] fp Output stream, a text file open for writing + * @param[in] tsm_params TSM input parameter struct + ******************************************************************************/ +void WriteTSMInputs(std::ofstream &fp, const TSMParams ¶ms) { + fp PRINT TAG__FREQ SETW13 params.f__ghz << UNITS__GHZ; + fp PRINT TAG__THETA SETW13 params.d__km << UNITS__KM; + fp PRINT TAG__PERCENTAGE SETW13 params.p << UNITS__PERCENT; +} \ No newline at end of file diff --git a/app/data/i_asm.txt b/app/data/i_asm.txt new file mode 100644 index 0000000..72a72cd --- /dev/null +++ b/app/data/i_asm.txt @@ -0,0 +1,3 @@ +f__ghz,10 +theta__deg,10.5 +p,45 \ No newline at end of file diff --git a/app/data/i_hgtcm.txt b/app/data/i_hgtcm.txt new file mode 100644 index 0000000..3bfb3ea --- /dev/null +++ b/app/data/i_hgtcm.txt @@ -0,0 +1,5 @@ +f__ghz,1.5 +h__meter,2 +w_s__meter,27 +R__meter,15 +clutter_type,4 \ No newline at end of file diff --git a/app/data/i_tsm.txt b/app/data/i_tsm.txt new file mode 100644 index 0000000..d8dbf87 --- /dev/null +++ b/app/data/i_tsm.txt @@ -0,0 +1,3 @@ +f__ghz,26.6 +d__km,15.8 +p,45 \ No newline at end of file From ce84c796fedae4c7841b8c69c9f8fb74dbfb560f Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 11 Oct 2024 14:03:08 -0400 Subject: [PATCH 060/379] Fix function names --- app/HeightGainTerminalCorrectionModel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/HeightGainTerminalCorrectionModel.cpp b/app/HeightGainTerminalCorrectionModel.cpp index b0dc1b9..6f2bda7 100644 --- a/app/HeightGainTerminalCorrectionModel.cpp +++ b/app/HeightGainTerminalCorrectionModel.cpp @@ -13,7 +13,7 @@ * @param[out] A_h__db Additional loss (clutter loss), in dB * @return Return code ******************************************************************************/ -int CallAeronauticalStatisticalModel( +int CallHeightGainTerminalCorrectionModel( const DrvrParams ¶ms, HGTCParams &hgtc_params, std::vector &A_h__db @@ -111,7 +111,7 @@ int ParseHGTCInputFile(const std::string &in_file, HGTCParams &hgtc_params) { * @param[in] fp Output stream, a text file open for writing * @param[in] hgtc_params HGTC input parameter struct ******************************************************************************/ -void WriteASMInputs(std::ofstream &fp, const HGTCParams ¶ms) { +void WriteHGTCInputs(std::ofstream &fp, const HGTCParams ¶ms) { fp PRINT TAG__FREQ SETW13 params.f__ghz << UNITS__GHZ; fp PRINT TAG__HEIGHT SETW13 params.h__meter << UNITS__METER; fp PRINT TAG__STREET_WIDTH SETW13 params.w_s__meter << UNITS__METER; From b479311dffa629ba64983ffb37884b57fe7a804a Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 11 Oct 2024 14:04:26 -0400 Subject: [PATCH 061/379] Add P2108 functions to driver header --- app/Driver.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/app/Driver.h b/app/Driver.h index 602686f..a5d43f2 100644 --- a/app/Driver.h +++ b/app/Driver.h @@ -35,6 +35,33 @@ void Version(); int ValidateInputs(const DrvrParams ¶ms); int Validate_RequiredErrMsgHelper(const char *opt, int err); +// Aeronautical Statistical Model +int CallAeronauticalStatisticalModel( + const DrvrParams ¶ms, + ASMParams &asm_params, + std::vector &L_ces__db +); +int ParseASMInputFile(const std::string &in_file, ASMParams &asm_params); +void WriteASMInputs(std::ofstream &fp, const ASMParams ¶ms); + +// Height Gain Terminal Correction Model +int CallHeightGainTerminalCorrectionModel( + const DrvrParams ¶ms, + HGTCParams &hgtc_params, + std::vector &A_h__db +); +int ParseHGTCInputFile(const std::string &in_file, HGTCParams &hgtc_params); +void WriteHGTCInputs(std::ofstream &fp, const HGTCParams ¶ms); + +// Terrestrial Statistical Model +int CallTerrestrialStatisticalModel( + const DrvrParams ¶ms, + TSMParams &tsm_params, + std::vector &L_ctt__db +); +int ParseTSMInputFile(const std::string &in_file, TSMParams &tsm_params); +void WriteTSMInputs(std::ofstream &fp, const TSMParams ¶ms); + // Reporting void PrintClutterTypeLabel(std::ofstream &fp, const ClutterType clutter_type); void PrintLabel(std::ofstream &fp, const char *lbl); From 3656d00f946b1e3aced9bcf82e4d0d03ac209c67 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:00:44 -0400 Subject: [PATCH 062/379] finish driver functionality, add outputs --- app/Driver.h | 1 + app/Errors.h | 1 + app/Labels.h | 18 +++++++- app/Reporting.cpp | 50 +++++++++++++++++++++++ app/Structs.h | 11 ++++- app/Tags.h | 3 +- app/data/o_asm.txt | 15 +++++++ app/data/o_hgtcm.txt | 17 ++++++++ app/data/o_tsm.txt | 15 +++++++ app/main.cpp | 97 +++++++++++++++++++++++++++++++++++++++----- 10 files changed, 215 insertions(+), 13 deletions(-) create mode 100644 app/data/o_asm.txt create mode 100644 app/data/o_hgtcm.txt create mode 100644 app/data/o_tsm.txt diff --git a/app/Driver.h b/app/Driver.h index a5d43f2..045bb78 100644 --- a/app/Driver.h +++ b/app/Driver.h @@ -64,6 +64,7 @@ void WriteTSMInputs(std::ofstream &fp, const TSMParams ¶ms); // Reporting void PrintClutterTypeLabel(std::ofstream &fp, const ClutterType clutter_type); +void PrintErrorMsgLabel(std::ofstream &fp, const int err); void PrintLabel(std::ofstream &fp, const char *lbl); // Utils diff --git a/app/Errors.h b/app/Errors.h index 1e976fe..72d6413 100644 --- a/app/Errors.h +++ b/app/Errors.h @@ -31,5 +31,6 @@ /** Validation Errors (1200-1299) */ #define DRVRERR__VALIDATION_IN_FILE 1202 #define DRVRERR__VALIDATION_OUT_FILE 1203 +#define DRVRERR__VALIDATION_MODEL 1204 // clang-format on \ No newline at end of file diff --git a/app/Labels.h b/app/Labels.h index 39e2569..e915253 100644 --- a/app/Labels.h +++ b/app/Labels.h @@ -10,6 +10,11 @@ #define LBL__SUCCESS_WITH_WARNINGS "Success - but with warnings" #define LBL__NO_WARNINGS "No Warnings" +/** P.2108 Model Names */ +#define LBL__HGTCM "Height Gain Terminal Correction Model" +#define LBL__TSM "Terrestrial Statistical Model" +#define LBL__ASM "Aeronautical Statistical Model" + /** Height Gain Terminal Correction Clutter Types */ #define LBL__CLUTTERTYPE_WATER_SEA "Water/sea clutter type" #define LBL__CLUTTERTYPE_OPEN_RURAL "Open/rural clutter type" @@ -20,6 +25,17 @@ /** Error Definitions */ #define LBL__ERROR_INVALID_VALUE "Invalid Value" - +#define LBL__ERROR_UNKNOWN "An unknown error occurred" +#define LBL__ERROR31_FREQUENCY "Frequency must be between 0.3 and 3 GHz, inclusive" +#define LBL__ERROR31_ANTENNA_HEIGHT "Antenna height must be >= 0 meters" +#define LBL__ERROR31_STREET_WIDTH "Street width must be > 0 meters" +#define LBL__ERROR31_CLUTTER_HEIGHT "Representative clutter height must be > 0 meters" +#define LBL__ERROR31_CLUTTER_TYPE "Invalid value for clutter type" +#define LBL__ERROR32_FREQUENCY "Frequency must be between 2 and 67 GHz, inclusive" +#define LBL__ERROR32_DISTANCE "Path distance must be >= 0.25 km" +#define LBL__ERROR32_PERCENTAGE "Percentage must be between 0 and 100" +#define LBL__ERROR33_FREQUENCY "Frequency must be between 10 and 100 GHz, inclusive" +#define LBL__ERROR33_THETA "Elevation angle must be between 0 and 100 GHz, inclusive" +#define LBL__ERROR33_PERCENTAGE "Percentage must be between 0 and 100, inclusive" // clang-format on \ No newline at end of file diff --git a/app/Reporting.cpp b/app/Reporting.cpp index a60e19a..cf5d6be 100644 --- a/app/Reporting.cpp +++ b/app/Reporting.cpp @@ -37,6 +37,56 @@ void PrintClutterTypeLabel(std::ofstream &fp, const ClutterType clutter_type) { } } +/******************************************************************************* + * Print text messages corresponding to error codes + * + * @param[in] fp Output stream, a text file open for writing + * @param[in] err Error code + ******************************************************************************/ +void PrintErrorMsgLabel(std::ofstream &fp, const int err) { + switch (err) { + case SUCCESS: + PrintLabel(fp, LBL__SUCCESS); + break; + case ERROR31__FREQUENCY: + PrintLabel(fp, LBL__ERROR31_FREQUENCY); + break; + case ERROR31__ANTENNA_HEIGHT: + PrintLabel(fp, LBL__ERROR31_ANTENNA_HEIGHT); + break; + case ERROR31__STREET_WIDTH: + PrintLabel(fp, LBL__ERROR31_STREET_WIDTH); + break; + case ERROR31__CLUTTER_HEIGHT: + PrintLabel(fp, LBL__ERROR31_CLUTTER_HEIGHT); + break; + case ERROR31__CLUTTER_TYPE: + PrintLabel(fp, LBL__ERROR31_CLUTTER_TYPE); + break; + case ERROR32__FREQUENCY: + PrintLabel(fp, LBL__ERROR32_FREQUENCY); + break; + case ERROR32__DISTANCE: + PrintLabel(fp, LBL__ERROR32_DISTANCE); + break; + case ERROR32__PERCENTAGE: + PrintLabel(fp, LBL__ERROR32_PERCENTAGE); + break; + case ERROR33__FREQUENCY: + PrintLabel(fp, LBL__ERROR33_FREQUENCY); + break; + case ERROR33__THETA: + PrintLabel(fp, LBL__ERROR33_THETA); + break; + case ERROR33__PERCENTAGE: + PrintLabel(fp, LBL__ERROR33_PERCENTAGE); + break; + default: + PrintLabel(fp, LBL__ERROR_UNKNOWN); + break; + } +} + /******************************************************************************* * Helper function to standardize printing of text labels to file * diff --git a/app/Structs.h b/app/Structs.h index b3c6132..db08101 100644 --- a/app/Structs.h +++ b/app/Structs.h @@ -5,13 +5,22 @@ #include // For std::string +///////////////////////////// +// Enums +enum class P2108Model { + NOT_SET = -1, + HGTCM = 1, + TSM = 2, + ASM = 3, +}; + ///////////////////////////// // Data Structures struct DrvrParams { std::string in_file = ""; // Input file std::string out_file = ""; // Output file - bool DBG = false; // Dump intermediate values to file? + P2108Model model = P2108Model::NOT_SET; }; struct ASMParams { diff --git a/app/Tags.h b/app/Tags.h index 07e193e..15e46a7 100644 --- a/app/Tags.h +++ b/app/Tags.h @@ -11,7 +11,7 @@ #define TAG__PERCENTAGE "p" #define TAG__HEIGHT "h__meter" #define TAG__STREET_WIDTH "w_s__meter" -#define TAG__REPR_HEIGHT "R__meter" +#define TAG__REPR_HEIGHT "r__meter" #define TAG__CLUTTER_TYPE "clutter_type" #define TAG__PATH_DIST "d__km" @@ -21,5 +21,6 @@ #define UNITS__PERCENT "(%)" #define UNITS__METER "(meters)" #define UNITS__KM "(kilometers)" +#define UNITS__DB "(dB)" // clang-format on diff --git a/app/data/o_asm.txt b/app/data/o_asm.txt new file mode 100644 index 0000000..cd771cf --- /dev/null +++ b/app/data/o_asm.txt @@ -0,0 +1,15 @@ +Model P2108 +Model Variant Aeronautical Statistical Model +Library Version v1.0.0 +Driver Version v1.0.0 +Date Generated Fri Oct 11 15:51:57 2024 +Input Arguments -i .\i_asm.txt -o .\o_asm.txt -model ASM + +Inputs +f__ghz 10 (gigahertz) +theta__deg 10.5 (degrees) +p 45 (%) + +Results +Return Code 0 [Success - No Errors] +Clutter loss 12.4 (dB) \ No newline at end of file diff --git a/app/data/o_hgtcm.txt b/app/data/o_hgtcm.txt new file mode 100644 index 0000000..e504dbe --- /dev/null +++ b/app/data/o_hgtcm.txt @@ -0,0 +1,17 @@ +Model P2108 +Model Variant Height Gain Terminal Correction Model +Library Version v1.0.0 +Driver Version v1.0.0 +Date Generated Fri Oct 11 15:54:02 2024 +Input Arguments -i .\i_hgtcm.txt -o .\o_hgtcm.txt -model HGTCM + +Inputs +f__ghz 1.5 (gigahertz) +h__meter 2 (meters) +w_s__meter 27 (meters) +r__meter 15 (meters) +clutter_type 4 [Urban clutter type] + +Results +Return Code 0 [Success - No Errors] +Clutter loss 24.5 (dB) \ No newline at end of file diff --git a/app/data/o_tsm.txt b/app/data/o_tsm.txt new file mode 100644 index 0000000..3d18956 --- /dev/null +++ b/app/data/o_tsm.txt @@ -0,0 +1,15 @@ +Model P2108 +Model Variant Terrestrial Statistical Model +Library Version v1.0.0 +Driver Version v1.0.0 +Date Generated Fri Oct 11 15:54:26 2024 +Input Arguments -i .\i_tsm.txt -o .\o_tsm.txt -model TSM + +Inputs +f__ghz 26.6 (gigahertz) +theta__deg 15.8 (kilometers) +p 45 (%) + +Results +Return Code 0 [Success - No Errors] +Clutter loss 32.5 (dB) \ No newline at end of file diff --git a/app/main.cpp b/app/main.cpp index 604c40f..8b5c88c 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -2,6 +2,8 @@ * Implements the main function of the executable, and other high-level functions */ #include "Driver.h" +#include "Labels.h" +#include "Tags.h" /******************************************************************************* * Main function of the driver executable @@ -26,7 +28,29 @@ int main(int argc, char **argv) { return rtn; } - // TODO-TEMPLATE: Add driver logic, e.g. validating inputs and calling the model + // Initialize model inputs/outputs + HGTCParams hgtc_params; + TSMParams tsm_params; + ASMParams asm_params; + std::vector loss__db; // Use for any model + + switch (params.model) { + case P2108Model::HGTCM: + rtn = CallHeightGainTerminalCorrectionModel( + params, hgtc_params, loss__db + ); + break; + case P2108Model::TSM: + rtn = CallTerrestrialStatisticalModel(params, tsm_params, loss__db); + break; + case P2108Model::ASM: + rtn = CallAeronauticalStatisticalModel( + params, asm_params, loss__db + ); + break; + default: + rtn = DRVRERR__VALIDATION_MODEL; + } // Return driver error code if one was returned if (rtn >= 1100) @@ -40,6 +64,19 @@ int main(int argc, char **argv) { return DRVRERR__OPENING_OUTPUT_FILE; } fp << std::left << std::setw(25) << "Model" << LIBRARY_NAME; + fp PRINT "Model Variant"; + switch (params.model) { + case P2108Model::HGTCM: + fp << LBL__HGTCM; + break; + case P2108Model::TSM: + fp << LBL__TSM; + break; + case P2108Model::ASM: + fp << LBL__ASM; + break; + // Validation above ensures one of these cases evaluates + } fp PRINT "Library Version" << "v" << LIBRARY_VERSION; fp PRINT "Driver Version" << "v" << DRIVER_VERSION; fp PRINT "Date Generated" << GetDatetimeString(); @@ -48,8 +85,33 @@ int main(int argc, char **argv) { fp << argv[i] << " "; } fp << std::endl << std::endl; - // TODO-TEMPLATE populate the rest of the output file + fp << "Inputs"; + + switch (params.model) { + case P2108Model::HGTCM: + WriteHGTCInputs(fp, hgtc_params); + break; + case P2108Model::TSM: + WriteTSMInputs(fp, tsm_params); + break; + case P2108Model::ASM: + WriteASMInputs(fp, asm_params); + break; + // Validation above ensures one of these cases evaluates + } + + if (rtn != SUCCESS) { + fp PRINT LIBRARY_NAME << " Error" SETW13 rtn; + PrintErrorMsgLabel(fp, rtn); + } else { + fp << std::endl << std::endl << "Results"; + fp PRINT "Return Code" SETW13 rtn; + PrintErrorMsgLabel(fp, rtn); + fp PRINT "Clutter loss" SETW13 std::fixed + << std::setprecision(1) << loss__db.front() << UNITS__DB; + } fp.close(); + return SUCCESS; } /******************************************************************************* @@ -67,18 +129,30 @@ int ParseArguments(int argc, char **argv, DrvrParams ¶ms) { return static_cast(std::tolower(c)); }); - // TODO-TEMPLATE: Specify behavior based on command line arguments. - // Template code will set in_file and out_file in DrvrParams based on -i - // and -o options. It will also set DrvrParams.DBG based on a -dbg flag - // and will call Version() or Help() respectively if -v or -h is given. if (arg == "-i") { params.in_file = argv[i + 1]; i++; } else if (arg == "-o") { params.out_file = argv[i + 1]; i++; - } else if (arg == "-dbg") { - params.DBG = true; + } else if (arg == "-model") { + std::string argval(argv[i + 1]); + std::transform( + argval.begin(), + argval.end(), + argval.begin(), + [](const char c) { return static_cast(std::tolower(c)); } + ); + if (argval == "asm") { + params.model = P2108Model::ASM; + } else if (argval == "hgtcm") { + params.model = P2108Model::HGTCM; + } else if (argval == "tsm") { + params.model = P2108Model::TSM; + } else { + params.model = P2108Model::NOT_SET; + } + i++; } else if (arg == "-v") { Version(); return DRVR__RETURN_SUCCESS; @@ -140,8 +214,6 @@ void Version() { * @return Return code ******************************************************************************/ int ValidateInputs(const DrvrParams ¶ms) { - // TODO-TEMPLATE: Check that required inputs were provided. - // This template code checks that input/output files were given with -i and -o if (params.in_file.length() == 0) return Validate_RequiredErrMsgHelper("-i", DRVRERR__VALIDATION_IN_FILE); @@ -150,6 +222,11 @@ int ValidateInputs(const DrvrParams ¶ms) { "-o", DRVRERR__VALIDATION_OUT_FILE ); + if (params.model == P2108Model::NOT_SET) + return Validate_RequiredErrMsgHelper( + "-model", DRVRERR__VALIDATION_MODEL + ); + return SUCCESS; } From bc6c9247b514baa3d87eaf8d3a8a5afc2c311b93 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:10:34 -0400 Subject: [PATCH 063/379] make GetDatetimeString thread-safe across platforms --- app/Driver.h | 10 +++++----- app/Utils.cpp | 17 +++++++++++++---- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/app/Driver.h b/app/Driver.h index f8364c4..6b1f46b 100644 --- a/app/Driver.h +++ b/app/Driver.h @@ -12,11 +12,11 @@ #include // for transform #include // for tolower #include // for strlen -#include // for localtime, time, time_t, strftime -#include // for ifstream, ofstream -#include // for setw -#include // for cerr, cout -#include // for string, stoi, stod +#include // for localtime_s, localtime_r, time, time_t, tm, strftime +#include // for ifstream, ofstream +#include // for setw +#include // for cerr, cout +#include // for string, stoi, stod ///////////////////////////// // Macros diff --git a/app/Utils.cpp b/app/Utils.cpp index 4f50d68..d30d316 100644 --- a/app/Utils.cpp +++ b/app/Utils.cpp @@ -65,9 +65,18 @@ int ParsingErrorHelper(int err, const char *msg) { ******************************************************************************/ std::string GetDatetimeString() { std::time_t now = std::time(nullptr); + struct std::tm localTime; + +#ifdef _WIN32 + localtime_s(&localTime, &now); +#else + if (localtime_r(&now, &localTime) == nullptr) { + return "Date and time unknown"; + } +#endif char mbstr[100]; - // Warning: std::localtime may or may not be thread-safe - std::strftime(mbstr, sizeof(mbstr), "%c", std::localtime(&now)); - std::string dtString = mbstr; - return dtString; + if (std::strftime(mbstr, sizeof(mbstr), "%c", &localTime) == 0) { + return "Could not format datetime string"; + } + return std::string(mbstr); } \ No newline at end of file From 446914046b9f09b70d36d3f05ca7ff9d664d2f92 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:25:11 -0400 Subject: [PATCH 064/379] move functions to utils --- app/Driver.h | 5 +++-- app/Utils.cpp | 30 +++++++++++++++++++++++++++++- app/main.cpp | 28 ---------------------------- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/app/Driver.h b/app/Driver.h index 6b1f46b..5e930f5 100644 --- a/app/Driver.h +++ b/app/Driver.h @@ -33,11 +33,12 @@ int ParseArguments(int argc, char **argv, DrvrParams ¶ms); void Help(); -void Version(); int ValidateInputs(const DrvrParams ¶ms); -int Validate_RequiredErrMsgHelper(const char *opt, int err); + // Utils +void Version(); +int Validate_RequiredErrMsgHelper(const char *opt, int err); int ParseInteger(const char *str, int &value); int ParseDouble(const char *str, double &value); int ParsingErrorHelper(int err, const char *msg); diff --git a/app/Utils.cpp b/app/Utils.cpp index d30d316..2cd18a9 100644 --- a/app/Utils.cpp +++ b/app/Utils.cpp @@ -1,8 +1,36 @@ /** @file Utils.cpp - * Implements utility functions for parsing driver inputs from text + * Implements various model-agnostic utility functions for the driver */ #include "Driver.h" +/******************************************************************************* + * Print version information to the terminal + ******************************************************************************/ +void Version() { + std::cout << std::setfill('*') << std::setw(55) << "" << std::endl; + std::cout << "Institute for Telecommunications Sciences - Boulder, CO" + << std::endl; + std::cout << "\tDriver Version: " << DRIVER_VERSION << std::endl; + std::cout << "\t" << LIBRARY_NAME << " Version: " << LIBRARY_VERSION + << std::endl; + std::cout << "Time: " << GetDatetimeString() << std::endl; + std::cout << std::setfill('*') << std::setw(55) << "" << std::endl; +} + +/******************************************************************************* + * Helper function to format and print error messages encountered during + * validation of input parameters + * + * @param[in] opt Command flag in error + * @param[in] err Error code + * @return Return code + ******************************************************************************/ +int Validate_RequiredErrMsgHelper(const char *opt, int err) { + std::cerr << "Driver Error " << err << ": Option " << opt + << " is required but was not provided" << std::endl; + return err; +} + /******************************************************************************* * Parse an integer value read from the input parameter file * diff --git a/app/main.cpp b/app/main.cpp index 604c40f..74f10de 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -116,20 +116,6 @@ void Help() { std::cout << std::endl; }; -/******************************************************************************* - * Print version information to the terminal - ******************************************************************************/ -void Version() { - std::cout << std::setfill('*') << std::setw(55) << "" << std::endl; - std::cout << "Institute for Telecommunications Sciences - Boulder, CO" - << std::endl; - std::cout << "\tDriver Version: " << DRIVER_VERSION << std::endl; - std::cout << "\t" << LIBRARY_NAME << " Version: " << LIBRARY_VERSION - << std::endl; - std::cout << "Time: " << GetDatetimeString() << std::endl; - std::cout << std::setfill('*') << std::setw(55) << "" << std::endl; -} - /******************************************************************************* * Validate that required inputs are present for the mode specified by the user. * @@ -152,17 +138,3 @@ int ValidateInputs(const DrvrParams ¶ms) { return SUCCESS; } - -/******************************************************************************* - * Helper function to format and print error messages encountered during - * validation of input parameters - * - * @param[in] opt Command flag in error - * @param[in] err Error code - * @return Return code - ******************************************************************************/ -int Validate_RequiredErrMsgHelper(const char *opt, int err) { - std::cerr << "Driver Error " << err << ": Option " << opt - << " is required but was not provided" << std::endl; - return err; -} From aa4257a42bf0e396bb74caa28a5bba41d6f46f8e Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:50:36 -0400 Subject: [PATCH 065/379] Add driver testing configuration --- CMakeLists.txt | 1 + CMakePresets.json | 6 ++++-- app/CMakeLists.txt | 4 ++++ app/tests/CMakeLists.txt | 26 ++++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 app/tests/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 61eca21..852b9f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ set(PYTHON_WRAPPER_DIR "${PROJECT_SOURCE_DIR}/wrap/python") # Define options. Defaults to: compile 64-bit library and driver, build docs, run tests option(BUILD_DOCS "Generate documentation with Doxygen" ON) option(BUILD_DRIVER "Build driver executable" ON) +option(RUN_DRIVER_TESTS "Test the driver executable" 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) diff --git a/CMakePresets.json b/CMakePresets.json index 1a9525e..5e0bee2 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -26,7 +26,8 @@ "cacheVariables": { "CMAKE_BUILD_TYPE": "Release", "BUILD_DOCS": "ON", - "BUILD_DRIVER": "ON" + "BUILD_DRIVER": "ON", + "RUN_DRIVER_TESTS": "ON" } }, { @@ -37,7 +38,8 @@ "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "BUILD_DOCS": "OFF", - "BUILD_DRIVER": "OFF" + "BUILD_DRIVER": "OFF", + "RUN_DRIVER_TESTS": "OFF" } }, { diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 5eb2e3d..b64adb9 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -48,3 +48,7 @@ else () ) endif () +# Build and run driver tests +if (RUN_DRIVER_TESTS) + add_subdirectory(tests) +endif() diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt new file mode 100644 index 0000000..c042642 --- /dev/null +++ b/app/tests/CMakeLists.txt @@ -0,0 +1,26 @@ +############################################ +## CONFIGURE COMMAND LINE DRIVER TESTS +############################################ +set(DRIVER_TEST_NAME "Test${LIB_NAME}Driver") + +## TODO-TEMPLATE: Include source AND header files for tests here. +add_executable( + ${DRIVER_TEST_NAME} + "TestDriverUtils.cpp" +) + +########################################### +## 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(${DRIVER_TEST_NAME} ${DRIVER_NAME} GTest::gtest_main) +gtest_discover_tests(${DRIVER_TEST_NAME}) \ No newline at end of file From 48d3c6d264383d5fc762c99ff95e57174137d4e8 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:51:07 -0400 Subject: [PATCH 066/379] modernize driver utility functions use strings, avoid pointers, fix a typo in Version output --- app/Driver.h | 12 ++++++------ app/Utils.cpp | 48 ++++++++++++++++++++++++------------------------ 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/app/Driver.h b/app/Driver.h index 5e930f5..9810c26 100644 --- a/app/Driver.h +++ b/app/Driver.h @@ -15,7 +15,7 @@ #include // for localtime_s, localtime_r, time, time_t, tm, strftime #include // for ifstream, ofstream #include // for setw -#include // for cerr, cout +#include // for cerr, cout, ostream #include // for string, stoi, stod ///////////////////////////// @@ -37,9 +37,9 @@ int ValidateInputs(const DrvrParams ¶ms); // Utils -void Version(); -int Validate_RequiredErrMsgHelper(const char *opt, int err); -int ParseInteger(const char *str, int &value); -int ParseDouble(const char *str, double &value); -int ParsingErrorHelper(int err, const char *msg); +void Version(std::ostream &os = std::cout); +int Validate_RequiredErrMsgHelper(const std::string &opt, int err); +int ParseInteger(const std::string &str, int &value); +int ParseDouble(const std::string &str, double &value); +int ParsingErrorHelper(int err, const std::string &msg); std::string GetDatetimeString(); \ No newline at end of file diff --git a/app/Utils.cpp b/app/Utils.cpp index 2cd18a9..7ef79a7 100644 --- a/app/Utils.cpp +++ b/app/Utils.cpp @@ -4,17 +4,17 @@ #include "Driver.h" /******************************************************************************* - * Print version information to the terminal + * Print version information to the specified output stream + * + * @param[in] os Output stream for writing; defaults to `std::cout` ******************************************************************************/ -void Version() { - std::cout << std::setfill('*') << std::setw(55) << "" << std::endl; - std::cout << "Institute for Telecommunications Sciences - Boulder, CO" - << std::endl; - std::cout << "\tDriver Version: " << DRIVER_VERSION << std::endl; - std::cout << "\t" << LIBRARY_NAME << " Version: " << LIBRARY_VERSION - << std::endl; - std::cout << "Time: " << GetDatetimeString() << std::endl; - std::cout << std::setfill('*') << std::setw(55) << "" << std::endl; +void Version(std::ostream &os = std::cout) { + os << std::setfill('*') << std::setw(55) << "" << std::endl; + os << "Institute for Telecommunication Sciences - Boulder, CO" << std::endl; + os << "\tDriver Version: " << DRIVER_VERSION << std::endl; + os << "\t" << LIBRARY_NAME << " Version: " << LIBRARY_VERSION << std::endl; + os << "Time: " << GetDatetimeString() << std::endl; + os << std::setfill('*') << std::setw(55) << "" << std::endl; } /******************************************************************************* @@ -25,9 +25,9 @@ void Version() { * @param[in] err Error code * @return Return code ******************************************************************************/ -int Validate_RequiredErrMsgHelper(const char *opt, int err) { - std::cerr << "Driver Error " << err << ": Option " << opt - << " is required but was not provided" << std::endl; +int Validate_RequiredErrMsgHelper(const std::string &opt, int err) { + std::cerr << "Driver Error " << err << ": Option \"" << opt + << "\" is required but was not provided" << std::endl; return err; } @@ -38,19 +38,19 @@ int Validate_RequiredErrMsgHelper(const char *opt, int err) { * @param[out] value Input file value converted to int * @return Return code ******************************************************************************/ -int ParseInteger(const char *str, int &value) { - size_t t; - +int ParseInteger(const std::string &str, int &value) { try { - value = std::stoi(str, &t, 10); + size_t pos; + value = std::stoi(str, &pos, 10); + + // Verify the entire string was parsed + if (pos != str.size()) { + return DRVRERR__PARSE; + } } catch (...) { // error parsing the input string value return DRVRERR__PARSE; - } - - // verify the entire string was parsed, and a trailing char wasn't omitted - if (std::strlen(str) != t) - return DRVRERR__PARSE; + }; return SUCCESS; } @@ -62,7 +62,7 @@ int ParseInteger(const char *str, int &value) { * @param[out] value Input file value converted to double * @return Return code ******************************************************************************/ -int ParseDouble(const char *str, double &value) { +int ParseDouble(const std::string &str, double &value) { try { value = std::stod(str); } catch (...) { @@ -80,7 +80,7 @@ int ParseDouble(const char *str, double &value) { * @param[in] msg Error message * @return Error code from input param ******************************************************************************/ -int ParsingErrorHelper(int err, const char *msg) { +int ParsingErrorHelper(int err, const std::string &msg) { std::cerr << "Driver Error " << err << ": Unable to parse '" << msg << "' value." << std::endl; return err; From 5b23eb6133d5e3902514673454d74bde1d7aa492 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:52:46 -0400 Subject: [PATCH 067/379] const correct driver util functions --- app/Driver.h | 4 ++-- app/Utils.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Driver.h b/app/Driver.h index 9810c26..7a35d05 100644 --- a/app/Driver.h +++ b/app/Driver.h @@ -38,8 +38,8 @@ int ValidateInputs(const DrvrParams ¶ms); // Utils void Version(std::ostream &os = std::cout); -int Validate_RequiredErrMsgHelper(const std::string &opt, int err); +int Validate_RequiredErrMsgHelper(const std::string &opt, const int err); int ParseInteger(const std::string &str, int &value); int ParseDouble(const std::string &str, double &value); -int ParsingErrorHelper(int err, const std::string &msg); +int ParsingErrorHelper(const int err, const std::string &msg); std::string GetDatetimeString(); \ No newline at end of file diff --git a/app/Utils.cpp b/app/Utils.cpp index 7ef79a7..0a9aaf2 100644 --- a/app/Utils.cpp +++ b/app/Utils.cpp @@ -25,7 +25,7 @@ void Version(std::ostream &os = std::cout) { * @param[in] err Error code * @return Return code ******************************************************************************/ -int Validate_RequiredErrMsgHelper(const std::string &opt, int err) { +int Validate_RequiredErrMsgHelper(const std::string &opt, const int err) { std::cerr << "Driver Error " << err << ": Option \"" << opt << "\" is required but was not provided" << std::endl; return err; @@ -80,7 +80,7 @@ int ParseDouble(const std::string &str, double &value) { * @param[in] msg Error message * @return Error code from input param ******************************************************************************/ -int ParsingErrorHelper(int err, const std::string &msg) { +int ParsingErrorHelper(const int err, const std::string &msg) { std::cerr << "Driver Error " << err << ": Unable to parse '" << msg << "' value." << std::endl; return err; From c333e6e19f5b68a20799d6001a7a1ee67d526204 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:53:52 -0400 Subject: [PATCH 068/379] rename Utils to DriverUtils --- app/CMakeLists.txt | 2 +- app/{Utils.cpp => DriverUtils.cpp} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename app/{Utils.cpp => DriverUtils.cpp} (99%) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index b64adb9..276c1ac 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -9,9 +9,9 @@ add_executable( ${DRIVER_NAME} "main.cpp" "Driver.h" + "DriverUtils.cpp" "Errors.h" "Structs.h" - "Utils.cpp" ) # Link the library to the executable diff --git a/app/Utils.cpp b/app/DriverUtils.cpp similarity index 99% rename from app/Utils.cpp rename to app/DriverUtils.cpp index 0a9aaf2..c715eda 100644 --- a/app/Utils.cpp +++ b/app/DriverUtils.cpp @@ -1,4 +1,4 @@ -/** @file Utils.cpp +/** @file DriverUtils.cpp * Implements various model-agnostic utility functions for the driver */ #include "Driver.h" From 4dae8a6c9a96ab06b827e4adbb11f960be3c4486 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:06:03 -0400 Subject: [PATCH 069/379] Add simple tests for parsing functions --- app/Driver.h | 2 +- app/tests/CMakeLists.txt | 1 + app/tests/TestDriver.h | 8 ++++++ app/tests/TestDriverUtils.cpp | 46 +++++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 app/tests/TestDriver.h create mode 100644 app/tests/TestDriverUtils.cpp diff --git a/app/Driver.h b/app/Driver.h index 7a35d05..ab1bb51 100644 --- a/app/Driver.h +++ b/app/Driver.h @@ -36,7 +36,7 @@ void Help(); int ValidateInputs(const DrvrParams ¶ms); -// Utils +// Driver Utils void Version(std::ostream &os = std::cout); int Validate_RequiredErrMsgHelper(const std::string &opt, const int err); int ParseInteger(const std::string &str, int &value); diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index c042642..cbdcfa7 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -7,6 +7,7 @@ set(DRIVER_TEST_NAME "Test${LIB_NAME}Driver") add_executable( ${DRIVER_TEST_NAME} "TestDriverUtils.cpp" + "TestDriver.h" ) ########################################### diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h new file mode 100644 index 0000000..987ae95 --- /dev/null +++ b/app/tests/TestDriver.h @@ -0,0 +1,8 @@ +/** @file TestDriver.h + * Primary header for command line driver tests. + */ +#pragma once + +#include "Driver.h" + +#include diff --git a/app/tests/TestDriverUtils.cpp b/app/tests/TestDriverUtils.cpp new file mode 100644 index 0000000..75ec489 --- /dev/null +++ b/app/tests/TestDriverUtils.cpp @@ -0,0 +1,46 @@ +#include "TestDriver.h" + +TEST(ParseIntegerTest, TestParseIntegerSuccess) { + int value; + std::string toParse = "42"; + int rtn = ParseInteger(toParse, value); + EXPECT_EQ(rtn, SUCCESS); + EXPECT_EQ(value, 42); +} + +TEST(ParseIntegerTest, TestParseIntegerFailure1) { + int value; + std::string toParse = "42.0"; + int rtn = ParseInteger(toParse, value); + EXPECT_EQ(rtn, DRVRERR__PARSE); +} + +TEST(ParseIntegerTest, TestParseIntegerFailure2) { + int value; + std::string toParse = "notAnInteger"; + int rtn = ParseInteger(toParse, value); + EXPECT_EQ(rtn, DRVRERR__PARSE); +} + +TEST(ParseDoubleTest, TestParseDoubleSuccess1) { + double value; + std::string toParse = "42.0"; + int rtn = ParseDouble(toParse, value); + EXPECT_EQ(rtn, SUCCESS); + EXPECT_DOUBLE_EQ(value, 42.0); +} + +TEST(ParseDoubleTest, TestParseDoubleSuccess2) { + double value; + std::string toParse = "42"; + int rtn = ParseDouble(toParse, value); + EXPECT_EQ(rtn, SUCCESS); + EXPECT_DOUBLE_EQ(value, 42.0); +} + +TEST(ParseDoubleTest, TestParseDoubleFailure) { + double value; + std::string toParse = "notADouble"; + int rtn = ParseDouble(toParse, value); + EXPECT_EQ(rtn, DRVRERR__PARSE); +} From 40b16c7844c1b19ea4795279335df31e429b3721 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:08:38 -0400 Subject: [PATCH 070/379] pragma once in driver --- include/ITS.ITU.PSeries.P2108/Enums.h | 5 +---- include/ITS.ITU.PSeries.P2108/Errors.h | 5 +---- include/ITS.ITU.PSeries.P2108/P2108.h | 5 +---- tests/TestUtils.h | 5 +---- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/include/ITS.ITU.PSeries.P2108/Enums.h b/include/ITS.ITU.PSeries.P2108/Enums.h index d998982..f63d635 100644 --- a/include/ITS.ITU.PSeries.P2108/Enums.h +++ b/include/ITS.ITU.PSeries.P2108/Enums.h @@ -1,8 +1,7 @@ /** @file Enums.h * Enumerated types used by this software */ -#ifndef __ITS_ITU_PSERIES_P2108_ENUMS__ -#define __ITS_ITU_PSERIES_P2108_ENUMS__ +#pragma once namespace ITS { namespace ITU { @@ -39,5 +38,3 @@ enum class RepresentativeClutterHeight { } // 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 index cc48e3f..ac9e72d 100644 --- a/include/ITS.ITU.PSeries.P2108/Errors.h +++ b/include/ITS.ITU.PSeries.P2108/Errors.h @@ -1,8 +1,7 @@ /** @file Errors.h * Contains return codes used by this software */ -#ifndef __ITS_ITU_PSERIES_P2108_ERRORS__ -#define __ITS_ITU_PSERIES_P2108_ERRORS__ +#pragma once namespace ITS { namespace ITU { @@ -39,5 +38,3 @@ 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 index a879c29..8a480e6 100644 --- a/include/ITS.ITU.PSeries.P2108/P2108.h +++ b/include/ITS.ITU.PSeries.P2108/P2108.h @@ -1,8 +1,7 @@ /** @file P2108.h * Interface header for this library */ -#ifndef __ITS_ITU_PSERIES_P2108_P2108_H__ -#define __ITS_ITU_PSERIES_P2108_P2108_H__ +#pragma once #include "Enums.h" #include "Errors.h" @@ -85,5 +84,3 @@ double TerrestrialStatisticalModelHelper( } // namespace PSeries } // namespace ITU } // namespace ITS - -#endif \ No newline at end of file diff --git a/tests/TestUtils.h b/tests/TestUtils.h index 8e5218b..e56e5d4 100644 --- a/tests/TestUtils.h +++ b/tests/TestUtils.h @@ -1,5 +1,4 @@ -#ifndef __ITS_ITU_PSERIES_P2108_TEST_UTILS_H__ -#define __ITS_ITU_PSERIES_P2108_TEST_UTILS_H__ +#pragma once #include "ITS.ITU.PSeries.P2108/P2108.h" @@ -46,5 +45,3 @@ std::vector std::vector readTerrestrialStatisticalModelTestData(const std::string &filename); - -#endif From 571078c317bc768937d0f5226daa88fcd50e0a3b Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:27:48 -0400 Subject: [PATCH 071/379] avoid duplicate googletest init --- app/CMakeLists.txt | 1 + app/tests/CMakeLists.txt | 10 ++++++---- tests/CMakeLists.txt | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index a138eba..ec07522 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -54,5 +54,6 @@ endif () # Build and run driver tests if (RUN_DRIVER_TESTS) + set_target_properties(${DRIVER_NAME} PROPERTIES ENABLE_EXPORTS ON) add_subdirectory(tests) endif() diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index cbdcfa7..1f114ed 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -13,12 +13,14 @@ add_executable( ########################################### ## 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) +if (NOT ${GOOGLETEST_INITIALIZED}) # Avoids duplicate initialization + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + add_subdirectory("${PROJECT_SOURCE_DIR}/extern/googletest" "extern/googletest") + include(GoogleTest) +endif() include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) set_target_properties( - ${TEST_NAME} PROPERTIES + ${DRIVER_TEST_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a505c89..2cb2d83 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -21,6 +21,7 @@ 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(GOOGLETEST_INITIALIZED ON) set_target_properties( ${TEST_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" From df9f3a182df0c640fa9b71d357a32eac99d98dd6 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:28:02 -0400 Subject: [PATCH 072/379] Avoid duplicate googletest init --- app/tests/CMakeLists.txt | 10 ++++++---- tests/CMakeLists.txt | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index cbdcfa7..1f114ed 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -13,12 +13,14 @@ add_executable( ########################################### ## 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) +if (NOT ${GOOGLETEST_INITIALIZED}) # Avoids duplicate initialization + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + add_subdirectory("${PROJECT_SOURCE_DIR}/extern/googletest" "extern/googletest") + include(GoogleTest) +endif() include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) set_target_properties( - ${TEST_NAME} PROPERTIES + ${DRIVER_TEST_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f3d6465..74aa7dd 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -22,6 +22,7 @@ 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(GOOGLETEST_INITIALIZED ON) set_target_properties( ${TEST_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" From 214465d89b759edc725bd2c9653b4e90098253b2 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:28:07 -0400 Subject: [PATCH 073/379] enable exports for driver tests --- app/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 276c1ac..73e7e36 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -50,5 +50,6 @@ endif () # Build and run driver tests if (RUN_DRIVER_TESTS) + set_target_properties(${DRIVER_NAME} PROPERTIES ENABLE_EXPORTS ON) add_subdirectory(tests) endif() From fa67822fa8ee096710bfd79790047b20c17d61a2 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:30:00 -0400 Subject: [PATCH 074/379] remove default from Version definition --- app/DriverUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/DriverUtils.cpp b/app/DriverUtils.cpp index c715eda..080e734 100644 --- a/app/DriverUtils.cpp +++ b/app/DriverUtils.cpp @@ -8,7 +8,7 @@ * * @param[in] os Output stream for writing; defaults to `std::cout` ******************************************************************************/ -void Version(std::ostream &os = std::cout) { +void Version(std::ostream &os) { os << std::setfill('*') << std::setw(55) << "" << std::endl; os << "Institute for Telecommunication Sciences - Boulder, CO" << std::endl; os << "\tDriver Version: " << DRIVER_VERSION << std::endl; From 909b70f8f62b60b0fbf906996a2ddf00583c9efa Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:38:55 -0400 Subject: [PATCH 075/379] Reorganize driver code --- app/CMakeLists.txt | 53 ++++------------------------------- app/{ => include}/Driver.h | 0 app/{ => include}/Errors.h | 0 app/{ => include}/Structs.h | 0 app/src/CMakeLists.txt | 50 +++++++++++++++++++++++++++++++++ app/{ => src}/DriverUtils.cpp | 0 app/{ => src}/main.cpp | 0 7 files changed, 55 insertions(+), 48 deletions(-) rename app/{ => include}/Driver.h (100%) rename app/{ => include}/Errors.h (100%) rename app/{ => include}/Structs.h (100%) create mode 100644 app/src/CMakeLists.txt rename app/{ => src}/DriverUtils.cpp (100%) rename app/{ => src}/main.cpp (100%) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 73e7e36..9cb4900 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -1,54 +1,11 @@ ########################################### -## BUILD THE DRIVER +## BUILD THE COMMAND LINE DRIVER ########################################### -set(DRIVER_NAME "${LIB_NAME}Driver") -set(DRIVER_VERSION ${PROJECT_VERSION}) # May be set manually instead -## TODO-TEMPLATE: Include source AND header files here. Do not include -## source/header files already included in `add_library()` in `src/CMakeLists.txt` -add_executable( - ${DRIVER_NAME} - "main.cpp" - "Driver.h" - "DriverUtils.cpp" - "Errors.h" - "Structs.h" -) +add_subdirectory(src) -# Link the library to the executable -target_link_libraries(${DRIVER_NAME} ${LIB_NAME}) - -# Add definitions to enable version identification inside the driver -add_definitions(-DLIBRARY_VERSION="${PROJECT_VERSION}") -add_definitions(-DDRIVER_VERSION="${DRIVER_VERSION}") -add_definitions(-DLIBRARY_NAME="${LIB_NAME}") -add_definitions(-DDRIVER_NAME="${DRIVER_NAME}") - -# Set some target metadata -set_target_properties( - ${DRIVER_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( - ${DRIVER_NAME} PROPERTIES - DEBUG_POSTFIX "x86" - RELEASE_POSTFIX "x86" - ) -else () - set_target_properties( - ${DRIVER_NAME} PROPERTIES - DEBUG_POSTFIX "x64" - RELEASE_POSTFIX "x64" - ) -endif () - -# Build and run driver tests +########################################### +## BUILD AND RUN THE DRIVER TESTS +########################################### if (RUN_DRIVER_TESTS) set_target_properties(${DRIVER_NAME} PROPERTIES ENABLE_EXPORTS ON) add_subdirectory(tests) diff --git a/app/Driver.h b/app/include/Driver.h similarity index 100% rename from app/Driver.h rename to app/include/Driver.h diff --git a/app/Errors.h b/app/include/Errors.h similarity index 100% rename from app/Errors.h rename to app/include/Errors.h diff --git a/app/Structs.h b/app/include/Structs.h similarity index 100% rename from app/Structs.h rename to app/include/Structs.h diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt new file mode 100644 index 0000000..5fa48c8 --- /dev/null +++ b/app/src/CMakeLists.txt @@ -0,0 +1,50 @@ +########################################### +## BUILD THE DRIVER +########################################### +set(DRIVER_NAME "${LIB_NAME}Driver") +set(DRIVER_VERSION ${PROJECT_VERSION}) # May be set manually instead +set(DRIVER_HEADERS "${PROJECT_SOURCE_DIR}/app/include") +## TODO-TEMPLATE: Include source AND header files here. Do not include +## source/header files already included in `add_library()` in `src/CMakeLists.txt` +add_executable( + ${DRIVER_NAME} + "main.cpp" + "DriverUtils.cpp" + "${DRIVER_HEADERS}/Driver.h" + "${DRIVER_HEADERS}/Errors.h" + "${DRIVER_HEADERS}/Structs.h" +) + +# Link the library to the executable +target_link_libraries(${DRIVER_NAME} ${LIB_NAME}) + +# Add definitions to enable version identification inside the driver +add_definitions(-DLIBRARY_VERSION="${PROJECT_VERSION}") +add_definitions(-DDRIVER_VERSION="${DRIVER_VERSION}") +add_definitions(-DLIBRARY_NAME="${LIB_NAME}") +add_definitions(-DDRIVER_NAME="${DRIVER_NAME}") + +# Set some target metadata +set_target_properties( + ${DRIVER_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( + ${DRIVER_NAME} PROPERTIES + DEBUG_POSTFIX "x86" + RELEASE_POSTFIX "x86" + ) +else () + set_target_properties( + ${DRIVER_NAME} PROPERTIES + DEBUG_POSTFIX "x64" + RELEASE_POSTFIX "x64" + ) +endif () \ No newline at end of file diff --git a/app/DriverUtils.cpp b/app/src/DriverUtils.cpp similarity index 100% rename from app/DriverUtils.cpp rename to app/src/DriverUtils.cpp diff --git a/app/main.cpp b/app/src/main.cpp similarity index 100% rename from app/main.cpp rename to app/src/main.cpp From e7285b1b2ed97e6287750fa752f5550222a87180 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:45:05 -0400 Subject: [PATCH 076/379] Add include directories for driver and driver tests --- app/src/CMakeLists.txt | 3 +++ app/tests/CMakeLists.txt | 3 +++ 2 files changed, 6 insertions(+) diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index 5fa48c8..0c39ac2 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -15,6 +15,9 @@ add_executable( "${DRIVER_HEADERS}/Structs.h" ) +# Add the include directory +target_include_directories(${DRIVER_NAME} PUBLIC "${DRIVER_HEADERS}") + # Link the library to the executable target_link_libraries(${DRIVER_NAME} ${LIB_NAME}) diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index 1f114ed..2dd7a70 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -10,6 +10,9 @@ add_executable( "TestDriver.h" ) +# Add the include directory +target_include_directories(${DRIVER_TEST_NAME} PUBLIC "${DRIVER_HEADERS}") + ########################################### ## SET UP AND DISCOVER TESTS ########################################### From 2f1b60b679dc07de118674a4b6abf3571cfca9e3 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:45:30 -0400 Subject: [PATCH 077/379] Move files into new directory structure --- app/{ => include}/Labels.h | 0 app/{ => include}/Tags.h | 0 app/{ => src}/AeronauticalStatisticalModel.cpp | 0 app/src/CMakeLists.txt | 11 ++++++++--- app/{ => src}/HeightGainTerminalCorrectionModel.cpp | 0 app/{ => src}/Reporting.cpp | 0 app/{ => src}/TerrestrialStatisticalModel.cpp | 0 7 files changed, 8 insertions(+), 3 deletions(-) rename app/{ => include}/Labels.h (100%) rename app/{ => include}/Tags.h (100%) rename app/{ => src}/AeronauticalStatisticalModel.cpp (100%) rename app/{ => src}/HeightGainTerminalCorrectionModel.cpp (100%) rename app/{ => src}/Reporting.cpp (100%) rename app/{ => src}/TerrestrialStatisticalModel.cpp (100%) diff --git a/app/Labels.h b/app/include/Labels.h similarity index 100% rename from app/Labels.h rename to app/include/Labels.h diff --git a/app/Tags.h b/app/include/Tags.h similarity index 100% rename from app/Tags.h rename to app/include/Tags.h diff --git a/app/AeronauticalStatisticalModel.cpp b/app/src/AeronauticalStatisticalModel.cpp similarity index 100% rename from app/AeronauticalStatisticalModel.cpp rename to app/src/AeronauticalStatisticalModel.cpp diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index 5fa48c8..e07b07a 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -4,15 +4,20 @@ set(DRIVER_NAME "${LIB_NAME}Driver") set(DRIVER_VERSION ${PROJECT_VERSION}) # May be set manually instead set(DRIVER_HEADERS "${PROJECT_SOURCE_DIR}/app/include") -## TODO-TEMPLATE: Include source AND header files here. Do not include -## source/header files already included in `add_library()` in `src/CMakeLists.txt` + add_executable( ${DRIVER_NAME} - "main.cpp" + "AeronauticalStatisticalModel.cpp" "DriverUtils.cpp" + "HeightGainTerminalCorrectionModel.cpp" + "main.cpp" + "Reporting.cpp" + "TerrestrialStatisticalModel.cpp" "${DRIVER_HEADERS}/Driver.h" "${DRIVER_HEADERS}/Errors.h" + "${DRIVER_HEADERS}/Labels.h" "${DRIVER_HEADERS}/Structs.h" + "${DRIVER_HEADERS}/Tags.h" ) # Link the library to the executable diff --git a/app/HeightGainTerminalCorrectionModel.cpp b/app/src/HeightGainTerminalCorrectionModel.cpp similarity index 100% rename from app/HeightGainTerminalCorrectionModel.cpp rename to app/src/HeightGainTerminalCorrectionModel.cpp diff --git a/app/Reporting.cpp b/app/src/Reporting.cpp similarity index 100% rename from app/Reporting.cpp rename to app/src/Reporting.cpp diff --git a/app/TerrestrialStatisticalModel.cpp b/app/src/TerrestrialStatisticalModel.cpp similarity index 100% rename from app/TerrestrialStatisticalModel.cpp rename to app/src/TerrestrialStatisticalModel.cpp From 52903992686881d1ccc83925bd5b467c07461d57 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:46:38 -0400 Subject: [PATCH 078/379] remove template text --- app/include/Errors.h | 2 -- app/tests/CMakeLists.txt | 1 - 2 files changed, 3 deletions(-) diff --git a/app/include/Errors.h b/app/include/Errors.h index 72d6413..3e4a086 100644 --- a/app/include/Errors.h +++ b/app/include/Errors.h @@ -26,8 +26,6 @@ #define DRVRERR__PARSE_CLUTTER_TYPE 1107 #define DRVRERR__PARSE_PATH_DIST 1108 -// TODO-TEMPLATE: Add driver error codes and document them in app/README.md - /** Validation Errors (1200-1299) */ #define DRVRERR__VALIDATION_IN_FILE 1202 #define DRVRERR__VALIDATION_OUT_FILE 1203 diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index 1f114ed..d5b2e98 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -3,7 +3,6 @@ ############################################ set(DRIVER_TEST_NAME "Test${LIB_NAME}Driver") -## TODO-TEMPLATE: Include source AND header files for tests here. add_executable( ${DRIVER_TEST_NAME} "TestDriverUtils.cpp" From 2ed99dea4fc522f9900cab7f11db40d80b1e0946 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:48:26 -0400 Subject: [PATCH 079/379] parameterize help output stream --- app/include/Driver.h | 2 +- app/src/main.cpp | 32 ++++++++++++++++---------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/app/include/Driver.h b/app/include/Driver.h index ab1bb51..9e09340 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -32,7 +32,7 @@ // Functions int ParseArguments(int argc, char **argv, DrvrParams ¶ms); -void Help(); +void Help(std::ostream &os = std::cout); int ValidateInputs(const DrvrParams ¶ms); diff --git a/app/src/main.cpp b/app/src/main.cpp index 74f10de..61a6af1 100644 --- a/app/src/main.cpp +++ b/app/src/main.cpp @@ -96,24 +96,24 @@ int ParseArguments(int argc, char **argv, DrvrParams ¶ms) { /******************************************************************************* * Print help instructions to the terminal + * + * @param[in] os Output stream for writing; defaults to `std::cout` ******************************************************************************/ -void Help() { +void Help(std::ostream &os) { // TODO-TEMPLATE: Update driver help message - std::cout << std::endl - << "Usage: .\\ [Options]" << std::endl; - std::cout << "Options (not case sensitive)" << std::endl; - std::cout << "\t-i :: Input file name" << std::endl; - std::cout << "\t-t :: Terrain file name" << std::endl; - std::cout << "\t-o :: Output file name" << std::endl; - std::cout << "\t-dbg :: Dump intermediate values to output file [optional]" - << std::endl; - std::cout << std::endl << "Examples:" << std::endl; - std::cout << "\t[WINDOWS] " << DRIVER_NAME - << ".exe -i inputs.txt -t terrain.txt -o results.txt" - << std::endl; - std::cout << "\t[LINUX] .\\" << DRIVER_NAME - << " -i inputs.txt -t terrain.txt -o results.txt" << std::endl; - std::cout << std::endl; + os << std::endl << "Usage: .\\ [Options]" << std::endl; + os << "Options (not case sensitive)" << std::endl; + os << "\t-i :: Input file name" << std::endl; + os << "\t-t :: Terrain file name" << std::endl; + os << "\t-o :: Output file name" << std::endl; + os << "\t-dbg :: Dump intermediate values to output file [optional]" + << std::endl; + os << std::endl << "Examples:" << std::endl; + os << "\t[WINDOWS] " << DRIVER_NAME + << ".exe -i inputs.txt -t terrain.txt -o results.txt" << std::endl; + os << "\t[LINUX] .\\" << DRIVER_NAME + << " -i inputs.txt -t terrain.txt -o results.txt" << std::endl; + os << std::endl; }; /******************************************************************************* From b1c2450f68a1fcab408b2e81d4084e0a703ac047 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:50:39 -0400 Subject: [PATCH 080/379] Update driver help message --- app/src/main.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/app/src/main.cpp b/app/src/main.cpp index 0dd8cdf..15ddd61 100644 --- a/app/src/main.cpp +++ b/app/src/main.cpp @@ -174,19 +174,16 @@ int ParseArguments(int argc, char **argv, DrvrParams ¶ms) { * @param[in] os Output stream for writing; defaults to `std::cout` ******************************************************************************/ void Help(std::ostream &os) { - // TODO-TEMPLATE: Update driver help message os << std::endl << "Usage: .\\ [Options]" << std::endl; os << "Options (not case sensitive)" << std::endl; - os << "\t-i :: Input file name" << std::endl; - os << "\t-t :: Terrain file name" << std::endl; - os << "\t-o :: Output file name" << std::endl; - os << "\t-dbg :: Dump intermediate values to output file [optional]" - << std::endl; + os << "\t-i :: Input file name" << std::endl; + os << "\t-o :: Output file name" << std::endl; + os << "\t-model :: Model to run [HGTCM, TSM, ASM]" << std::endl; os << std::endl << "Examples:" << std::endl; os << "\t[WINDOWS] " << DRIVER_NAME - << ".exe -i inputs.txt -t terrain.txt -o results.txt" << std::endl; + << ".exe -i inputs.txt -o results.txt -model ASM" << std::endl; os << "\t[LINUX] .\\" << DRIVER_NAME - << " -i inputs.txt -t terrain.txt -o results.txt" << std::endl; + << " -i inputs.txt -o results.txt -model ASM" << std::endl; os << std::endl; }; From f4139c1312b2f92bd558d3f105dff02c468c72c8 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 15 Oct 2024 14:17:21 -0400 Subject: [PATCH 081/379] Pass strings directly to parsers --- app/src/AeronauticalStatisticalModel.cpp | 8 +++----- app/src/HeightGainTerminalCorrectionModel.cpp | 15 +++++---------- app/src/TerrestrialStatisticalModel.cpp | 8 +++----- 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/app/src/AeronauticalStatisticalModel.cpp b/app/src/AeronauticalStatisticalModel.cpp index 206a27c..7693676 100644 --- a/app/src/AeronauticalStatisticalModel.cpp +++ b/app/src/AeronauticalStatisticalModel.cpp @@ -56,17 +56,15 @@ int ParseASMInputFile(const std::string &in_file, ASMParams &asm_params) { }); if (key.compare(TAG__FREQ) == 0) { - if (ParseDouble(value.c_str(), asm_params.f__ghz) - == DRVRERR__PARSE) { + if (ParseDouble(value, asm_params.f__ghz) == DRVRERR__PARSE) { return ParsingErrorHelper(DRVRERR__PARSE_FREQ, TAG__FREQ); } } else if (key.compare(TAG__THETA) == 0) { - if (ParseDouble(value.c_str(), asm_params.theta__deg) - == DRVRERR__PARSE) { + if (ParseDouble(value, asm_params.theta__deg) == DRVRERR__PARSE) { return ParsingErrorHelper(DRVRERR__PARSE_THETA, TAG__THETA); } } else if (key.compare(TAG__PERCENTAGE) == 0) { - if (ParseDouble(value.c_str(), asm_params.p) == DRVRERR__PARSE) { + if (ParseDouble(value, asm_params.p) == DRVRERR__PARSE) { return ParsingErrorHelper( DRVRERR__PARSE_PERCENTAGE, TAG__PERCENTAGE ); diff --git a/app/src/HeightGainTerminalCorrectionModel.cpp b/app/src/HeightGainTerminalCorrectionModel.cpp index 6f2bda7..394ff61 100644 --- a/app/src/HeightGainTerminalCorrectionModel.cpp +++ b/app/src/HeightGainTerminalCorrectionModel.cpp @@ -61,33 +61,28 @@ int ParseHGTCInputFile(const std::string &in_file, HGTCParams &hgtc_params) { }); if (key.compare(TAG__FREQ) == 0) { - if (ParseDouble(value.c_str(), hgtc_params.f__ghz) - == DRVRERR__PARSE) { + if (ParseDouble(value, hgtc_params.f__ghz) == DRVRERR__PARSE) { return ParsingErrorHelper(DRVRERR__PARSE_FREQ, TAG__FREQ); } } else if (key.compare(TAG__HEIGHT) == 0) { - if (ParseDouble(value.c_str(), hgtc_params.h__meter) - == DRVRERR__PARSE) { + if (ParseDouble(value, hgtc_params.h__meter) == DRVRERR__PARSE) { return ParsingErrorHelper(DRVRERR__PARSE_HEIGHT, TAG__HEIGHT); } } else if (key.compare(TAG__STREET_WIDTH) == 0) { - if (ParseDouble(value.c_str(), hgtc_params.w_s__meter) - == DRVRERR__PARSE) { + if (ParseDouble(value, hgtc_params.w_s__meter) == DRVRERR__PARSE) { return ParsingErrorHelper( DRVRERR__PARSE_STREET_WIDTH, TAG__STREET_WIDTH ); } } else if (key.compare(TAG__REPR_HEIGHT) == 0) { - if (ParseDouble(value.c_str(), hgtc_params.R__meter) - == DRVRERR__PARSE) { + if (ParseDouble(value, hgtc_params.R__meter) == DRVRERR__PARSE) { return ParsingErrorHelper( DRVRERR__PARSE_REPR_HEIGHT, TAG__REPR_HEIGHT ); } } else if (key.compare(TAG__CLUTTER_TYPE) == 0) { int clutter_type_int; - if (ParseInteger(value.c_str(), clutter_type_int) - == DRVRERR__PARSE) { + if (ParseInteger(value, clutter_type_int) == DRVRERR__PARSE) { return ParsingErrorHelper( DRVRERR__PARSE_CLUTTER_TYPE, TAG__CLUTTER_TYPE ); diff --git a/app/src/TerrestrialStatisticalModel.cpp b/app/src/TerrestrialStatisticalModel.cpp index ed43e30..ea11c74 100644 --- a/app/src/TerrestrialStatisticalModel.cpp +++ b/app/src/TerrestrialStatisticalModel.cpp @@ -56,19 +56,17 @@ int ParseTSMInputFile(const std::string &in_file, TSMParams &tsm_params) { }); if (key.compare(TAG__FREQ) == 0) { - if (ParseDouble(value.c_str(), tsm_params.f__ghz) - == DRVRERR__PARSE) { + if (ParseDouble(value, tsm_params.f__ghz) == DRVRERR__PARSE) { return ParsingErrorHelper(DRVRERR__PARSE_FREQ, TAG__FREQ); } } else if (key.compare(TAG__PATH_DIST) == 0) { - if (ParseDouble(value.c_str(), tsm_params.d__km) - == DRVRERR__PARSE) { + if (ParseDouble(value, tsm_params.d__km) == DRVRERR__PARSE) { return ParsingErrorHelper( DRVRERR__PARSE_PATH_DIST, TAG__PATH_DIST ); } } else if (key.compare(TAG__PERCENTAGE) == 0) { - if (ParseDouble(value.c_str(), tsm_params.p) == DRVRERR__PARSE) { + if (ParseDouble(value, tsm_params.p) == DRVRERR__PARSE) { return ParsingErrorHelper( DRVRERR__PARSE_PERCENTAGE, TAG__PERCENTAGE ); From 16849f59c6fead4dacca66a0a63fbf8fce4e6408 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:07:43 -0400 Subject: [PATCH 082/379] Add CSV iterator class --- app/include/CommaSeparatedIterator.h | 28 +++++++++++ app/src/CommaSeparatedIterator.cpp | 69 ++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 app/include/CommaSeparatedIterator.h create mode 100644 app/src/CommaSeparatedIterator.cpp diff --git a/app/include/CommaSeparatedIterator.h b/app/include/CommaSeparatedIterator.h new file mode 100644 index 0000000..d283a75 --- /dev/null +++ b/app/include/CommaSeparatedIterator.h @@ -0,0 +1,28 @@ +#pragma once + +#include // for transform +#include // for std::tolower +#include // for std::istream +#include // For std::string +#include // for std::pair + +/******************************************************************************* + * @class CommaSeparatedIterator + * An iterator that reads lines from an input stream, splitting each line + * into two strings based on a comma delimiter. + * + * This iterator can work with both `std::stringstream` and `std::ifstream`. + ******************************************************************************/ +class CommaSeparatedIterator { + public: + using value_type = std::pair; + CommaSeparatedIterator(std::istream &stream); + CommaSeparatedIterator &operator++(); + value_type operator*() const; + explicit operator bool() const; + private: + std::istream &stream_; + std::string line_; + std::string first_; + std::string second_; +}; diff --git a/app/src/CommaSeparatedIterator.cpp b/app/src/CommaSeparatedIterator.cpp new file mode 100644 index 0000000..24539bf --- /dev/null +++ b/app/src/CommaSeparatedIterator.cpp @@ -0,0 +1,69 @@ +#include "CommaSeparatedIterator.h" + +#include // for std::ptrdiff_t +#include // for std::input_iterator_tag, + +CommaSeparatedIterator::CommaSeparatedIterator(std::istream &stream): + stream_(stream) { + ++(*this); // Move to the first line +} + +/*********************************************************************** + * Pre-increment operator. + * + * Advances the iterator to the next line and splits it into two substrings. + * If the end of the stream is reached, both substrings will be empty. Both + * parsed substrings are converted to lowercase. + * + * @return A reference to the updated iterator. + **********************************************************************/ +CommaSeparatedIterator &CommaSeparatedIterator::operator++() { + if (std::getline(stream_, line_)) { + size_t pos = line_.find(','); + if (pos != std::string::npos) { + first_ = line_.substr(0, pos); + second_ = line_.substr(pos + 1); + } else { + first_ = line_; + second_ = ""; + } + // Convert both substrings to lowercase + std::transform( + first_.begin(), + first_.end(), + first_.begin(), + [](const char c) { return static_cast(std::tolower(c)); } + ); + std::transform( + second_.begin(), + second_.end(), + second_.begin(), + [](const char c) { return static_cast(std::tolower(c)); } + ); + } else { + first_ = second_ = ""; // End of stream + } + return *this; +} + +/*********************************************************************** + * Dereference operator. + * + * Returns the current pair of substrings (first and second). + * + * @return A pair containing the two substrings from the current line. + **********************************************************************/ +CommaSeparatedIterator::value_type CommaSeparatedIterator::operator*() const { + return {first_, second_}; +} + +/*********************************************************************** + * Conversion to boolean. + * + * Checks if the iterator is still valid (not at the end of the input). + * + * @return True if there are still lines to read, otherwise false. + **********************************************************************/ +CommaSeparatedIterator::operator bool() const { + return !line_.empty(); +} \ No newline at end of file From 8124a5ae2cc8202a31bddb874bff4f48ba19973b Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:08:23 -0400 Subject: [PATCH 083/379] Use CSV iterator class --- app/include/Driver.h | 16 +++--- app/include/Errors.h | 1 + app/src/AeronauticalStatisticalModel.cpp | 54 +++++++++---------- app/src/CMakeLists.txt | 15 +++--- app/src/HeightGainTerminalCorrectionModel.cpp | 54 +++++++++---------- app/src/TerrestrialStatisticalModel.cpp | 54 +++++++++---------- 6 files changed, 92 insertions(+), 102 deletions(-) diff --git a/app/include/Driver.h b/app/include/Driver.h index 3b0de35..5962201 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -3,6 +3,7 @@ */ #pragma once +#include "CommaSeparatedIterator.h" #include "Errors.h" #include "ITS.ITU.PSeries.P2108/P2108.h" #include "Structs.h" @@ -15,6 +16,7 @@ #include // for setw #include // for cerr, cout, ostream #include // for string, stoi, stod +#include // for std::tie #include ///////////////////////////// @@ -34,27 +36,21 @@ int ValidateInputs(const DrvrParams ¶ms); // Aeronautical Statistical Model int CallAeronauticalStatisticalModel( - const DrvrParams ¶ms, - ASMParams &asm_params, - std::vector &L_ces__db + ASMParams &asm_params, std::vector &L_ces__db ); int ParseASMInputFile(const std::string &in_file, ASMParams &asm_params); void WriteASMInputs(std::ofstream &fp, const ASMParams ¶ms); // Height Gain Terminal Correction Model int CallHeightGainTerminalCorrectionModel( - const DrvrParams ¶ms, - HGTCParams &hgtc_params, - std::vector &A_h__db + HGTCParams &hgtc_params, std::vector &A_h__db ); int ParseHGTCInputFile(const std::string &in_file, HGTCParams &hgtc_params); void WriteHGTCInputs(std::ofstream &fp, const HGTCParams ¶ms); // Terrestrial Statistical Model int CallTerrestrialStatisticalModel( - const DrvrParams ¶ms, - TSMParams &tsm_params, - std::vector &L_ctt__db + TSMParams &tsm_params, std::vector &L_ctt__db ); int ParseTSMInputFile(const std::string &in_file, TSMParams &tsm_params); void WriteTSMInputs(std::ofstream &fp, const TSMParams ¶ms); @@ -70,4 +66,4 @@ int Validate_RequiredErrMsgHelper(const std::string &opt, const int err); int ParseInteger(const std::string &str, int &value); int ParseDouble(const std::string &str, double &value); int ParsingErrorHelper(const int err, const std::string &msg); -std::string GetDatetimeString(); \ No newline at end of file +std::string GetDatetimeString(); diff --git a/app/include/Errors.h b/app/include/Errors.h index 3e4a086..0de3806 100644 --- a/app/include/Errors.h +++ b/app/include/Errors.h @@ -13,6 +13,7 @@ /** Primary Return Codes (1000-1099) */ #define DRVR__RETURN_SUCCESS 1000 #define DRVRERR__INVALID_OPTION 1003 +#define DRVRERR__OPENING_INPUT_FILE 1006 #define DRVRERR__OPENING_OUTPUT_FILE 1007 /** Input File Parsing Errors (1100-1199) */ diff --git a/app/src/AeronauticalStatisticalModel.cpp b/app/src/AeronauticalStatisticalModel.cpp index 7693676..8a4dce2 100644 --- a/app/src/AeronauticalStatisticalModel.cpp +++ b/app/src/AeronauticalStatisticalModel.cpp @@ -8,22 +8,14 @@ /******************************************************************************* * Top-level control function for Aeronautical Statistical Model operation * - * @param[in] params Driver input parameter struct * @param[out] asm_params Aeronautical Statistical Model input parameter struct * @param[out] L_ces__db Basic transmission loss, in dB * @return Return code ******************************************************************************/ int CallAeronauticalStatisticalModel( - const DrvrParams ¶ms, - ASMParams &asm_params, - std::vector &L_ces__db + ASMParams &asm_params, std::vector &L_ces__db ) { - // Parse input file and populate asm_params struct - int rtn = ParseASMInputFile(params.in_file, asm_params); - if (rtn != SUCCESS) { - return rtn; - } - + int rtn; double L_ces; rtn = AeronauticalStatisticalModel( asm_params.f__ghz, asm_params.theta__deg, asm_params.p, L_ces @@ -34,27 +26,17 @@ int CallAeronauticalStatisticalModel( } /******************************************************************************* - * Parse Aeronautical Statistical Model input parameter file + * Parse input stream (file or string stream) to ASM parameter struct. * - * @param[in] in_file Path to ASM input parameter file + * @param[in] stream Path to ASM input parameter file * @param[out] asm_params ASM input parameter struct * @return Return code ******************************************************************************/ -int ParseASMInputFile(const std::string &in_file, ASMParams &asm_params) { - std::ifstream file; - file.open(in_file.c_str()); - std::string line; - - while (std::getline(file, line)) { - size_t i = line.find(","); - - std::string key = line.substr(0, i); - std::string value = line.substr(i + 1); - - std::transform(key.begin(), key.end(), key.begin(), [](const char c) { - return static_cast(std::tolower(c)); - }); - +int ParseASMInputStream(std::istream &stream, ASMParams &asm_params) { + CommaSeparatedIterator it(stream); + std::string key, value; + while (it) { + std::tie(key, value) = *it; if (key.compare(TAG__FREQ) == 0) { if (ParseDouble(value, asm_params.f__ghz) == DRVRERR__PARSE) { return ParsingErrorHelper(DRVRERR__PARSE_FREQ, TAG__FREQ); @@ -75,11 +57,27 @@ int ParseASMInputFile(const std::string &in_file, ASMParams &asm_params) { << std::endl; return DRVRERR__PARSE; } + ++it; } - file.close(); return SUCCESS; } +/******************************************************************************* + * Parse Aeronautical Statistical Model input parameter file + * + * @param[in] in_file Path to ASM input parameter file + * @param[out] asm_params ASM input parameter struct + * @return Return code + ******************************************************************************/ +int ParseASMInputFile(const std::string &in_file, ASMParams &asm_params) { + std::ifstream file(in_file); + if (!file) { + std::cerr << "Failed to open file " << in_file << std::endl; + return DRVRERR__OPENING_INPUT_FILE; + } + return ParseASMInputStream(file, asm_params); +} + /******************************************************************************* * Write Aeronautical Statistical Model inputs to the report file * diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index 41b1b16..ef772f4 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -1,18 +1,17 @@ ########################################### ## BUILD THE DRIVER ########################################### -set(DRIVER_NAME "${LIB_NAME}Driver") -set(DRIVER_VERSION ${PROJECT_VERSION}) # May be set manually instead -set(DRIVER_HEADERS "${PROJECT_SOURCE_DIR}/app/include") - +# Driver name set in CMakeLists.txt one level above this one. add_executable( ${DRIVER_NAME} "AeronauticalStatisticalModel.cpp" + "CommaSeparatedIterator.cpp" "DriverUtils.cpp" "HeightGainTerminalCorrectionModel.cpp" "main.cpp" "Reporting.cpp" "TerrestrialStatisticalModel.cpp" + "${DRIVER_HEADERS}/CommaSeparatedIterator.h" "${DRIVER_HEADERS}/Driver.h" "${DRIVER_HEADERS}/Errors.h" "${DRIVER_HEADERS}/Labels.h" @@ -46,13 +45,13 @@ set_target_properties( if (BUILD_32BIT) set_target_properties( ${DRIVER_NAME} PROPERTIES - DEBUG_POSTFIX "x86" - RELEASE_POSTFIX "x86" + DEBUG_POSTFIX "_x86" + RELEASE_POSTFIX "_x86" ) else () set_target_properties( ${DRIVER_NAME} PROPERTIES - DEBUG_POSTFIX "x64" - RELEASE_POSTFIX "x64" + DEBUG_POSTFIX "_x64" + RELEASE_POSTFIX "_x64" ) endif () \ No newline at end of file diff --git a/app/src/HeightGainTerminalCorrectionModel.cpp b/app/src/HeightGainTerminalCorrectionModel.cpp index 394ff61..ecf1d16 100644 --- a/app/src/HeightGainTerminalCorrectionModel.cpp +++ b/app/src/HeightGainTerminalCorrectionModel.cpp @@ -8,22 +8,14 @@ /******************************************************************************* * Top-level control function for Height Gain Terminal Correction Model * - * @param[in] params Driver input parameter struct * @param[out] hgtc_params Height Gain Terminal Correction Model input struct * @param[out] A_h__db Additional loss (clutter loss), in dB * @return Return code ******************************************************************************/ int CallHeightGainTerminalCorrectionModel( - const DrvrParams ¶ms, - HGTCParams &hgtc_params, - std::vector &A_h__db + HGTCParams &hgtc_params, std::vector &A_h__db ) { - // Parse input file and populate hgtc_params struct - int rtn = ParseHGTCInputFile(params.in_file, hgtc_params); - if (rtn != SUCCESS) { - return rtn; - } - + int rtn; double A_h; rtn = HeightGainTerminalCorrectionModel( hgtc_params.f__ghz, @@ -39,27 +31,17 @@ int CallHeightGainTerminalCorrectionModel( } /******************************************************************************* - * Parse Height Gain Terminal Correction Model input parameter file + * Parse input stream (file or string stream) to HGTC parameter struct. * - * @param[in] in_file Path to HGTC input parameter file + * @param[in] stream Path to ASM input parameter file * @param[out] hgtc_params HGTC input parameter struct * @return Return code ******************************************************************************/ -int ParseHGTCInputFile(const std::string &in_file, HGTCParams &hgtc_params) { - std::ifstream file; - file.open(in_file.c_str()); - std::string line; - - while (std::getline(file, line)) { - size_t i = line.find(","); - - std::string key = line.substr(0, i); - std::string value = line.substr(i + 1); - - std::transform(key.begin(), key.end(), key.begin(), [](const char c) { - return static_cast(std::tolower(c)); - }); - +int ParseHGTCInputStream(std::istream &stream, HGTCParams &hgtc_params) { + CommaSeparatedIterator it(stream); + std::string key, value; + while (it) { + std::tie(key, value) = *it; if (key.compare(TAG__FREQ) == 0) { if (ParseDouble(value, hgtc_params.f__ghz) == DRVRERR__PARSE) { return ParsingErrorHelper(DRVRERR__PARSE_FREQ, TAG__FREQ); @@ -95,11 +77,27 @@ int ParseHGTCInputFile(const std::string &in_file, HGTCParams &hgtc_params) { << std::endl; return DRVRERR__PARSE; } + ++it; } - file.close(); return SUCCESS; } +/******************************************************************************* + * Parse Height Gain Terminal Correction Model input parameter file + * + * @param[in] in_file Path to HGTC input parameter file + * @param[out] hgtc_params HGTC input parameter struct + * @return Return code + ******************************************************************************/ +int ParseHGTCInputFile(const std::string &in_file, HGTCParams &hgtc_params) { + std::ifstream file(in_file); + if (!file) { + std::cerr << "Failed to open file " << in_file << std::endl; + return DRVRERR__OPENING_INPUT_FILE; + } + return ParseHGTCInputStream(file, hgtc_params); +} + /******************************************************************************* * Write Height Gain Terminal Correction Model inputs to the report file * diff --git a/app/src/TerrestrialStatisticalModel.cpp b/app/src/TerrestrialStatisticalModel.cpp index ea11c74..2e00551 100644 --- a/app/src/TerrestrialStatisticalModel.cpp +++ b/app/src/TerrestrialStatisticalModel.cpp @@ -8,22 +8,14 @@ /******************************************************************************* * Top-level control function for Terrestrial Statistical Model operation * - * @param[in] params Driver input parameter struct * @param[out] tsm_params Terrestrial Statistical Model input parameter struct * @param[out] L_ctt__db Additional loss (clutter loss), in dB * @return Return code ******************************************************************************/ int CallTerrestrialStatisticalModel( - const DrvrParams ¶ms, - TSMParams &tsm_params, - std::vector &L_ctt__db + TSMParams &tsm_params, std::vector &L_ctt__db ) { - // Parse input file and populate tsm_params struct - int rtn = ParseTSMInputFile(params.in_file, tsm_params); - if (rtn != SUCCESS) { - return rtn; - } - + int rtn; double L_ctt; rtn = TerrestrialStatisticalModel( tsm_params.f__ghz, tsm_params.d__km, tsm_params.p, L_ctt @@ -34,27 +26,17 @@ int CallTerrestrialStatisticalModel( } /******************************************************************************* - * Parse Terrestrial Statistical Model input parameter file + * Parse input stream (file or string stream) to TSM parameter struct. * - * @param[in] in_file Path to TSM input parameter file + * @param[in] stream Path to TSM input parameter file * @param[out] tsm_params TSM input parameter struct * @return Return code ******************************************************************************/ -int ParseTSMInputFile(const std::string &in_file, TSMParams &tsm_params) { - std::ifstream file; - file.open(in_file.c_str()); - std::string line; - - while (std::getline(file, line)) { - size_t i = line.find(","); - - std::string key = line.substr(0, i); - std::string value = line.substr(i + 1); - - std::transform(key.begin(), key.end(), key.begin(), [](const char c) { - return static_cast(std::tolower(c)); - }); - +int ParseTSMInputStream(std::istream &stream, TSMParams &tsm_params) { + CommaSeparatedIterator it(stream); + std::string key, value; + while (it) { + std::tie(key, value) = *it; if (key.compare(TAG__FREQ) == 0) { if (ParseDouble(value, tsm_params.f__ghz) == DRVRERR__PARSE) { return ParsingErrorHelper(DRVRERR__PARSE_FREQ, TAG__FREQ); @@ -77,11 +59,27 @@ int ParseTSMInputFile(const std::string &in_file, TSMParams &tsm_params) { << std::endl; return DRVRERR__PARSE; } + ++it; } - file.close(); return SUCCESS; } +/******************************************************************************* + * Parse Terrestrial Statistical Model input parameter file + * + * @param[in] in_file Path to TSM input parameter file + * @param[out] tsm_params TSM input parameter struct + * @return Return code + ******************************************************************************/ +int ParseTSMInputFile(const std::string &in_file, TSMParams &tsm_params) { + std::ifstream file(in_file); + if (!file) { + std::cerr << "Failed to open file " << in_file << std::endl; + return DRVRERR__OPENING_INPUT_FILE; + } + return ParseTSMInputStream(file, tsm_params); +} + /******************************************************************************* * Write Terrestrial Statistical Model inputs to the report file * From f5db9a782b8dd7f2f37d0e359f02f392d97f851a Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:08:33 -0400 Subject: [PATCH 084/379] Update main for CSV iterator usage --- app/src/main.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/app/src/main.cpp b/app/src/main.cpp index 15ddd61..d8e164b 100644 --- a/app/src/main.cpp +++ b/app/src/main.cpp @@ -36,17 +36,25 @@ int main(int argc, char **argv) { switch (params.model) { case P2108Model::HGTCM: - rtn = CallHeightGainTerminalCorrectionModel( - params, hgtc_params, loss__db - ); + rtn = ParseHGTCInputFile(params.in_file, hgtc_params); + if (rtn != SUCCESS) { + return rtn; + } + rtn = CallHeightGainTerminalCorrectionModel(hgtc_params, loss__db); break; case P2108Model::TSM: - rtn = CallTerrestrialStatisticalModel(params, tsm_params, loss__db); + rtn = ParseTSMInputFile(params.in_file, tsm_params); + if (rtn != SUCCESS) { + return rtn; + } + rtn = CallTerrestrialStatisticalModel(tsm_params, loss__db); break; case P2108Model::ASM: - rtn = CallAeronauticalStatisticalModel( - params, asm_params, loss__db - ); + rtn = ParseASMInputFile(params.in_file, asm_params); + if (rtn != SUCCESS) { + return rtn; + } + rtn = CallAeronauticalStatisticalModel(asm_params, loss__db); break; default: rtn = DRVRERR__VALIDATION_MODEL; From 872c36beb35103f83da839c6ee8118105940b040 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:08:49 -0400 Subject: [PATCH 085/379] Fix scope of cmake driver variables --- app/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 9cb4900..fb3cd29 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -1,3 +1,10 @@ +########################################### +## CONFIGURE THE COMMAND LINE DRIVER +########################################### +set(DRIVER_NAME "${LIB_NAME}Driver") +set(DRIVER_VERSION ${PROJECT_VERSION}) # May be set manually instead +set(DRIVER_HEADERS "${PROJECT_SOURCE_DIR}/app/include") + ########################################### ## BUILD THE COMMAND LINE DRIVER ########################################### From 17fb947c4f75d6fc2635a44123e24065fc820b80 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:09:20 -0400 Subject: [PATCH 086/379] Make driver test a simple executable --- app/tests/CMakeLists.txt | 32 ++++++++++++------------ app/tests/TestDriver.cpp | 45 ++++++++++++++++++++++++++++++++++ app/tests/TestDriver.h | 4 ++- app/tests/TestDriverUtils.cpp | 46 ----------------------------------- 4 files changed, 65 insertions(+), 62 deletions(-) create mode 100644 app/tests/TestDriver.cpp delete mode 100644 app/tests/TestDriverUtils.cpp diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index 2708a39..70aef4e 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -1,31 +1,33 @@ ############################################ ## CONFIGURE COMMAND LINE DRIVER TESTS ############################################ -set(DRIVER_TEST_NAME "Test${LIB_NAME}Driver") +set(DRIVER_TEST_NAME "Test${DRIVER_NAME}") add_executable( ${DRIVER_TEST_NAME} - "TestDriverUtils.cpp" + "TestDriver.cpp" "TestDriver.h" + "${DRIVER_HEADERS}/Driver.h" ) -# Add the include directory -target_include_directories(${DRIVER_TEST_NAME} PUBLIC "${DRIVER_HEADERS}") +# Add the include directories +target_include_directories( + ${DRIVER_TEST_NAME} PUBLIC + "${DRIVER_HEADERS}" + "${PROJECT_SOURCE_DIR}/app/tests/" +) + +# Link the library to the executable +target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME}) + +# Make driver executable location available to source +add_definitions(-DDRIVER_LOCATION="${PROJECT_SOURCE_DIR}/bin") +add_definitions(-DDRIVER_NAME="${DRIVER_NAME}") +add_definitions(-DDRIVER_DATA_LOCATION="${PROJECT_SOURCE_DIR}/app/data") -########################################### -## SET UP AND DISCOVER TESTS -########################################### -if (NOT ${GOOGLETEST_INITIALIZED}) # Avoids duplicate initialization - set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - add_subdirectory("${PROJECT_SOURCE_DIR}/extern/googletest" "extern/googletest") - include(GoogleTest) -endif() -include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) set_target_properties( ${DRIVER_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(${DRIVER_TEST_NAME} ${DRIVER_NAME} GTest::gtest_main) -gtest_discover_tests(${DRIVER_TEST_NAME}) \ No newline at end of file diff --git a/app/tests/TestDriver.cpp b/app/tests/TestDriver.cpp new file mode 100644 index 0000000..69a9f61 --- /dev/null +++ b/app/tests/TestDriver.cpp @@ -0,0 +1,45 @@ +#include "TestDriver.h" + +std::string joinArguments(const std::vector &args) { + std::ostringstream oss; + for (std::string arg : args) { + oss << arg + " "; + } + return oss.str(); +} + +void appendDirectorySep(std::string &str) { +#ifdef _WIN32 + str += "\\"; +#else + str += "/"; +#endif +} + +int main() { + // Driver executable needs to be in the same directory as this executable + std::string executable = std::string(DRIVER_NAME); + +#ifdef _WIN32 + executable += ".exe"; +#endif + + std::string command; + int rtn; + + // Input files must exist in the directory of this executable + std::vector> argSet = { + {"-i", "i_hgtcm.txt", "-o", "o_hgtcm.txt", "-model", "HGTCM"}, + {"-i", "i_tsm.txt", "-o", "o_tsm.txt", "-model", "TSM"}, + {"-i", "i_asm.txt", "-o", "o_asm.txt", "-model", "ASM"}, + }; + + for (auto args : argSet) { + command = executable + " " + joinArguments(args); + std::cout << "Running command: " << command << std::endl; + rtn = std::system(command.c_str()); + std::cout << "Return code: " << rtn << std::endl; + } + + return SUCCESS; +} \ No newline at end of file diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index 987ae95..19e927b 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -5,4 +5,6 @@ #include "Driver.h" -#include +#include // for std::system +#include // for std::ostringstream +#include // for std::string diff --git a/app/tests/TestDriverUtils.cpp b/app/tests/TestDriverUtils.cpp deleted file mode 100644 index 75ec489..0000000 --- a/app/tests/TestDriverUtils.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "TestDriver.h" - -TEST(ParseIntegerTest, TestParseIntegerSuccess) { - int value; - std::string toParse = "42"; - int rtn = ParseInteger(toParse, value); - EXPECT_EQ(rtn, SUCCESS); - EXPECT_EQ(value, 42); -} - -TEST(ParseIntegerTest, TestParseIntegerFailure1) { - int value; - std::string toParse = "42.0"; - int rtn = ParseInteger(toParse, value); - EXPECT_EQ(rtn, DRVRERR__PARSE); -} - -TEST(ParseIntegerTest, TestParseIntegerFailure2) { - int value; - std::string toParse = "notAnInteger"; - int rtn = ParseInteger(toParse, value); - EXPECT_EQ(rtn, DRVRERR__PARSE); -} - -TEST(ParseDoubleTest, TestParseDoubleSuccess1) { - double value; - std::string toParse = "42.0"; - int rtn = ParseDouble(toParse, value); - EXPECT_EQ(rtn, SUCCESS); - EXPECT_DOUBLE_EQ(value, 42.0); -} - -TEST(ParseDoubleTest, TestParseDoubleSuccess2) { - double value; - std::string toParse = "42"; - int rtn = ParseDouble(toParse, value); - EXPECT_EQ(rtn, SUCCESS); - EXPECT_DOUBLE_EQ(value, 42.0); -} - -TEST(ParseDoubleTest, TestParseDoubleFailure) { - double value; - std::string toParse = "notADouble"; - int rtn = ParseDouble(toParse, value); - EXPECT_EQ(rtn, DRVRERR__PARSE); -} From ea672a5b2f9a7bc309f56f1e5ffdbca293855f4c Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:23:31 -0400 Subject: [PATCH 087/379] Add CommaSeparatedIterator --- app/include/CommaSeparatedIterator.h | 31 +++++++++++++ app/include/Driver.h | 13 +++--- app/src/CMakeLists.txt | 1 + app/src/CommaSeparatedIterator.cpp | 69 ++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 app/include/CommaSeparatedIterator.h create mode 100644 app/src/CommaSeparatedIterator.cpp diff --git a/app/include/CommaSeparatedIterator.h b/app/include/CommaSeparatedIterator.h new file mode 100644 index 0000000..f1d6605 --- /dev/null +++ b/app/include/CommaSeparatedIterator.h @@ -0,0 +1,31 @@ +/** @file CommaSeparatedIterator.h + * Iterator class for reading comma-delimited input text streams. + */ +#pragma once + +#include // for transform +#include // for std::tolower +#include // for std::istream +#include // For std::string +#include // for std::pair + +/******************************************************************************* + * @class CommaSeparatedIterator + * An iterator that reads lines from an input stream, splitting each line + * into two strings based on a comma delimiter. + * + * This iterator can work with both `std::stringstream` and `std::ifstream`. + ******************************************************************************/ +class CommaSeparatedIterator { + public: + using value_type = std::pair; + CommaSeparatedIterator(std::istream &stream); + CommaSeparatedIterator &operator++(); + value_type operator*() const; + explicit operator bool() const; + private: + std::istream &stream_; + std::string line_; + std::string first_; + std::string second_; +}; diff --git a/app/include/Driver.h b/app/include/Driver.h index 9e09340..167d1e0 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -3,20 +3,19 @@ */ #pragma once +#include "CommaSeparatedIterator.h" #include "Errors.h" #include "Structs.h" // TODO-TEMPLATE: Include your library's main interface header // #include "ITS./.h" -#include // for transform -#include // for tolower -#include // for strlen #include // for localtime_s, localtime_r, time, time_t, tm, strftime -#include // for ifstream, ofstream -#include // for setw -#include // for cerr, cout, ostream -#include // for string, stoi, stod +#include // for std::ofstream +#include // for std::setw +#include // for std::cerr, std::cout, std::ostream +#include // for std::string, std::stoi, std::stod +#include // for std::tie ///////////////////////////// // Macros diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index 0c39ac2..cf2cc14 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -9,6 +9,7 @@ set(DRIVER_HEADERS "${PROJECT_SOURCE_DIR}/app/include") add_executable( ${DRIVER_NAME} "main.cpp" + "CommaSeparatedIterator.cpp" "DriverUtils.cpp" "${DRIVER_HEADERS}/Driver.h" "${DRIVER_HEADERS}/Errors.h" diff --git a/app/src/CommaSeparatedIterator.cpp b/app/src/CommaSeparatedIterator.cpp new file mode 100644 index 0000000..24539bf --- /dev/null +++ b/app/src/CommaSeparatedIterator.cpp @@ -0,0 +1,69 @@ +#include "CommaSeparatedIterator.h" + +#include // for std::ptrdiff_t +#include // for std::input_iterator_tag, + +CommaSeparatedIterator::CommaSeparatedIterator(std::istream &stream): + stream_(stream) { + ++(*this); // Move to the first line +} + +/*********************************************************************** + * Pre-increment operator. + * + * Advances the iterator to the next line and splits it into two substrings. + * If the end of the stream is reached, both substrings will be empty. Both + * parsed substrings are converted to lowercase. + * + * @return A reference to the updated iterator. + **********************************************************************/ +CommaSeparatedIterator &CommaSeparatedIterator::operator++() { + if (std::getline(stream_, line_)) { + size_t pos = line_.find(','); + if (pos != std::string::npos) { + first_ = line_.substr(0, pos); + second_ = line_.substr(pos + 1); + } else { + first_ = line_; + second_ = ""; + } + // Convert both substrings to lowercase + std::transform( + first_.begin(), + first_.end(), + first_.begin(), + [](const char c) { return static_cast(std::tolower(c)); } + ); + std::transform( + second_.begin(), + second_.end(), + second_.begin(), + [](const char c) { return static_cast(std::tolower(c)); } + ); + } else { + first_ = second_ = ""; // End of stream + } + return *this; +} + +/*********************************************************************** + * Dereference operator. + * + * Returns the current pair of substrings (first and second). + * + * @return A pair containing the two substrings from the current line. + **********************************************************************/ +CommaSeparatedIterator::value_type CommaSeparatedIterator::operator*() const { + return {first_, second_}; +} + +/*********************************************************************** + * Conversion to boolean. + * + * Checks if the iterator is still valid (not at the end of the input). + * + * @return True if there are still lines to read, otherwise false. + **********************************************************************/ +CommaSeparatedIterator::operator bool() const { + return !line_.empty(); +} \ No newline at end of file From 78f0561aee71090c7836130a256921bf3a1c9232 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:24:40 -0400 Subject: [PATCH 088/379] Relocate ctime include --- app/include/Driver.h | 1 - app/src/DriverUtils.cpp | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/include/Driver.h b/app/include/Driver.h index 167d1e0..ebd6e0c 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -10,7 +10,6 @@ // TODO-TEMPLATE: Include your library's main interface header // #include "ITS./.h" -#include // for localtime_s, localtime_r, time, time_t, tm, strftime #include // for std::ofstream #include // for std::setw #include // for std::cerr, std::cout, std::ostream diff --git a/app/src/DriverUtils.cpp b/app/src/DriverUtils.cpp index 080e734..da86e95 100644 --- a/app/src/DriverUtils.cpp +++ b/app/src/DriverUtils.cpp @@ -3,6 +3,8 @@ */ #include "Driver.h" +#include // for localtime_{s,r}, std::{time, time_t, tm, strftime} + /******************************************************************************* * Print version information to the specified output stream * From e3efa9732008b33453da52efeb1ee894a217adb2 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:25:40 -0400 Subject: [PATCH 089/379] Rename main.cpp to Driver.cpp --- app/src/CMakeLists.txt | 2 +- app/src/{main.cpp => Driver.cpp} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename app/src/{main.cpp => Driver.cpp} (99%) diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index cf2cc14..df03328 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -8,8 +8,8 @@ set(DRIVER_HEADERS "${PROJECT_SOURCE_DIR}/app/include") ## source/header files already included in `add_library()` in `src/CMakeLists.txt` add_executable( ${DRIVER_NAME} - "main.cpp" "CommaSeparatedIterator.cpp" + "Driver.cpp" "DriverUtils.cpp" "${DRIVER_HEADERS}/Driver.h" "${DRIVER_HEADERS}/Errors.h" diff --git a/app/src/main.cpp b/app/src/Driver.cpp similarity index 99% rename from app/src/main.cpp rename to app/src/Driver.cpp index 61a6af1..47b347f 100644 --- a/app/src/main.cpp +++ b/app/src/Driver.cpp @@ -1,4 +1,4 @@ -/** @file main.cpp +/** @file Driver.cpp * Implements the main function of the executable, and other high-level functions */ #include "Driver.h" From 63e3de783dcc838acac69f8c7c112a7a63f857cb Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:25:48 -0400 Subject: [PATCH 090/379] Add input file opening error code --- app/include/Errors.h | 1 + 1 file changed, 1 insertion(+) diff --git a/app/include/Errors.h b/app/include/Errors.h index 81205f4..d8c35cd 100644 --- a/app/include/Errors.h +++ b/app/include/Errors.h @@ -16,6 +16,7 @@ /** Primary Return Codes (1000-1099) */ #define DRVR__RETURN_SUCCESS 1000 #define DRVRERR__INVALID_OPTION 1003 +#define DRVRERR__OPENING_INPUT_FILE 1006 #define DRVRERR__OPENING_OUTPUT_FILE 1007 /** Input File Parsing Errors (1100-1199) */ From 19eb5e4b103a5a3256beed82e13ae5fca9df931c Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:25:57 -0400 Subject: [PATCH 091/379] Better use of cmake variable --- app/tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index 2dd7a70..1503acb 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -1,7 +1,7 @@ ############################################ ## CONFIGURE COMMAND LINE DRIVER TESTS ############################################ -set(DRIVER_TEST_NAME "Test${LIB_NAME}Driver") +set(DRIVER_TEST_NAME "Test${DRIVER_NAME}") ## TODO-TEMPLATE: Include source AND header files for tests here. add_executable( From 53fbd0f07f053262da88ba391de3d9b8042b4e2c Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:26:26 -0400 Subject: [PATCH 092/379] Reformat cmakelists remove unused variable --- tests/CMakeLists.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 74aa7dd..af6072a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -15,6 +15,13 @@ add_executable( "TestUtils.h" ) +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" +) + ########################################### ## SET UP AND DISCOVER TESTS ########################################### @@ -22,12 +29,5 @@ 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(GOOGLETEST_INITIALIZED ON) -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 From 3545807c9606db8e505925730e30ce1fbfbe74a7 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:30:23 -0400 Subject: [PATCH 093/379] Remove 32-bit configuration --- CMakeLists.txt | 1 - CMakePresets.json | 87 ++++++++++-------------------------------- app/src/CMakeLists.txt | 15 -------- src/CMakeLists.txt | 15 -------- 4 files changed, 20 insertions(+), 98 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 852b9f9..8bb5736 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,6 @@ option(RUN_DRIVER_TESTS "Test the driver executable" 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 diff --git a/CMakePresets.json b/CMakePresets.json index 5e0bee2..7b456bf 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -14,7 +14,6 @@ "cacheVariables": { "CMAKE_BUILD_RPATH_USE_ORIGIN": "ON", "DOCS_ONLY": "OFF", - "BUILD_32BIT": "OFF", "RUN_TESTS": "ON" } }, @@ -43,35 +42,17 @@ } }, { - "name": "debug64", - "displayName": "Debug, 64-bit", + "name": "debug", + "displayName": "Debug", "description": "Build library and tests with debug options, skip building docs", "inherits": "proplib-config-debug-base" }, { - "name": "release64", - "displayName": "Release, 64-bit", + "name": "release", + "displayName": "Release", "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", @@ -107,32 +88,18 @@ "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", + "name": "debug", "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" + "displayName": "Build Debug", + "description": "Build library and tests with debug options, skip building docs", + "configurePreset": "debug" }, { - "name": "release32", + "name": "release", "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" + "displayName": "Build Release", + "description": "Build library and tests with release options, and build docs", + "configurePreset": "release" }, { "name": "docsOnly", @@ -165,32 +132,18 @@ "description": "Base 'Release' test preset for ITS PropLib libraries" }, { - "name": "debug64", + "name": "debu", "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" + "displayName": "Test Debug", + "description": "Build library and tests with debug options, skip building docs", + "configurePreset": "debug" }, { - "name": "release32", + "name": "release", "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" + "displayName": "Test Release", + "description": "Build library and tests with release options, and build docs", + "configurePreset": "release" } ] } \ No newline at end of file diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index df03328..7ee1112 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -37,18 +37,3 @@ set_target_properties( ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" ) - -# Architecture-dependent configuration -if (BUILD_32BIT) - set_target_properties( - ${DRIVER_NAME} PROPERTIES - DEBUG_POSTFIX "x86" - RELEASE_POSTFIX "x86" - ) -else () - set_target_properties( - ${DRIVER_NAME} PROPERTIES - DEBUG_POSTFIX "x64" - RELEASE_POSTFIX "x64" - ) -endif () \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0efd2fe..e4dbba0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -31,21 +31,6 @@ set_target_properties( 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) From 706b5bf4a1ea5f7ad26c5f9c91ad953920e52081 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:30:36 -0400 Subject: [PATCH 094/379] Fix cmake variable scope --- app/CMakeLists.txt | 7 +++++++ app/src/CMakeLists.txt | 3 --- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 9cb4900..fb3cd29 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -1,3 +1,10 @@ +########################################### +## CONFIGURE THE COMMAND LINE DRIVER +########################################### +set(DRIVER_NAME "${LIB_NAME}Driver") +set(DRIVER_VERSION ${PROJECT_VERSION}) # May be set manually instead +set(DRIVER_HEADERS "${PROJECT_SOURCE_DIR}/app/include") + ########################################### ## BUILD THE COMMAND LINE DRIVER ########################################### diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index 7ee1112..8506864 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -1,9 +1,6 @@ ########################################### ## BUILD THE DRIVER ########################################### -set(DRIVER_NAME "${LIB_NAME}Driver") -set(DRIVER_VERSION ${PROJECT_VERSION}) # May be set manually instead -set(DRIVER_HEADERS "${PROJECT_SOURCE_DIR}/app/include") ## TODO-TEMPLATE: Include source AND header files here. Do not include ## source/header files already included in `add_library()` in `src/CMakeLists.txt` add_executable( From 86c552d544b92341fb733dd20d0150317f6637e0 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:31:19 -0400 Subject: [PATCH 095/379] Fix preset names in docs --- CONTRIBUTING.md | 2 +- README.md | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1693ac9..a193678 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -134,7 +134,7 @@ wrap/ 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. +CMakePresets.json # Presets for CMake, e.g. "release", "debug", etc. ... ``` diff --git a/README.md b/README.md index 0c1dfa9..0eeac96 100644 --- a/README.md +++ b/README.md @@ -50,12 +50,12 @@ provided for cross-platform builds, which can be carried out, for example, by: # From this repository's root directory, try one of the following command pairs: # "Release" configurations compile the library, build docs, and configure tests: -cmake --preset release64 -cmake --build --preset release64 +cmake --preset release +cmake --build --preset release # "Debug" configurations skip building the docs: -cmake --preset debug64 -cmake --build --preset debug64 +cmake --preset debug +cmake --build --preset debug # "DocsOnly" configurations only build the docs: cmake --preset docsOnly @@ -82,7 +82,7 @@ If you've configured tests when building the project, for example by using one o the "Release" or "Debug" CMake presets, you can run the included unit tests as follows: ```cmd -ctest --preset release64 +ctest --preset release ``` ## References ## From 3c43b5a96382dd196af3a14b579105fbc06bede2 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:33:19 -0400 Subject: [PATCH 096/379] Consistent file handling --- app/src/Driver.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index 47b347f..e72e247 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -33,9 +33,8 @@ int main(int argc, char **argv) { return rtn; // Print results to file - std::ofstream fp; - fp.open(params.out_file.c_str()); - if (!fp.is_open()) { + std::ofstream fp(params.out_file); + if (!fp) { std::cerr << "Error opening output file. Exiting." << std::endl; return DRVRERR__OPENING_OUTPUT_FILE; } From 7c7444c9194312b5b936ef0cf568c3e26163e3c7 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:34:17 -0400 Subject: [PATCH 097/379] Add CI workflows --- .github/workflows/ctest.yml | 58 +++++++++++++++++++++++++++++ .github/workflows/doxygen.yml | 70 +++++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 70 +++++++++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 .github/workflows/ctest.yml create mode 100644 .github/workflows/doxygen.yml create mode 100644 .github/workflows/release.yml 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 From f40a297b0e3614693cde3626758cc7fdc3c25d06 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:36:22 -0400 Subject: [PATCH 098/379] Update CI workflows for new preset names and removing 32-bit stuff --- .github/workflows/ctest.yml | 20 +++++--------------- .github/workflows/release.yml | 32 +++++++------------------------- 2 files changed, 12 insertions(+), 40 deletions(-) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index 75f0645..8fd1996 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -16,8 +16,7 @@ jobs: 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] + os: [ubuntu-latest, macos-latest, windows-latest] # CMake >= 3.21 is required to use "--preset " and discover generators cmakeVersion: ["3.21", latest] @@ -41,18 +40,9 @@ jobs: with: version: "1.11.0" - - name: "CMake: Build and Test (32-bit)" - if: matrix.os == 'windows-2019' + - name: "CMake: Build and Test" 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 + configurePreset: release + buildPreset: release + testPreset: release diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c369e5d..f9a25c9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macos-latest, windows-2019, windows-latest] + os: [ubuntu-latest, macos-latest, windows-latest] steps: - name: Checkout repository uses: actions/checkout@v4 @@ -24,21 +24,12 @@ jobs: - name: Install CMake # (latest stable version) uses: lukka/get-cmake@latest - - name: "CMake: Build (32-bit)" - if: matrix.os == 'windows-2019' + - name: "CMake: Build" uses: lukka/run-cmake@v10 with: - configurePreset: release32 + configurePreset: release 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 + buildPreset: release - name: Upload release artifact (macOS or Linux) if: runner.os != 'Windows' @@ -50,21 +41,12 @@ jobs: ${{ 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' + - name: Upload release artifact (Windows) + if: matrix.os == 'Windows' uses: actions/upload-artifact@v4 with: - name: release-windows-x64 + name: release-${{ matrix.os }} path: ${{ github.workspace }}\bin\Debug\*.dll if-no-files-found: error overwrite: true From 0dcabe15182e131e661130fa8f26111add162eaf Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:36:40 -0400 Subject: [PATCH 099/379] Rename main to Driver.cpp --- app/src/CMakeLists.txt | 2 +- app/src/{main.cpp => Driver.cpp} | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) rename app/src/{main.cpp => Driver.cpp} (98%) diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index ef772f4..9360eb5 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -6,9 +6,9 @@ add_executable( ${DRIVER_NAME} "AeronauticalStatisticalModel.cpp" "CommaSeparatedIterator.cpp" + "Driver.cpp" "DriverUtils.cpp" "HeightGainTerminalCorrectionModel.cpp" - "main.cpp" "Reporting.cpp" "TerrestrialStatisticalModel.cpp" "${DRIVER_HEADERS}/CommaSeparatedIterator.h" diff --git a/app/src/main.cpp b/app/src/Driver.cpp similarity index 98% rename from app/src/main.cpp rename to app/src/Driver.cpp index d8e164b..3b6586c 100644 --- a/app/src/main.cpp +++ b/app/src/Driver.cpp @@ -1,4 +1,4 @@ -/** @file main.cpp +/** @file Driver.cpp * Implements the main function of the executable, and other high-level functions */ #include "Driver.h" @@ -65,9 +65,8 @@ int main(int argc, char **argv) { return rtn; // Print results to file - std::ofstream fp; - fp.open(params.out_file.c_str()); - if (!fp.is_open()) { + std::ofstream fp(params.out_file); + if (!fp) { std::cerr << "Error opening output file. Exiting." << std::endl; return DRVRERR__OPENING_OUTPUT_FILE; } From 4bffa65e3f182ca79094d5ec1b1dc30c6fd47910 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:36:50 -0400 Subject: [PATCH 100/379] Header cleanup --- app/include/Driver.h | 12 ++++-------- app/src/DriverUtils.cpp | 2 ++ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/app/include/Driver.h b/app/include/Driver.h index 5962201..43b83a8 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -8,14 +8,10 @@ #include "ITS.ITU.PSeries.P2108/P2108.h" #include "Structs.h" -#include // for transform -#include // for tolower -#include // for strlen -#include // for localtime_s, localtime_r, time, time_t, tm, strftime -#include // for ifstream, ofstream -#include // for setw -#include // for cerr, cout, ostream -#include // for string, stoi, stod +#include // for std::ifstream, std::ofstream +#include // for std::setw +#include // for std::cerr, std::cout, std::ostream +#include // for std::string, std::stoi, std::stod #include // for std::tie #include diff --git a/app/src/DriverUtils.cpp b/app/src/DriverUtils.cpp index 080e734..05b17ca 100644 --- a/app/src/DriverUtils.cpp +++ b/app/src/DriverUtils.cpp @@ -3,6 +3,8 @@ */ #include "Driver.h" +#include // for localtime_s, localtime_r, time, time_t, tm, strftime + /******************************************************************************* * Print version information to the specified output stream * From f2129ec0c30f75d8c036729a376a6ba266bffa06 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:36:58 -0400 Subject: [PATCH 101/379] Add doxygen file header --- app/include/CommaSeparatedIterator.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/include/CommaSeparatedIterator.h b/app/include/CommaSeparatedIterator.h index d283a75..f1d6605 100644 --- a/app/include/CommaSeparatedIterator.h +++ b/app/include/CommaSeparatedIterator.h @@ -1,3 +1,6 @@ +/** @file CommaSeparatedIterator.h + * Iterator class for reading comma-delimited input text streams. + */ #pragma once #include // for transform From 70bf7671c8e491f10078a76eb5efe6f1cfa8437b Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:40:03 -0400 Subject: [PATCH 102/379] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0eeac96..aee3b26 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ This code repository is a template repository for software in the NTIA/ITS Propagation Library (PropLib). This template is intended for developers wishing to develop a cross-platform C++ library as part of PropLib. Instructions on how -to use this repository are found in [CREATING-REPOSITORIES.md](./CREATING-REPOSITORIES.md). +to use this repository are found in its [GitHub Wiki](https://github.com/NTIA/proplib-template/wiki). ## Getting Started ## From a8db36141b3f113910119d29348cc8aa7f15326c Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:42:48 -0400 Subject: [PATCH 103/379] clang-format --- app/src/Driver.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index 3b6586c..6ba3d9b 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -2,6 +2,7 @@ * Implements the main function of the executable, and other high-level functions */ #include "Driver.h" + #include "Labels.h" #include "Tags.h" From e8447defd53ca974a99aebc035debf48b9746e57 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:48:05 -0400 Subject: [PATCH 104/379] Remove unused definitions --- app/tests/CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index 70aef4e..51d8d7c 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -20,10 +20,8 @@ target_include_directories( # Link the library to the executable target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME}) -# Make driver executable location available to source -add_definitions(-DDRIVER_LOCATION="${PROJECT_SOURCE_DIR}/bin") +# Make driver executable name available to source add_definitions(-DDRIVER_NAME="${DRIVER_NAME}") -add_definitions(-DDRIVER_DATA_LOCATION="${PROJECT_SOURCE_DIR}/app/data") set_target_properties( ${DRIVER_TEST_NAME} PROPERTIES From 160cf652584f91bfe3ae95570ffd5e235106f300 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 17 Oct 2024 14:37:30 -0400 Subject: [PATCH 105/379] Improve CSV iterator --- app/src/CommaSeparatedIterator.cpp | 38 ++++++++++++++++++------------ 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/app/src/CommaSeparatedIterator.cpp b/app/src/CommaSeparatedIterator.cpp index 24539bf..cfcf1e4 100644 --- a/app/src/CommaSeparatedIterator.cpp +++ b/app/src/CommaSeparatedIterator.cpp @@ -1,7 +1,7 @@ #include "CommaSeparatedIterator.h" #include // for std::ptrdiff_t -#include // for std::input_iterator_tag, +#include // for std::input_iterator_tag CommaSeparatedIterator::CommaSeparatedIterator(std::istream &stream): stream_(stream) { @@ -19,6 +19,12 @@ CommaSeparatedIterator::CommaSeparatedIterator(std::istream &stream): **********************************************************************/ CommaSeparatedIterator &CommaSeparatedIterator::operator++() { if (std::getline(stream_, line_)) { + // Skip line if empty + if (line_.empty()) { + return ++(*this); + } + + // Parse line by comma delimiter size_t pos = line_.find(','); if (pos != std::string::npos) { first_ = line_.substr(0, pos); @@ -27,22 +33,24 @@ CommaSeparatedIterator &CommaSeparatedIterator::operator++() { first_ = line_; second_ = ""; } + // Convert both substrings to lowercase - std::transform( - first_.begin(), - first_.end(), - first_.begin(), - [](const char c) { return static_cast(std::tolower(c)); } - ); - std::transform( - second_.begin(), - second_.end(), - second_.begin(), - [](const char c) { return static_cast(std::tolower(c)); } - ); + auto toLower = [](std::string &s) { + std::transform(s.begin(), s.end(), s.begin(), [](const char c) { + return static_cast(std::tolower(c)); + }); + }; + toLower(first_); + toLower(second_); + } else { - first_ = second_ = ""; // End of stream + if (stream_.bad()) { + throw std::runtime_error("Error reading stream."); + } + // End of stream reached + first_ = second_ = ""; } + return *this; } @@ -65,5 +73,5 @@ CommaSeparatedIterator::value_type CommaSeparatedIterator::operator*() const { * @return True if there are still lines to read, otherwise false. **********************************************************************/ CommaSeparatedIterator::operator bool() const { - return !line_.empty(); + return stream_.good() || !first_.empty() || !second_.empty(); } \ No newline at end of file From 70bcd206ac107c028d345e942a9693aacadd9f8a Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 17 Oct 2024 14:41:31 -0400 Subject: [PATCH 106/379] Make TestDriver a simple executable --- app/tests/CMakeLists.txt | 25 ++++++------ app/tests/TestDriver.cpp | 46 ++++++++++++++++++++++ app/tests/TestDriver.h | 12 +++++- app/tests/TestDriverUtils.cpp | 73 +++++++++++++++++------------------ 4 files changed, 106 insertions(+), 50 deletions(-) create mode 100644 app/tests/TestDriver.cpp diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index 1503acb..8448378 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -6,27 +6,28 @@ set(DRIVER_TEST_NAME "Test${DRIVER_NAME}") ## TODO-TEMPLATE: Include source AND header files for tests here. add_executable( ${DRIVER_TEST_NAME} + "TestDriver.cpp" "TestDriverUtils.cpp" "TestDriver.h" + "${DRIVER_HEADERS}/Driver.h" ) # Add the include directory -target_include_directories(${DRIVER_TEST_NAME} PUBLIC "${DRIVER_HEADERS}") +target_include_directories( + ${DRIVER_TEST_NAME} PUBLIC + "${DRIVER_HEADERS}" + "${PROJECT_SOURCE_DIR}/app/tests" +) + +# Link the library to the executable +target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME}) + +# Make driver executable name available to source +add_definitions(-DDRIVER_NAME="${DRIVER_NAME}") -########################################### -## SET UP AND DISCOVER TESTS -########################################### -if (NOT ${GOOGLETEST_INITIALIZED}) # Avoids duplicate initialization - set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - add_subdirectory("${PROJECT_SOURCE_DIR}/extern/googletest" "extern/googletest") - include(GoogleTest) -endif() -include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) set_target_properties( ${DRIVER_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(${DRIVER_TEST_NAME} ${DRIVER_NAME} GTest::gtest_main) -gtest_discover_tests(${DRIVER_TEST_NAME}) \ No newline at end of file diff --git a/app/tests/TestDriver.cpp b/app/tests/TestDriver.cpp new file mode 100644 index 0000000..376af85 --- /dev/null +++ b/app/tests/TestDriver.cpp @@ -0,0 +1,46 @@ +/** @file TestDriver.cpp + * The main entrypoint for the TestDriver executable + */ +#include "TestDriver.h" + +int main() { + // Driver executable needs to be in the same directory as this executable + std::string executable = std::string(DRIVER_NAME); + +#ifdef _WIN32 + executable += ".exe"; +#endif + + std::string command; + int rtn; + + // TODO-TEMPLATE + // Implement the set of arguments to use when test-running the driver. The last + // argument should be the expected return code. Example: if calling + // `MyDriver.exe -i input.txt -o output.txt` is expected to return 0, the vector + // argument would be: `{"-i", "input.txt", "-o", "output.txt", "0"}` + + // Input files must exist in the directory of this executable + std::vector> argSet = { + {"-i", "input.txt", "-o", "output.txt", "0"}, + }; + + int expected_rtn; + + for (auto args : argSet) { + expected_rtn = ParseInteger(args.back()); + args.pop_back(); + command = executable + " " + joinArguments(args); + std::cout << "Running command: " << command << std::endl; + rtn = std::system(command.c_str()); + if (rtn != expected_rtn) { + std::cout << "[FAILURE] Returned " << rtn << ", expected " + << expected_rtn << std::endl; + } else { + std::cout << "[SUCCESS] Returned " << rtn << std::endl; + } + std::cout << std::endl; + } + + return SUCCESS; +} \ No newline at end of file diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index 987ae95..92d7be6 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -5,4 +5,14 @@ #include "Driver.h" -#include +#include // for std::system +#include // for std::ostringstream +#include // for std::string +#include + +////////////////////// +// FUNCTIONS + +std::string joinArguments(const std::vector &args); +void appendDirectorySep(std::string &str); +int ParseInteger(const std::string &str); diff --git a/app/tests/TestDriverUtils.cpp b/app/tests/TestDriverUtils.cpp index 75ec489..0ecc25d 100644 --- a/app/tests/TestDriverUtils.cpp +++ b/app/tests/TestDriverUtils.cpp @@ -1,46 +1,45 @@ +/** @file TestDriverUtils.cpp + * Utility functions for the TestDriver executable + */ + #include "TestDriver.h" -TEST(ParseIntegerTest, TestParseIntegerSuccess) { - int value; - std::string toParse = "42"; - int rtn = ParseInteger(toParse, value); - EXPECT_EQ(rtn, SUCCESS); - EXPECT_EQ(value, 42); +std::string joinArguments(const std::vector &args) { + std::ostringstream oss; + for (std::string arg : args) { + oss << arg + " "; + } + return oss.str(); } -TEST(ParseIntegerTest, TestParseIntegerFailure1) { - int value; - std::string toParse = "42.0"; - int rtn = ParseInteger(toParse, value); - EXPECT_EQ(rtn, DRVRERR__PARSE); +void appendDirectorySep(std::string &str) { +#ifdef _WIN32 + str += "\\"; +#else + str += "/"; +#endif } -TEST(ParseIntegerTest, TestParseIntegerFailure2) { +/******************************************************************************* + * Parse an integer value from the input string + * + * @param[in] str Input value as string + * @return Parsed integer value + ******************************************************************************/ +int ParseInteger(const std::string &str) { int value; - std::string toParse = "notAnInteger"; - int rtn = ParseInteger(toParse, value); - EXPECT_EQ(rtn, DRVRERR__PARSE); -} + try { + size_t pos; + value = std::stoi(str, &pos, 10); -TEST(ParseDoubleTest, TestParseDoubleSuccess1) { - double value; - std::string toParse = "42.0"; - int rtn = ParseDouble(toParse, value); - EXPECT_EQ(rtn, SUCCESS); - EXPECT_DOUBLE_EQ(value, 42.0); -} - -TEST(ParseDoubleTest, TestParseDoubleSuccess2) { - double value; - std::string toParse = "42"; - int rtn = ParseDouble(toParse, value); - EXPECT_EQ(rtn, SUCCESS); - EXPECT_DOUBLE_EQ(value, 42.0); -} + // Verify the entire string was parsed + if (pos != str.size()) { + throw std::invalid_argument("Input string contains non-numeric characters"); + } + } catch (...) { + // error parsing the input string value + throw std::runtime_error("Could not parse integer value"); + }; -TEST(ParseDoubleTest, TestParseDoubleFailure) { - double value; - std::string toParse = "notADouble"; - int rtn = ParseDouble(toParse, value); - EXPECT_EQ(rtn, DRVRERR__PARSE); -} + return SUCCESS; +} \ No newline at end of file From ee88c586e37b5e7d34483eafed3f0699a301e62b Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 17 Oct 2024 14:45:06 -0400 Subject: [PATCH 107/379] update driver test executable --- app/src/CommaSeparatedIterator.cpp | 38 ++++++++++++++---------- app/tests/CMakeLists.txt | 1 + app/tests/TestDriver.cpp | 37 +++++++++++------------ app/tests/TestDriver.h | 8 +++++ app/tests/TestDriverUtils.cpp | 47 ++++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 35 deletions(-) create mode 100644 app/tests/TestDriverUtils.cpp diff --git a/app/src/CommaSeparatedIterator.cpp b/app/src/CommaSeparatedIterator.cpp index 24539bf..cfcf1e4 100644 --- a/app/src/CommaSeparatedIterator.cpp +++ b/app/src/CommaSeparatedIterator.cpp @@ -1,7 +1,7 @@ #include "CommaSeparatedIterator.h" #include // for std::ptrdiff_t -#include // for std::input_iterator_tag, +#include // for std::input_iterator_tag CommaSeparatedIterator::CommaSeparatedIterator(std::istream &stream): stream_(stream) { @@ -19,6 +19,12 @@ CommaSeparatedIterator::CommaSeparatedIterator(std::istream &stream): **********************************************************************/ CommaSeparatedIterator &CommaSeparatedIterator::operator++() { if (std::getline(stream_, line_)) { + // Skip line if empty + if (line_.empty()) { + return ++(*this); + } + + // Parse line by comma delimiter size_t pos = line_.find(','); if (pos != std::string::npos) { first_ = line_.substr(0, pos); @@ -27,22 +33,24 @@ CommaSeparatedIterator &CommaSeparatedIterator::operator++() { first_ = line_; second_ = ""; } + // Convert both substrings to lowercase - std::transform( - first_.begin(), - first_.end(), - first_.begin(), - [](const char c) { return static_cast(std::tolower(c)); } - ); - std::transform( - second_.begin(), - second_.end(), - second_.begin(), - [](const char c) { return static_cast(std::tolower(c)); } - ); + auto toLower = [](std::string &s) { + std::transform(s.begin(), s.end(), s.begin(), [](const char c) { + return static_cast(std::tolower(c)); + }); + }; + toLower(first_); + toLower(second_); + } else { - first_ = second_ = ""; // End of stream + if (stream_.bad()) { + throw std::runtime_error("Error reading stream."); + } + // End of stream reached + first_ = second_ = ""; } + return *this; } @@ -65,5 +73,5 @@ CommaSeparatedIterator::value_type CommaSeparatedIterator::operator*() const { * @return True if there are still lines to read, otherwise false. **********************************************************************/ CommaSeparatedIterator::operator bool() const { - return !line_.empty(); + return stream_.good() || !first_.empty() || !second_.empty(); } \ No newline at end of file diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index 51d8d7c..662dec2 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -6,6 +6,7 @@ set(DRIVER_TEST_NAME "Test${DRIVER_NAME}") add_executable( ${DRIVER_TEST_NAME} "TestDriver.cpp" + "TestDriverUtils.cpp" "TestDriver.h" "${DRIVER_HEADERS}/Driver.h" ) diff --git a/app/tests/TestDriver.cpp b/app/tests/TestDriver.cpp index 69a9f61..b318d8a 100644 --- a/app/tests/TestDriver.cpp +++ b/app/tests/TestDriver.cpp @@ -1,21 +1,8 @@ +/** @file TestDriver.cpp + * The main entrypoint for the TestDriver executable + */ #include "TestDriver.h" -std::string joinArguments(const std::vector &args) { - std::ostringstream oss; - for (std::string arg : args) { - oss << arg + " "; - } - return oss.str(); -} - -void appendDirectorySep(std::string &str) { -#ifdef _WIN32 - str += "\\"; -#else - str += "/"; -#endif -} - int main() { // Driver executable needs to be in the same directory as this executable std::string executable = std::string(DRIVER_NAME); @@ -29,16 +16,26 @@ int main() { // Input files must exist in the directory of this executable std::vector> argSet = { - {"-i", "i_hgtcm.txt", "-o", "o_hgtcm.txt", "-model", "HGTCM"}, - {"-i", "i_tsm.txt", "-o", "o_tsm.txt", "-model", "TSM"}, - {"-i", "i_asm.txt", "-o", "o_asm.txt", "-model", "ASM"}, + {"-i", "i_hgtcm.txt", "-o", "o_hgtcm.txt", "-model", "HGTCM", "0"}, + {"-i", "i_tsm.txt", "-o", "o_tsm.txt", "-model", "TSM", "0"}, + {"-i", "i_asm.txt", "-o", "o_asm.txt", "-model", "ASM", "0"}, }; + int expected_rtn; + for (auto args : argSet) { + expected_rtn = ParseInteger(args.back()); + args.pop_back(); command = executable + " " + joinArguments(args); std::cout << "Running command: " << command << std::endl; rtn = std::system(command.c_str()); - std::cout << "Return code: " << rtn << std::endl; + if (rtn != expected_rtn) { + std::cout << "[FAILURE] Returned " << rtn << ", expected " + << expected_rtn << std::endl; + } else { + std::cout << "[SUCCESS] Returned " << rtn << std::endl; + } + std::cout << std::endl; } return SUCCESS; diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index 19e927b..92d7be6 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -8,3 +8,11 @@ #include // for std::system #include // for std::ostringstream #include // for std::string +#include + +////////////////////// +// FUNCTIONS + +std::string joinArguments(const std::vector &args); +void appendDirectorySep(std::string &str); +int ParseInteger(const std::string &str); diff --git a/app/tests/TestDriverUtils.cpp b/app/tests/TestDriverUtils.cpp new file mode 100644 index 0000000..4adf355 --- /dev/null +++ b/app/tests/TestDriverUtils.cpp @@ -0,0 +1,47 @@ +/** @file TestDriverUtils.cpp + * Utility functions for the TestDriver executable + */ + +#include "TestDriver.h" + +#include + +std::string joinArguments(const std::vector &args) { + std::ostringstream oss; + for (std::string arg : args) { + oss << arg + " "; + } + return oss.str(); +} + +void appendDirectorySep(std::string &str) { +#ifdef _WIN32 + str += "\\"; +#else + str += "/"; +#endif +} + +/******************************************************************************* + * Parse an integer value from the input string + * + * @param[in] str Input value as string + * @return Parsed integer value + ******************************************************************************/ +int ParseInteger(const std::string &str) { + int value; + try { + size_t pos; + value = std::stoi(str, &pos, 10); + + // Verify the entire string was parsed + if (pos != str.size()) { + throw std::invalid_argument("Input string contains non-numeric characters"); + } + } catch (...) { + // error parsing the input string value + throw std::runtime_error("Could not parse integer value"); + }; + + return SUCCESS; +} \ No newline at end of file From 3e820227532258e822114ca527e54440f2f23d9c Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 17 Oct 2024 14:48:02 -0400 Subject: [PATCH 108/379] minor updates --- app/tests/CMakeLists.txt | 2 +- app/tests/TestDriverUtils.cpp | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index 8448378..bae9272 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -12,7 +12,7 @@ add_executable( "${DRIVER_HEADERS}/Driver.h" ) -# Add the include directory +# Add the include directories target_include_directories( ${DRIVER_TEST_NAME} PUBLIC "${DRIVER_HEADERS}" diff --git a/app/tests/TestDriverUtils.cpp b/app/tests/TestDriverUtils.cpp index 0ecc25d..31e8d97 100644 --- a/app/tests/TestDriverUtils.cpp +++ b/app/tests/TestDriverUtils.cpp @@ -4,6 +4,8 @@ #include "TestDriver.h" +#include + std::string joinArguments(const std::vector &args) { std::ostringstream oss; for (std::string arg : args) { @@ -34,7 +36,9 @@ int ParseInteger(const std::string &str) { // Verify the entire string was parsed if (pos != str.size()) { - throw std::invalid_argument("Input string contains non-numeric characters"); + throw std::invalid_argument( + "Input string contains non-numeric characters" + ); } } catch (...) { // error parsing the input string value From 680188fa9a7bc8d34c853fa5105f4c3832afc7b5 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:37:21 -0400 Subject: [PATCH 109/379] Don't run CI workflows in template repository --- .github/workflows/ctest.yml | 1 + .github/workflows/doxygen.yml | 3 ++- .github/workflows/release.yml | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index 8fd1996..f5d58f9 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -12,6 +12,7 @@ on: # Define the matrix for different operating systems jobs: build-and-test: + if: github.repository != 'NTIA/proplib-template' name: ${{ matrix.os }} / CMake ${{ matrix.cmakeVersion }} runs-on: ${{ matrix.os }} strategy: diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index 544c7dc..c7e9635 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -18,6 +18,7 @@ concurrency: jobs: build: + if: github.repository != 'NTIA/proplib-template' runs-on: ubuntu-latest steps: - name: Checkout repository @@ -54,7 +55,7 @@ jobs: path: ./docs/html/ deploy: - if: ${{ github.event_name == 'release'}} + if: ${{ github.event_name == 'release'}} && github.repository != 'NTIA/proplib-template' needs: build permissions: contents: read diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f9a25c9..815d0d9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,6 +12,7 @@ permissions: jobs: create_release_artifacts: + if: github.repository != 'NTIA/proplib-template' name: Create release artifacts runs-on: ${{ matrix.os }} strategy: From 9f07710df6c051e812d844646fd5ac76dde8c275 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:42:08 -0400 Subject: [PATCH 110/379] Update README.md Comment out boilerplate content for clarity --- README.md | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index aee3b26..228b32b 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,20 @@ # NTIA/ITS Propagation Library Template Project # - - - +[![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-releases-badge]: https://img.shields.io/github/v/release/NTIA/TODO-TEMPLATE -[gh-issues-badge]: https://img.shields.io/github/issues/NTIA/TODO-TEMPLATE +[gh-actions-test-link]: https://github.com/NTIA/proplib-template/actions/workflows/ctest.yml +[gh-actions-test-badge]: https://github.com/NTIA/proplib-template/actions/workflows/ctest.yml/badge.svg?branch=main +[gh-actions-docs-link]: https://github.com/NTIA/proplib-template/actions/workflows/doxygen.yml +[gh-actions-docs-badge]: https://github.com/NTIA/proplib-template/actions/workflows/doxygen.yml/badge.svg?branch=main +[gh-releases-badge]: https://img.shields.io/github/v/release/NTIA/proplib-template +[gh-issues-badge]: https://img.shields.io/github/issues/NTIA/proplib-template @@ -26,10 +25,13 @@ Propagation Library (PropLib). This template is intended for developers wishing to develop a cross-platform C++ library as part of PropLib. Instructions on how to use this repository are found in its [GitHub Wiki](https://github.com/NTIA/proplib-template/wiki). + - +TODO-TEMPLATE: Update links in this section, if applicable +TODO-TEMPLATE: Otherwise, add correct "getting started" information here. + 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/TODO-TEMPLATE/). There, you will find installation instructions, usage information, and code @@ -75,8 +77,9 @@ git submodule update # Clones the initialized submodul ``` ## Running Tests ## - - + +TODO-TEMPLATE: Update this section if needed, based on tests in the repo +TODO-TEMPLATE: Add any other testing info, such as links to available data 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: @@ -87,8 +90,8 @@ ctest --preset release ## References ## - - +TODO-TEMPLATE: Add refs to, e.g., publications related to the software +TODO-TEMPLATE: Update or remove the link here to the Doxygen docs * [ITS Propagation Library Wiki](https://ntia.github.io/propagation-library-wiki) * [`ITS.TODO-TEMPLATE.THIS-LIBRARY` C++ API Reference](https://ntia.github.io/TODO-TEMPLATE) @@ -96,3 +99,5 @@ ctest --preset release ## Contact ## For technical questions, contact . + +--> From a716be1c490a180be9ebb32bec366ee72649cc66 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 17 Oct 2024 18:20:08 -0400 Subject: [PATCH 111/379] Fix return value of utility function --- app/tests/TestDriverUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/tests/TestDriverUtils.cpp b/app/tests/TestDriverUtils.cpp index 31e8d97..0b71813 100644 --- a/app/tests/TestDriverUtils.cpp +++ b/app/tests/TestDriverUtils.cpp @@ -45,5 +45,5 @@ int ParseInteger(const std::string &str) { throw std::runtime_error("Could not parse integer value"); }; - return SUCCESS; + return value; } \ No newline at end of file From ba45906ba75c4c5d78c3bf5bc463e3a8217becd1 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 17 Oct 2024 18:20:19 -0400 Subject: [PATCH 112/379] Suppress executable output when testing driver --- app/tests/TestDriver.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/tests/TestDriver.cpp b/app/tests/TestDriver.cpp index 376af85..3b6d032 100644 --- a/app/tests/TestDriver.cpp +++ b/app/tests/TestDriver.cpp @@ -32,6 +32,13 @@ int main() { args.pop_back(); command = executable + " " + joinArguments(args); std::cout << "Running command: " << command << std::endl; + // Suppress stdout when executable is called: +#ifdef _WIN32 + command += " > nul"; +#else + command += " > /dev/null"; +#endif + command += " 2>&1"; // Also suppress stderr rtn = std::system(command.c_str()); if (rtn != expected_rtn) { std::cout << "[FAILURE] Returned " << rtn << ", expected " From 37cc6bf11d463080b53d7e703a7e632db688efeb Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 17 Oct 2024 18:27:19 -0400 Subject: [PATCH 113/379] fix parser --- app/tests/TestDriverUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/tests/TestDriverUtils.cpp b/app/tests/TestDriverUtils.cpp index 31e8d97..0b71813 100644 --- a/app/tests/TestDriverUtils.cpp +++ b/app/tests/TestDriverUtils.cpp @@ -45,5 +45,5 @@ int ParseInteger(const std::string &str) { throw std::runtime_error("Could not parse integer value"); }; - return SUCCESS; + return value; } \ No newline at end of file From a88ad395d5a3e70ba7ab896a02b7c3b16edbafe2 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 17 Oct 2024 18:27:24 -0400 Subject: [PATCH 114/379] Add test cases --- app/tests/TestDriver.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/tests/TestDriver.cpp b/app/tests/TestDriver.cpp index b318d8a..a466e29 100644 --- a/app/tests/TestDriver.cpp +++ b/app/tests/TestDriver.cpp @@ -19,6 +19,9 @@ int main() { {"-i", "i_hgtcm.txt", "-o", "o_hgtcm.txt", "-model", "HGTCM", "0"}, {"-i", "i_tsm.txt", "-o", "o_tsm.txt", "-model", "TSM", "0"}, {"-i", "i_asm.txt", "-o", "o_asm.txt", "-model", "ASM", "0"}, + {"-k", "INVALIDOPTION", "1003"}, + {"-i", "i_hgtcm.txt", "-o", "o_hgtcm.txt", "-model", "INVALID", "1204"}, + {"-i", "INVALID", "-o", "out.txt", "-model", "ASM", "1006"} }; int expected_rtn; @@ -28,6 +31,13 @@ int main() { args.pop_back(); command = executable + " " + joinArguments(args); std::cout << "Running command: " << command << std::endl; + // Suppress stdout when executable is called: +#ifdef _WIN32 + command += " > nul"; +#else + command += " > /dev/null"; +#endif + command += " 2>&1"; // Also suppress stderr rtn = std::system(command.c_str()); if (rtn != expected_rtn) { std::cout << "[FAILURE] Returned " << rtn << ", expected " From ad24f20f237b79cee490c378480ff96ebe9358a7 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 17 Oct 2024 18:47:39 -0400 Subject: [PATCH 115/379] fix test driver command for unix/macos --- app/tests/TestDriver.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/tests/TestDriver.cpp b/app/tests/TestDriver.cpp index a466e29..c4bd8b4 100644 --- a/app/tests/TestDriver.cpp +++ b/app/tests/TestDriver.cpp @@ -9,6 +9,8 @@ int main() { #ifdef _WIN32 executable += ".exe"; +#else + executable = "./" + executable; #endif std::string command; From 9bd082982b93457637335826ef3886afb633a645 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 18 Oct 2024 12:10:13 -0400 Subject: [PATCH 116/379] Add missing header file for driver --- app/src/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index 8506864..d41276d 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -8,6 +8,7 @@ add_executable( "CommaSeparatedIterator.cpp" "Driver.cpp" "DriverUtils.cpp" + "${DRIVER_HEADERS}/CommaSeparatedIterator.h" "${DRIVER_HEADERS}/Driver.h" "${DRIVER_HEADERS}/Errors.h" "${DRIVER_HEADERS}/Structs.h" From 33fb6469f227894f6857379c568a9f8923b41274 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 18 Oct 2024 13:35:54 -0400 Subject: [PATCH 117/379] Explicit include of for std::endl This is usually included by but this is not guaranteed --- app/include/Driver.h | 1 + 1 file changed, 1 insertion(+) diff --git a/app/include/Driver.h b/app/include/Driver.h index ebd6e0c..ab36dfa 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -13,6 +13,7 @@ #include // for std::ofstream #include // for std::setw #include // for std::cerr, std::cout, std::ostream +#include // for std::endl #include // for std::string, std::stoi, std::stod #include // for std::tie From b715454da0942585b9935d664db1107e16d51d89 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:36:27 -0400 Subject: [PATCH 118/379] Add TempTextFile class Helps in testing driver --- app/tests/CMakeLists.txt | 2 ++ app/tests/TempTextFile.cpp | 56 ++++++++++++++++++++++++++++++++++++++ app/tests/TempTextFile.h | 34 +++++++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 app/tests/TempTextFile.cpp create mode 100644 app/tests/TempTextFile.h diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index 32fa1ba..c69a733 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -5,8 +5,10 @@ set(DRIVER_TEST_NAME "Test${DRIVER_NAME}") add_executable( ${DRIVER_TEST_NAME} + "TempTextFile.cpp" "TestDriver.cpp" "TestDriverUtils.cpp" + "TempTextFile.h" "TestDriver.h" "${DRIVER_HEADERS}/Driver.h" ) diff --git a/app/tests/TempTextFile.cpp b/app/tests/TempTextFile.cpp new file mode 100644 index 0000000..edd612e --- /dev/null +++ b/app/tests/TempTextFile.cpp @@ -0,0 +1,56 @@ +/** @file TempFile.cpp + * Contains a class implementation to + */ +#include "TempTextFile.h" + +#ifdef _WIN32 + // Ensure tmpnam_s is available on Windows + #ifndef __STDC_LIB_EXT1__ + #define __STDC_LIB_EXT1__ + #endif + #ifndef __STDC_WANT_LIB_EXT1__ + #define __STDC_WANT_LIB_EXT1__ 1 + #endif + #include // for L_tmpnam_s, tmpnam_s +#endif + +#include // for std::ofstream +#include // for std::cerr, std::cout, std::ios::trunc +#include // for std::endl +#include // for std::runtime_error + +TempTextFile::TempTextFile(const std::string &content) { +#ifdef _WIN32 + // Generate and store a temporary file name + char tempFileName[L_tmpnam_s]; + if (tmpnam_s(tempFileName, sizeof(tempFileName)) != 0) { + throw std::runtime_error("Failed to create temporary file name."); + } +#else + // Safer implementation for POSIX platforms + char tempFileName[] = "/tmp/proplib-tempfile.XXXXXX"; + int fd = mkstemp(tempFileName); + if (fd == -1) { + throw std::runtime_error("Failed to create temporary file."); + } + close(fd); +#endif + filename = tempFileName; // Store generated filename + std::ofstream tempFile(tempFileName, std::ios::trunc); + if (!tempFile.is_open()) { + std::cerr << "Temp file name is: " << filename << std::endl; + throw std::runtime_error("Failed to open temporary file for writing."); + } + tempFile << content; + tempFile.close(); +} + +TempTextFile::~TempTextFile() { + // Delete the temporary file upon destruction + std::remove(filename.c_str()); +} + +std::string TempTextFile::getFileName() const { + // Return the name of the temporary file. + return filename; +} \ No newline at end of file diff --git a/app/tests/TempTextFile.h b/app/tests/TempTextFile.h new file mode 100644 index 0000000..47b3ee8 --- /dev/null +++ b/app/tests/TempTextFile.h @@ -0,0 +1,34 @@ +#pragma once + +#include // for std::string + +/******************************************************************************* + * A class to manage a temporary text file. + * + * The TempTextFile class creates a temporary text file from a string that is + * automatically deleted when the object is destroyed. + ******************************************************************************/ +class TempTextFile { + public: + /*********************************************************************** + * Constructor that creates a temporary file and writes content to it. + * + * @param[in] content String content to write to the file. + * @throws std::runtime_error On failure to create or write to file. + **********************************************************************/ + TempTextFile(const std::string &content); + + /*********************************************************************** + * Destructor that closes (and deletes) the temporary file. + **********************************************************************/ + ~TempTextFile(); + + /*********************************************************************** + * Retrieve the name of the temporary file + * + * @return A string containing the name of the temporary file. + **********************************************************************/ + std::string getFileName() const; + private: + std::string filename; /**< Name of the temporary file */ +}; From a2e3c4cf9bef0b7b734a8d679496285daae3cdab Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:36:48 -0400 Subject: [PATCH 119/379] Set flags to ensure localtime_s is available on Windows --- app/src/DriverUtils.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/DriverUtils.cpp b/app/src/DriverUtils.cpp index da86e95..b189770 100644 --- a/app/src/DriverUtils.cpp +++ b/app/src/DriverUtils.cpp @@ -3,6 +3,16 @@ */ #include "Driver.h" +#ifdef _WIN32 + // Ensure localtime_s is available on Windows + #ifndef __STDC_LIB_EXT1__ + #define __STDC_LIB_EXT1__ + #endif + #ifndef __STDC_WANT_LIB_EXT1__ + #define __STDC_WANT_LIB_EXT1__ 1 + #endif +#endif + #include // for localtime_{s,r}, std::{time, time_t, tm, strftime} /******************************************************************************* From 2aaa63b43074c1c80918cb7903f05e4ed763f7e5 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:39:19 -0400 Subject: [PATCH 120/379] Add tempfile header to testdriver header --- app/tests/TestDriver.h | 1 + 1 file changed, 1 insertion(+) diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index 92d7be6..b67854c 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -4,6 +4,7 @@ #pragma once #include "Driver.h" +#include "TempTextFile.h" #include // for std::system #include // for std::ostringstream From bed5791d23aa5fbdb630e47f8847fbe28bb97b63 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:40:24 -0400 Subject: [PATCH 121/379] Add TempTextFile class --- app/tests/CMakeLists.txt | 2 ++ app/tests/TempTextFile.cpp | 56 ++++++++++++++++++++++++++++++++++++++ app/tests/TempTextFile.h | 34 +++++++++++++++++++++++ app/tests/TestDriver.h | 1 + 4 files changed, 93 insertions(+) create mode 100644 app/tests/TempTextFile.cpp create mode 100644 app/tests/TempTextFile.h diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index bae9272..d9b11e2 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -6,8 +6,10 @@ set(DRIVER_TEST_NAME "Test${DRIVER_NAME}") ## TODO-TEMPLATE: Include source AND header files for tests here. add_executable( ${DRIVER_TEST_NAME} + "TempTextFile.cpp" "TestDriver.cpp" "TestDriverUtils.cpp" + "TempTextFile.h" "TestDriver.h" "${DRIVER_HEADERS}/Driver.h" ) diff --git a/app/tests/TempTextFile.cpp b/app/tests/TempTextFile.cpp new file mode 100644 index 0000000..edd612e --- /dev/null +++ b/app/tests/TempTextFile.cpp @@ -0,0 +1,56 @@ +/** @file TempFile.cpp + * Contains a class implementation to + */ +#include "TempTextFile.h" + +#ifdef _WIN32 + // Ensure tmpnam_s is available on Windows + #ifndef __STDC_LIB_EXT1__ + #define __STDC_LIB_EXT1__ + #endif + #ifndef __STDC_WANT_LIB_EXT1__ + #define __STDC_WANT_LIB_EXT1__ 1 + #endif + #include // for L_tmpnam_s, tmpnam_s +#endif + +#include // for std::ofstream +#include // for std::cerr, std::cout, std::ios::trunc +#include // for std::endl +#include // for std::runtime_error + +TempTextFile::TempTextFile(const std::string &content) { +#ifdef _WIN32 + // Generate and store a temporary file name + char tempFileName[L_tmpnam_s]; + if (tmpnam_s(tempFileName, sizeof(tempFileName)) != 0) { + throw std::runtime_error("Failed to create temporary file name."); + } +#else + // Safer implementation for POSIX platforms + char tempFileName[] = "/tmp/proplib-tempfile.XXXXXX"; + int fd = mkstemp(tempFileName); + if (fd == -1) { + throw std::runtime_error("Failed to create temporary file."); + } + close(fd); +#endif + filename = tempFileName; // Store generated filename + std::ofstream tempFile(tempFileName, std::ios::trunc); + if (!tempFile.is_open()) { + std::cerr << "Temp file name is: " << filename << std::endl; + throw std::runtime_error("Failed to open temporary file for writing."); + } + tempFile << content; + tempFile.close(); +} + +TempTextFile::~TempTextFile() { + // Delete the temporary file upon destruction + std::remove(filename.c_str()); +} + +std::string TempTextFile::getFileName() const { + // Return the name of the temporary file. + return filename; +} \ No newline at end of file diff --git a/app/tests/TempTextFile.h b/app/tests/TempTextFile.h new file mode 100644 index 0000000..47b3ee8 --- /dev/null +++ b/app/tests/TempTextFile.h @@ -0,0 +1,34 @@ +#pragma once + +#include // for std::string + +/******************************************************************************* + * A class to manage a temporary text file. + * + * The TempTextFile class creates a temporary text file from a string that is + * automatically deleted when the object is destroyed. + ******************************************************************************/ +class TempTextFile { + public: + /*********************************************************************** + * Constructor that creates a temporary file and writes content to it. + * + * @param[in] content String content to write to the file. + * @throws std::runtime_error On failure to create or write to file. + **********************************************************************/ + TempTextFile(const std::string &content); + + /*********************************************************************** + * Destructor that closes (and deletes) the temporary file. + **********************************************************************/ + ~TempTextFile(); + + /*********************************************************************** + * Retrieve the name of the temporary file + * + * @return A string containing the name of the temporary file. + **********************************************************************/ + std::string getFileName() const; + private: + std::string filename; /**< Name of the temporary file */ +}; diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index 92d7be6..b67854c 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -4,6 +4,7 @@ #pragma once #include "Driver.h" +#include "TempTextFile.h" #include // for std::system #include // for std::ostringstream From 6dd8423ce55343f89db0843ab015727e8e283e2f Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:40:54 -0400 Subject: [PATCH 122/379] ensure localtime_s is available on Windows --- app/src/DriverUtils.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/DriverUtils.cpp b/app/src/DriverUtils.cpp index da86e95..b189770 100644 --- a/app/src/DriverUtils.cpp +++ b/app/src/DriverUtils.cpp @@ -3,6 +3,16 @@ */ #include "Driver.h" +#ifdef _WIN32 + // Ensure localtime_s is available on Windows + #ifndef __STDC_LIB_EXT1__ + #define __STDC_LIB_EXT1__ + #endif + #ifndef __STDC_WANT_LIB_EXT1__ + #define __STDC_WANT_LIB_EXT1__ 1 + #endif +#endif + #include // for localtime_{s,r}, std::{time, time_t, tm, strftime} /******************************************************************************* From 17452832afcf89af80bc05898e534eaaba6bf90e Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:53:06 -0400 Subject: [PATCH 123/379] Add include for std::remove --- app/tests/TempTextFile.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/app/tests/TempTextFile.cpp b/app/tests/TempTextFile.cpp index edd612e..78ce497 100644 --- a/app/tests/TempTextFile.cpp +++ b/app/tests/TempTextFile.cpp @@ -14,6 +14,7 @@ #include // for L_tmpnam_s, tmpnam_s #endif +#include // for std::remove #include // for std::ofstream #include // for std::cerr, std::cout, std::ios::trunc #include // for std::endl From dd9b9dc7141d07ff3e8f0c2ca9f3397e5072adb5 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:53:38 -0400 Subject: [PATCH 124/379] Add include for std::remove --- app/tests/TempTextFile.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/app/tests/TempTextFile.cpp b/app/tests/TempTextFile.cpp index edd612e..78ce497 100644 --- a/app/tests/TempTextFile.cpp +++ b/app/tests/TempTextFile.cpp @@ -14,6 +14,7 @@ #include // for L_tmpnam_s, tmpnam_s #endif +#include // for std::remove #include // for std::ofstream #include // for std::cerr, std::cout, std::ios::trunc #include // for std::endl From da7a6075fd2ed27c3a699b5d14df1c310e845494 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 23 Oct 2024 17:23:39 -0400 Subject: [PATCH 125/379] Improve driver input parsing and validation --- app/README.md | 2 ++ app/include/Driver.h | 1 + app/include/Errors.h | 1 + app/src/Driver.cpp | 69 +++++++++++++++++++++++++---------------- app/src/DriverUtils.cpp | 11 +++++++ 5 files changed, 57 insertions(+), 27 deletions(-) diff --git a/app/README.md b/app/README.md index a3edf8a..0c33d03 100644 --- a/app/README.md +++ b/app/README.md @@ -66,6 +66,7 @@ driver implements the following return codes. | Value | Const Name | Description | |-------|--------------------------------|--------------------------------------------| | 1000 | `DRVR__RETURN_SUCCESS` | Successful execution | +| 1002 | `DRVRERR__MISSING_OPTION` | An input flag was given without a value | | 1003 | `DRVRERR__INVALID_OPTION` | Unknown option specified | | 1007 | `DRVRERR__OPENING_OUTPUT_FILE` | Failed to open the output file for writing | @@ -87,3 +88,4 @@ which may include, e.g., parameter out-of-range errors. |-------|------------------------------------|---------------------------------------| | 1202 | `DRVRERR__VALIDATION_IN_FILE` | Input parameter file is not specified | | 1203 | `DRVRERR__VALIDATION_OUT_FILE` | Output file is not specified | +| 1204 | `DRVRERR__VALIDATION_MODEL` | Model is not specified | diff --git a/app/include/Driver.h b/app/include/Driver.h index 6b2ecab..48f4824 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -64,3 +64,4 @@ int ParseInteger(const std::string &str, int &value); int ParseDouble(const std::string &str, double &value); int ParsingErrorHelper(const int err, const std::string &msg); std::string GetDatetimeString(); +void StringToLower(std::string &str); diff --git a/app/include/Errors.h b/app/include/Errors.h index 0de3806..a5a9648 100644 --- a/app/include/Errors.h +++ b/app/include/Errors.h @@ -12,6 +12,7 @@ /** Primary Return Codes (1000-1099) */ #define DRVR__RETURN_SUCCESS 1000 +#define DRVRERR__MISSING_OPTION 1002 #define DRVRERR__INVALID_OPTION 1003 #define DRVRERR__OPENING_INPUT_FILE 1006 #define DRVRERR__OPENING_OUTPUT_FILE 1007 diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index 6ba3d9b..227e93e 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -6,6 +6,8 @@ #include "Labels.h" #include "Tags.h" +#include // for std::find + /******************************************************************************* * Main function of the driver executable ******************************************************************************/ @@ -17,14 +19,14 @@ int main(int argc, char **argv) { rtn = ParseArguments(argc, argv, params); if (rtn == DRVR__RETURN_SUCCESS) return SUCCESS; - if (rtn) { + if (rtn != SUCCESS) { Help(); return rtn; } - // validate command line inputs + // Ensure required options were provided rtn = ValidateInputs(params); - if (rtn) { + if (rtn != SUCCESS) { Help(); return rtn; } @@ -131,12 +133,38 @@ int main(int argc, char **argv) { * @return Return code ******************************************************************************/ int ParseArguments(int argc, char **argv, DrvrParams ¶ms) { + const std::vector validArgs + = {"-i", "-o", "-model", "-h", "--help", "-v", "--version"}; + for (int i = 1; i < argc; i++) { + // Parse arg to lowercase string std::string arg(argv[i]); - std::transform(arg.begin(), arg.end(), arg.begin(), [](const char c) { - return static_cast(std::tolower(c)); - }); + StringToLower(arg); + + // Check if provided flag is valid + if (std::find(validArgs.begin(), validArgs.end(), arg) + == validArgs.end()) { + // Invalid argument provided + std::cerr << "Unknown option: " << argv[i] << std::endl; + return DRVRERR__INVALID_OPTION; + } + // Handle simple flags which don't have values + if (arg == "-v" || arg == "--version") { + Version(); + return DRVR__RETURN_SUCCESS; + } else if (arg == "-h" || arg == "--help") { + Help(); + return DRVR__RETURN_SUCCESS; + } + + // Check if end of arguments reached or next argument is another flag + if (i + 1 >= argc || argv[i + 1][0] == '-') { + std::cerr << "Error: no value given for " << arg << std::endl; + return DRVRERR__MISSING_OPTION; + } + + // Match valid flags and store specified values in params if (arg == "-i") { params.in_file = argv[i + 1]; i++; @@ -145,31 +173,15 @@ int ParseArguments(int argc, char **argv, DrvrParams ¶ms) { i++; } else if (arg == "-model") { std::string argval(argv[i + 1]); - std::transform( - argval.begin(), - argval.end(), - argval.begin(), - [](const char c) { return static_cast(std::tolower(c)); } - ); + StringToLower(argval); if (argval == "asm") { params.model = P2108Model::ASM; } else if (argval == "hgtcm") { params.model = P2108Model::HGTCM; } else if (argval == "tsm") { params.model = P2108Model::TSM; - } else { - params.model = P2108Model::NOT_SET; } i++; - } else if (arg == "-v") { - Version(); - return DRVR__RETURN_SUCCESS; - } else if (arg == "-h") { - Help(); - return DRVR__RETURN_SUCCESS; - } else { - std::cerr << "Unknown option: " << argv[i] << std::endl; - return DRVRERR__INVALID_OPTION; } } @@ -187,6 +199,8 @@ void Help(std::ostream &os) { os << "\t-i :: Input file name" << std::endl; os << "\t-o :: Output file name" << std::endl; os << "\t-model :: Model to run [HGTCM, TSM, ASM]" << std::endl; + os << "\t -h :: Display this help message" << std::endl; + os << "\t -v :: Display program version information" << std::endl; os << std::endl << "Examples:" << std::endl; os << "\t[WINDOWS] " << DRIVER_NAME << ".exe -i inputs.txt -o results.txt -model ASM" << std::endl; @@ -205,18 +219,19 @@ void Help(std::ostream &os) { * @return Return code ******************************************************************************/ int ValidateInputs(const DrvrParams ¶ms) { - if (params.in_file.length() == 0) + DrvrParams not_set; + if (params.in_file == not_set.in_file) return Validate_RequiredErrMsgHelper("-i", DRVRERR__VALIDATION_IN_FILE); - if (params.out_file.length() == 0) + if (params.out_file == not_set.out_file) return Validate_RequiredErrMsgHelper( "-o", DRVRERR__VALIDATION_OUT_FILE ); - if (params.model == P2108Model::NOT_SET) + if (params.model == not_set.model) return Validate_RequiredErrMsgHelper( "-model", DRVRERR__VALIDATION_MODEL ); return SUCCESS; -} +} \ No newline at end of file diff --git a/app/src/DriverUtils.cpp b/app/src/DriverUtils.cpp index b189770..6775406 100644 --- a/app/src/DriverUtils.cpp +++ b/app/src/DriverUtils.cpp @@ -119,4 +119,15 @@ std::string GetDatetimeString() { return "Could not format datetime string"; } return std::string(mbstr); +} + +/****************************************************************************** + * Convert a string to lowercase. + * + * @param[in, out] str The string to convert + ******************************************************************************/ +void StringToLower(std::string &str) { + std::transform(str.begin(), str.end(), str.begin(), [](const char c) { + return static_cast(std::tolower(c)); + }); } \ No newline at end of file From 5af75e2ac9cc7e6adeb5d321f592984dcd870845 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 23 Oct 2024 17:24:24 -0400 Subject: [PATCH 126/379] Implement driver testing with GoogleTest Add test cases for all driver errors. Need to add cases for valid inputs. --- app/tests/CMakeLists.txt | 18 +++++- app/tests/TestDriver.cpp | 110 ++++++++++++++++++---------------- app/tests/TestDriver.h | 94 +++++++++++++++++++++++++---- app/tests/TestDriverASM.cpp | 34 +++++++++++ app/tests/TestDriverHGTCM.cpp | 44 ++++++++++++++ app/tests/TestDriverTSM.cpp | 33 ++++++++++ app/tests/TestDriverUtils.cpp | 49 --------------- tests/CMakeLists.txt | 1 + 8 files changed, 270 insertions(+), 113 deletions(-) create mode 100644 app/tests/TestDriverASM.cpp create mode 100644 app/tests/TestDriverHGTCM.cpp create mode 100644 app/tests/TestDriverTSM.cpp delete mode 100644 app/tests/TestDriverUtils.cpp diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index c69a733..5b480ac 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -7,7 +7,9 @@ add_executable( ${DRIVER_TEST_NAME} "TempTextFile.cpp" "TestDriver.cpp" - "TestDriverUtils.cpp" + "TestDriverASM.cpp" + "TestDriverHGTCM.cpp" + "TestDriverTSM.cpp" "TempTextFile.h" "TestDriver.h" "${DRIVER_HEADERS}/Driver.h" @@ -32,3 +34,17 @@ set_target_properties( ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" ) + +########################################### +## SET UP AND DISCOVER TESTS +########################################### +if (NOT ${GOOGLETEST_ADDED}) + 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(GOOGLETEST_ADDED ON) +endif () + +target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME} GTest::gtest_main) +gtest_discover_tests(${DRIVER_TEST_NAME}) diff --git a/app/tests/TestDriver.cpp b/app/tests/TestDriver.cpp index c4bd8b4..2648a5b 100644 --- a/app/tests/TestDriver.cpp +++ b/app/tests/TestDriver.cpp @@ -1,54 +1,60 @@ -/** @file TestDriver.cpp - * The main entrypoint for the TestDriver executable - */ #include "TestDriver.h" -int main() { - // Driver executable needs to be in the same directory as this executable - std::string executable = std::string(DRIVER_NAME); - -#ifdef _WIN32 - executable += ".exe"; -#else - executable = "./" + executable; -#endif - - std::string command; - int rtn; - - // Input files must exist in the directory of this executable - std::vector> argSet = { - {"-i", "i_hgtcm.txt", "-o", "o_hgtcm.txt", "-model", "HGTCM", "0"}, - {"-i", "i_tsm.txt", "-o", "o_tsm.txt", "-model", "TSM", "0"}, - {"-i", "i_asm.txt", "-o", "o_asm.txt", "-model", "ASM", "0"}, - {"-k", "INVALIDOPTION", "1003"}, - {"-i", "i_hgtcm.txt", "-o", "o_hgtcm.txt", "-model", "INVALID", "1204"}, - {"-i", "INVALID", "-o", "out.txt", "-model", "ASM", "1006"} - }; - - int expected_rtn; - - for (auto args : argSet) { - expected_rtn = ParseInteger(args.back()); - args.pop_back(); - command = executable + " " + joinArguments(args); - std::cout << "Running command: " << command << std::endl; - // Suppress stdout when executable is called: -#ifdef _WIN32 - command += " > nul"; -#else - command += " > /dev/null"; -#endif - command += " 2>&1"; // Also suppress stderr - rtn = std::system(command.c_str()); - if (rtn != expected_rtn) { - std::cout << "[FAILURE] Returned " << rtn << ", expected " - << expected_rtn << std::endl; - } else { - std::cout << "[SUCCESS] Returned " << rtn << std::endl; - } - std::cout << std::endl; - } - - return SUCCESS; -} \ No newline at end of file + +TEST_F(DriverTest, MissingOptionError1) { + // Test case: missing option between two provided flags + std::string cmd = executable + " -i -o out.txt"; + SuppressOutputs(cmd); + int rtn = std::system(cmd.c_str()); + EXPECT_EQ(rtn, DRVRERR__MISSING_OPTION); +} + +TEST_F(DriverTest, MissingOptionError2) { + // Test case: missing option at the end of command + std::string cmd = executable + " -i"; + SuppressOutputs(cmd); + int rtn = std::system(cmd.c_str()); + EXPECT_EQ(rtn, DRVRERR__MISSING_OPTION); +} + +TEST_F(DriverTest, InvalidOptionError) { + std::string cmd = executable + " -X"; + SuppressOutputs(cmd); + int rtn = std::system(cmd.c_str()); + EXPECT_EQ(rtn, DRVRERR__INVALID_OPTION); +} + +TEST_F(DriverTest, OpeningInputFileError) { + int rtn = RunDriver("/invalid/path/input.xyz", "ASM", "out.txt"); + EXPECT_EQ(rtn, DRVRERR__OPENING_INPUT_FILE); +} + +TEST_F(DriverTest, OpeningOutputFileError) { + // Provide valid inputs but invalid output file path + std::string inputs = "f__ghz,10\ntheta__deg,10.5\np,45"; + int rtn = RunDriverWithInputFile(inputs, "ASM", "/invalid/path/output.xyz"); + EXPECT_EQ(rtn, DRVRERR__OPENING_OUTPUT_FILE); +} + +TEST_F(DriverTest, ValidationInFileError) { + std::string cmd = executable + " -o out.txt -model ASM"; + SuppressOutputs(cmd); + int rtn = std::system(cmd.c_str()); + EXPECT_EQ(rtn, DRVRERR__VALIDATION_IN_FILE); +} + +TEST_F(DriverTest, ValidationOutFileError) { + // Input file does not need to exist here, just has to be specified + std::string cmd = executable + " -i in.txt -model ASM"; + SuppressOutputs(cmd); + int rtn = std::system(cmd.c_str()); + EXPECT_EQ(rtn, DRVRERR__VALIDATION_OUT_FILE); +} + +TEST_F(DriverTest, ValidationModelError) { + // Input file does not need to exist here, just has to be specified + std::string cmd = executable + " -i in.txt -o out.txt"; + SuppressOutputs(cmd); + int rtn = std::system(cmd.c_str()); + EXPECT_EQ(rtn, DRVRERR__VALIDATION_MODEL); +} diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index b67854c..286f0ed 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -1,19 +1,91 @@ -/** @file TestDriver.h - * Primary header for command line driver tests. - */ #pragma once #include "Driver.h" #include "TempTextFile.h" +#include // for std::remove #include // for std::system -#include // for std::ostringstream -#include // for std::string -#include +#include +#include // for std::cout, std::endl +#include // for std::string -////////////////////// -// FUNCTIONS +class DriverTest: public ::testing::Test { + protected: + void SetUp() override { + // Get the name of the executable to test + executable = std::string(DRIVER_NAME); +#ifdef _WIN32 + executable += ".exe"; +#else + executable = "./" + executable; +#endif + } -std::string joinArguments(const std::vector &args); -void appendDirectorySep(std::string &str); -int ParseInteger(const std::string &str); + void SuppressOutputs(std::string &cmd) { +#ifdef _WIN32 + cmd += " > nul"; +#else + cmd += " > /dev/null"; +#endif + cmd += " 2>&1"; + } + + std::string BuildCommand( + const std::string &inFile, + const std::string &model, + const std::string &outFile, + const bool suppressOutputs = true + ) { + // Construct command from parameters + std::string command = executable; + command += " -i " + inFile; + command += " -model " + model; + command += " -o " + outFile; + if (suppressOutputs) { + // Suppress outputs (cross-platform) + SuppressOutputs(command); + } + // Return the full command string + return command; + } + + int RunDriver( + const std::string &inFile, + const std::string &model, + const std::string &outFile + ) { + std::string cmd = BuildCommand(inFile, model, outFile); + int rtn = std::system(cmd.c_str()); + return rtn; + } + + int RunDriverWithInputFile( + const std::string &inFileContents, + const std::string &model, + const std::string &outFile = "tmp_out.txt" + ) { + TempTextFile tempFile(inFileContents); + std::string inFile = tempFile.getFileName(); + int rtn = RunDriver(inFile, model, outFile); + // Cleanup: delete output file if it was created + DeleteOutputFile(outFile); + return rtn; + } + + void DeleteOutputFile(const std::string &fileName) { + bool fileExists = false; +#ifdef _WIN32 + fileExists = _access(fileName.c_str(), 0) == 0; +#else + fileExists = access(fileName.c_str(), F_OK) == 0; +#endif + if (fileExists) { + if (std::remove(fileName.c_str()) != 0) { + std::perror("Error deleting output file"); + } + } + } + + // Holds the platform-dependent string to call the executable + std::string executable; +}; diff --git a/app/tests/TestDriverASM.cpp b/app/tests/TestDriverASM.cpp new file mode 100644 index 0000000..32ae953 --- /dev/null +++ b/app/tests/TestDriverASM.cpp @@ -0,0 +1,34 @@ +#include "TestDriver.h" + +// Test fixture for the unit tests +class ASMDriverTest: public DriverTest { + protected: + void + TestASM(const std::string &inFileContents, const int expected_rtn) { + int asm_rtn; + asm_rtn = RunDriverWithInputFile(inFileContents, "ASM"); + EXPECT_EQ(asm_rtn, expected_rtn); + } + + std::string ASMInputs; +}; + +TEST_F(ASMDriverTest, TestParseError) { + ASMInputs = "unknown_param,0.0"; + TestASM(ASMInputs, DRVRERR__PARSE); +} + +TEST_F(ASMDriverTest, TestParseFrequencyError) { + ASMInputs = "f__ghz,invalid"; + TestASM(ASMInputs, DRVRERR__PARSE_FREQ); +} + +TEST_F(ASMDriverTest, TestParseThetaError) { + ASMInputs = "theta__deg,invalid"; + TestASM(ASMInputs, DRVRERR__PARSE_THETA); +} + +TEST_F(ASMDriverTest, TestParsePercentageError) { + ASMInputs = "p,invalid"; + TestASM(ASMInputs, DRVRERR__PARSE_PERCENTAGE); +} \ No newline at end of file diff --git a/app/tests/TestDriverHGTCM.cpp b/app/tests/TestDriverHGTCM.cpp new file mode 100644 index 0000000..7783bd7 --- /dev/null +++ b/app/tests/TestDriverHGTCM.cpp @@ -0,0 +1,44 @@ +#include "TestDriver.h" + +class HGTCMDriverTest: public DriverTest { + protected: + void TestHGTCM( + const std::string &inFileContents, const int expected_rtn + ) { + int hgtcm_rtn; + hgtcm_rtn = RunDriverWithInputFile(inFileContents, "HGTCM"); + EXPECT_EQ(hgtcm_rtn, expected_rtn); + } + + std::string HGTCMInputs; +}; + +TEST_F(HGTCMDriverTest, TestParseError) { + HGTCMInputs = "unknown_param,0.0"; + TestHGTCM(HGTCMInputs, DRVRERR__PARSE); +} + +TEST_F(HGTCMDriverTest, TestParseFrequencyError) { + HGTCMInputs = "f__ghz,invalid"; + TestHGTCM(HGTCMInputs, DRVRERR__PARSE_FREQ); +} + +TEST_F(HGTCMDriverTest, TestParseHeightError) { + HGTCMInputs = "h__meter,invalid"; + TestHGTCM(HGTCMInputs, DRVRERR__PARSE_HEIGHT); +} + +TEST_F(HGTCMDriverTest, TestParseStreetWidthError) { + HGTCMInputs = "w_s__meter,invalid"; + TestHGTCM(HGTCMInputs, DRVRERR__PARSE_STREET_WIDTH); +} + +TEST_F(HGTCMDriverTest, TestParseReprHeightError) { + HGTCMInputs = "R__meter,invalid"; + TestHGTCM(HGTCMInputs, DRVRERR__PARSE_REPR_HEIGHT); +} + +TEST_F(HGTCMDriverTest, TestParseClutterTypeError) { + HGTCMInputs = "clutter_type,invalid"; + TestHGTCM(HGTCMInputs, DRVRERR__PARSE_CLUTTER_TYPE); +} \ No newline at end of file diff --git a/app/tests/TestDriverTSM.cpp b/app/tests/TestDriverTSM.cpp new file mode 100644 index 0000000..c5eb703 --- /dev/null +++ b/app/tests/TestDriverTSM.cpp @@ -0,0 +1,33 @@ +#include "TestDriver.h" + +class TSMDriverTest: public DriverTest { + protected: + void + TestTSM(const std::string &inFileContents, const int expected_rtn) { + int tsm_rtn; + tsm_rtn = RunDriverWithInputFile(inFileContents, "TSM"); + EXPECT_EQ(tsm_rtn, expected_rtn); + } + + std::string TSMInputs; +}; + +TEST_F(TSMDriverTest, TestParseError) { + TSMInputs = "unknown_param,0.0"; + TestTSM(TSMInputs, DRVRERR__PARSE); +} + +TEST_F(TSMDriverTest, TestParseFrequencyError) { + TSMInputs = "f__ghz,invalid"; + TestTSM(TSMInputs, DRVRERR__PARSE_FREQ); +} + +TEST_F(TSMDriverTest, TestParsePercentageError) { + TSMInputs = "p,invalid"; + TestTSM(TSMInputs, DRVRERR__PARSE_PERCENTAGE); +} + +TEST_F(TSMDriverTest, TestParsePathDistanceError) { + TSMInputs = "d__km,invalid"; + TestTSM(TSMInputs, DRVRERR__PARSE_PATH_DIST); +} \ No newline at end of file diff --git a/app/tests/TestDriverUtils.cpp b/app/tests/TestDriverUtils.cpp deleted file mode 100644 index 0b71813..0000000 --- a/app/tests/TestDriverUtils.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/** @file TestDriverUtils.cpp - * Utility functions for the TestDriver executable - */ - -#include "TestDriver.h" - -#include - -std::string joinArguments(const std::vector &args) { - std::ostringstream oss; - for (std::string arg : args) { - oss << arg + " "; - } - return oss.str(); -} - -void appendDirectorySep(std::string &str) { -#ifdef _WIN32 - str += "\\"; -#else - str += "/"; -#endif -} - -/******************************************************************************* - * Parse an integer value from the input string - * - * @param[in] str Input value as string - * @return Parsed integer value - ******************************************************************************/ -int ParseInteger(const std::string &str) { - int value; - try { - size_t pos; - value = std::stoi(str, &pos, 10); - - // Verify the entire string was parsed - if (pos != str.size()) { - throw std::invalid_argument( - "Input string contains non-numeric characters" - ); - } - } catch (...) { - // error parsing the input string value - throw std::runtime_error("Could not parse integer value"); - }; - - return value; -} \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9ecc803..ad99839 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -28,5 +28,6 @@ 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(GOOGLETEST_ADDED ON) target_link_libraries(${TEST_NAME} ${LIB_NAME} GTest::gtest_main) gtest_discover_tests(${TEST_NAME}) \ No newline at end of file From ca2e05e085a04112b88da3a7be1a46b7e87a4438 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 23 Oct 2024 17:27:19 -0400 Subject: [PATCH 127/379] Add success cases to driver tests --- app/tests/TestDriverASM.cpp | 5 +++++ app/tests/TestDriverHGTCM.cpp | 6 ++++++ app/tests/TestDriverTSM.cpp | 5 +++++ 3 files changed, 16 insertions(+) diff --git a/app/tests/TestDriverASM.cpp b/app/tests/TestDriverASM.cpp index 32ae953..9d2a9c8 100644 --- a/app/tests/TestDriverASM.cpp +++ b/app/tests/TestDriverASM.cpp @@ -13,6 +13,11 @@ class ASMDriverTest: public DriverTest { std::string ASMInputs; }; +TEST_F(ASMDriverTest, TestSuccess) { + ASMInputs = "f__ghz,10\ntheta__deg,10.5\np,45"; + TestASM(ASMInputs, SUCCESS); +} + TEST_F(ASMDriverTest, TestParseError) { ASMInputs = "unknown_param,0.0"; TestASM(ASMInputs, DRVRERR__PARSE); diff --git a/app/tests/TestDriverHGTCM.cpp b/app/tests/TestDriverHGTCM.cpp index 7783bd7..467daa6 100644 --- a/app/tests/TestDriverHGTCM.cpp +++ b/app/tests/TestDriverHGTCM.cpp @@ -13,6 +13,12 @@ class HGTCMDriverTest: public DriverTest { std::string HGTCMInputs; }; +TEST_F(HGTCMDriverTest, TestSuccess) { + HGTCMInputs + = "f__ghz,1.5\nh__meter,2\nw_s__meter,27\nR__meter,15\nclutter_type,4"; + TestHGTCM(HGTCMInputs, SUCCESS); +} + TEST_F(HGTCMDriverTest, TestParseError) { HGTCMInputs = "unknown_param,0.0"; TestHGTCM(HGTCMInputs, DRVRERR__PARSE); diff --git a/app/tests/TestDriverTSM.cpp b/app/tests/TestDriverTSM.cpp index c5eb703..36b3e32 100644 --- a/app/tests/TestDriverTSM.cpp +++ b/app/tests/TestDriverTSM.cpp @@ -12,6 +12,11 @@ class TSMDriverTest: public DriverTest { std::string TSMInputs; }; +TEST_F(TSMDriverTest, TestSuccess) { + TSMInputs = "f__ghz,26.6\nd__km,15.8\np,45"; + TestTSM(TSMInputs, SUCCESS); +} + TEST_F(TSMDriverTest, TestParseError) { TSMInputs = "unknown_param,0.0"; TestTSM(TSMInputs, DRVRERR__PARSE); From 1d4273ac8842cd2358178323fee318f483d8ac39 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 23 Oct 2024 18:18:53 -0400 Subject: [PATCH 128/379] Provide driver location to driver test --- app/CMakeLists.txt | 1 + app/src/CMakeLists.txt | 7 ++++--- app/tests/CMakeLists.txt | 9 +++++---- app/tests/TestDriver.h | 10 +++++++--- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index fb3cd29..9458d46 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -4,6 +4,7 @@ set(DRIVER_NAME "${LIB_NAME}Driver") set(DRIVER_VERSION ${PROJECT_VERSION}) # May be set manually instead set(DRIVER_HEADERS "${PROJECT_SOURCE_DIR}/app/include") +set(DRIVER_EXE_DIR "${PROJECT_SOURCE_DIR}/bin") ########################################### ## BUILD THE COMMAND LINE DRIVER diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index ac22ac8..715c0e1 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -32,11 +32,12 @@ add_definitions(-DLIBRARY_NAME="${LIB_NAME}") add_definitions(-DDRIVER_NAME="${DRIVER_NAME}") # Set some target metadata + set_target_properties( ${DRIVER_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" + LIBRARY_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} + ARCHIVE_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} + RUNTIME_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} ) diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index 5b480ac..52d10be 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -25,14 +25,15 @@ target_include_directories( # Link the library to the executable target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME}) -# Make driver executable name available to source +# Make driver executable name and location available to source add_definitions(-DDRIVER_NAME="${DRIVER_NAME}") +add_definitions(-DDRIVER_LOCATION="${DRIVER_EXE_DIR}/${CMAKE_BUILD_TYPE}") set_target_properties( ${DRIVER_TEST_NAME} PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" - ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" - RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" + LIBRARY_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} + ARCHIVE_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} + RUNTIME_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} ) ########################################### diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index 286f0ed..dbc61fd 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -3,8 +3,9 @@ #include "Driver.h" #include "TempTextFile.h" -#include // for std::remove -#include // for std::system +#include // for std::replace +#include // for std::remove +#include // for std::system #include #include // for std::cout, std::endl #include // for std::string @@ -13,8 +14,10 @@ class DriverTest: public ::testing::Test { protected: void SetUp() override { // Get the name of the executable to test - executable = std::string(DRIVER_NAME); + executable = std::string(DRIVER_LOCATION); + executable += "/" + std::string(DRIVER_NAME); #ifdef _WIN32 + std::replace(executable.begin(), executable.end(), '/', '\\'); executable += ".exe"; #else executable = "./" + executable; @@ -55,6 +58,7 @@ class DriverTest: public ::testing::Test { const std::string &outFile ) { std::string cmd = BuildCommand(inFile, model, outFile); + std::cerr << "Running: " << cmd << std::endl; int rtn = std::system(cmd.c_str()); return rtn; } From fd35684df672f46755455f9759ade4b25763f178 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 24 Oct 2024 14:30:41 -0400 Subject: [PATCH 129/379] minor tweaks to prepare for template merge --- app/src/CMakeLists.txt | 1 - app/tests/TestDriver.cpp | 3 +++ app/tests/TestDriver.h | 15 +++++++++------ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index 715c0e1..4ba81cb 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -32,7 +32,6 @@ add_definitions(-DLIBRARY_NAME="${LIB_NAME}") add_definitions(-DDRIVER_NAME="${DRIVER_NAME}") # Set some target metadata - set_target_properties( ${DRIVER_NAME} PROPERTIES VERSION ${PROJECT_VERSION} diff --git a/app/tests/TestDriver.cpp b/app/tests/TestDriver.cpp index 2648a5b..6182da7 100644 --- a/app/tests/TestDriver.cpp +++ b/app/tests/TestDriver.cpp @@ -1,3 +1,6 @@ +/** @file TestDriver.cpp + * The main entrypoint for the TestDriver executable + */ #include "TestDriver.h" diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index dbc61fd..3c79d5e 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -1,14 +1,17 @@ +/** @file TestDriver.h + * Primary header for command line driver tests. + */ #pragma once #include "Driver.h" #include "TempTextFile.h" -#include // for std::replace -#include // for std::remove -#include // for std::system -#include -#include // for std::cout, std::endl -#include // for std::string +#include // for std::replace +#include // for std::remove +#include // for std::system +#include // GoogleTest +#include // for std::cout, std::endl +#include // for std::string class DriverTest: public ::testing::Test { protected: From e505cd0856f316956d929f4dda4ffa8c643fc9be Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 24 Oct 2024 14:31:52 -0400 Subject: [PATCH 130/379] Remove text output used for debugging --- app/tests/TestDriver.h | 1 - 1 file changed, 1 deletion(-) diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index 3c79d5e..7a47bfe 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -61,7 +61,6 @@ class DriverTest: public ::testing::Test { const std::string &outFile ) { std::string cmd = BuildCommand(inFile, model, outFile); - std::cerr << "Running: " << cmd << std::endl; int rtn = std::system(cmd.c_str()); return rtn; } From 8c11c0993a4da58b95f8af052531e7dde2f50025 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 24 Oct 2024 14:43:26 -0400 Subject: [PATCH 131/379] Update driver testing --- app/README.md | 21 ++-- app/include/Driver.h | 8 +- app/include/Errors.h | 15 +-- app/src/CommaSeparatedIterator.cpp | 4 + app/src/Driver.cpp | 65 +++++++---- app/src/DriverUtils.cpp | 11 ++ app/tests/CMakeLists.txt | 18 ++- app/tests/TempTextFile.cpp | 2 +- app/tests/TempTextFile.h | 3 + app/tests/TestDriver.cpp | 106 +++++++++--------- app/tests/TestDriver.h | 169 +++++++++++++++++++++++++++-- app/tests/TestDriverUtils.cpp | 49 --------- tests/CMakeLists.txt | 1 + 13 files changed, 319 insertions(+), 153 deletions(-) delete mode 100644 app/tests/TestDriverUtils.cpp diff --git a/app/README.md b/app/README.md index 921a392..3851894 100644 --- a/app/README.md +++ b/app/README.md @@ -56,7 +56,8 @@ order. A generic example of calling the command-line driver on Windows is: ## Command-line Driver Errors ## -(TODO-TEMPLATE: Update the below tables with your driver errors) +(TODO-TEMPLATE: Update the below tables with your driver errors. Align with app/Errors.h) + In addition to the return codes defined by the library itself, the command-line driver implements the following return codes. @@ -64,17 +65,17 @@ driver implements the following return codes. | Value | Const Name | Description | |-------|--------------------------------|--------------------------------------------| -| 1000 | `DRVR__RETURN_SUCCESS` | Successful execution | -| 1003 | `DRVRERR__INVALID_OPTION` | Unknown option specified | -| 1007 | `DRVRERR__OPENING_OUTPUT_FILE` | Failed to open the output file for writing | +| 1000 | `DRVR__RETURN_SUCCESS` | Internal driver success code | +| 1001 | `DRVRERR__MISSING_OPTION` | No value provided for given argument | +| 1002 | `DRVRERR__INVALID_OPTION` | Unknown option specified | +| 1003 | `DRVRERR__OPENING_INPUT_FILE` | Failed to open the input file for reading | +| 1004 | `DRVRERR__OPENING_OUTPUT_FILE` | Failed to open the output file for writing | ### Input File Parsing Errors ### | Value | Const Name | Description | |-------|-------------------------------|---------------------------------------| | 1100 | `DRVRERR__PARSE` | General input file parsing error | -| 1101 | `DRVRERR__PARSE_HTX` | Unable to parse transmitter height | - ### Validation Errors ### @@ -82,7 +83,7 @@ Driver validation errors occur when required command line arguments are missing. These validation errors are distinct from any defined within the library itself, which may include, e.g., parameter out-of-range errors. -| Value | Const Name | Description | -|-------|------------------------------------|---------------------------------------| -| 1202 | `DRVRERR__VALIDATION_IN_FILE` | Input parameter file is not specified | -| 1203 | `DRVRERR__VALIDATION_OUT_FILE` | Output file is not specified | +| Value | Const Name | Description | +|-------|------------------------------------|----------------------------------| +| 1200 | `DRVRERR__VALIDATION_IN_FILE` | Input file not specified | +| 1201 | `DRVRERR__VALIDATION_OUT_FILE` | Output file not specified | diff --git a/app/include/Driver.h b/app/include/Driver.h index ab36dfa..a2bee78 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -16,6 +16,7 @@ #include // for std::endl #include // for std::string, std::stoi, std::stod #include // for std::tie +#include // for std::vector ///////////////////////////// // Macros @@ -24,21 +25,22 @@ #define PRINT << std::endl << std::left << std::setw(25) << #define SETW13 << std::setw(13) << +////////////////////////////// +// Library Namespace // TODO-TEMPLATE: use the namespace of your library // using namespace ITS::YourLibraryNamespace::YourLibraryName; ///////////////////////////// // Functions - int ParseArguments(int argc, char **argv, DrvrParams ¶ms); void Help(std::ostream &os = std::cout); int ValidateInputs(const DrvrParams ¶ms); - // Driver Utils void Version(std::ostream &os = std::cout); int Validate_RequiredErrMsgHelper(const std::string &opt, const int err); int ParseInteger(const std::string &str, int &value); int ParseDouble(const std::string &str, double &value); int ParsingErrorHelper(const int err, const std::string &msg); -std::string GetDatetimeString(); \ No newline at end of file +std::string GetDatetimeString(); +void StringToLower(std::string &str); diff --git a/app/include/Errors.h b/app/include/Errors.h index d8c35cd..30a5e34 100644 --- a/app/include/Errors.h +++ b/app/include/Errors.h @@ -14,18 +14,19 @@ #endif /** Primary Return Codes (1000-1099) */ -#define DRVR__RETURN_SUCCESS 1000 -#define DRVRERR__INVALID_OPTION 1003 -#define DRVRERR__OPENING_INPUT_FILE 1006 -#define DRVRERR__OPENING_OUTPUT_FILE 1007 +#define DRVR__RETURN_SUCCESS 1000 /**< Internal driver success code */ +#define DRVRERR__MISSING_OPTION 1001 /**< No value provided for given argument */ +#define DRVRERR__INVALID_OPTION 1002 /**< Unknown option specified */ +#define DRVRERR__OPENING_INPUT_FILE 1003 /**< Failed to open the input file for reading */ +#define DRVRERR__OPENING_OUTPUT_FILE 1004 /**< Failed to open the output file for writing */ /** Input File Parsing Errors (1100-1199) */ -#define DRVRERR__PARSE 1100 +#define DRVRERR__PARSE 1100 /**< General error parsing inputs */ // TODO-TEMPLATE: Add driver error codes and document them in app/README.md /** Validation Errors (1200-1299) */ -#define DRVRERR__VALIDATION_IN_FILE 1202 -#define DRVRERR__VALIDATION_OUT_FILE 1203 +#define DRVRERR__VALIDATION_IN_FILE 1200 /**< Input file not specified */ +#define DRVRERR__VALIDATION_OUT_FILE 1201 /**< Output file not specified */ // clang-format on \ No newline at end of file diff --git a/app/src/CommaSeparatedIterator.cpp b/app/src/CommaSeparatedIterator.cpp index cfcf1e4..d653f6b 100644 --- a/app/src/CommaSeparatedIterator.cpp +++ b/app/src/CommaSeparatedIterator.cpp @@ -1,3 +1,7 @@ +/** @file CommaSeparatedIterator.cpp + * Implementation of class to read comma-delimited input text streams. +*/ + #include "CommaSeparatedIterator.h" #include // for std::ptrdiff_t diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index e72e247..86aae97 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -14,14 +14,14 @@ int main(int argc, char **argv) { rtn = ParseArguments(argc, argv, params); if (rtn == DRVR__RETURN_SUCCESS) return SUCCESS; - if (rtn) { + if (rtn != SUCCESS) { Help(); return rtn; } - // validate command line inputs + // Ensure required options were provided rtn = ValidateInputs(params); - if (rtn) { + if (rtn != SUCCESS) { Help(); return rtn; } @@ -29,7 +29,7 @@ int main(int argc, char **argv) { // TODO-TEMPLATE: Add driver logic, e.g. validating inputs and calling the model // Return driver error code if one was returned - if (rtn >= 1100) + if (rtn > DRVR__RETURN_SUCCESS) return rtn; // Print results to file @@ -60,33 +60,50 @@ int main(int argc, char **argv) { * @return Return code ******************************************************************************/ int ParseArguments(int argc, char **argv, DrvrParams ¶ms) { + // TODO-TEMPLATE: Populate vector with all valid arguments + const std::vector validArgs + = {"-i", "-o", "-h", "--help", "-v", "--version"}; + for (int i = 1; i < argc; i++) { + // Parse arg to lowercase string std::string arg(argv[i]); - std::transform(arg.begin(), arg.end(), arg.begin(), [](const char c) { - return static_cast(std::tolower(c)); - }); + StringToLower(arg); + + // Check if provided flag is valid + if (std::find(validArgs.begin(), validArgs.end(), arg) + == validArgs.end()) { + // Invalid argument provided + std::cerr << "Unknown option: " << argv[i] << std::endl; + return DRVRERR__INVALID_OPTION; + } - // TODO-TEMPLATE: Specify behavior based on command line arguments. + // Handle simple flags which don't have associated values (e.g., "-v", "-DBG") + if (arg == "-v" || arg == "--version") { + Version(); + return DRVR__RETURN_SUCCESS; + } else if (arg == "-h" || arg == "--help") { + Help(); + return DRVR__RETURN_SUCCESS; + // TODO-TEMPLATE handle any model input flags here + } else if (arg == "-dbg") { + params.DBG = true; + } + + // Check if end of arguments reached or next argument is another flag + if (i + 1 >= argc || argv[i + 1][0] == '-') { + std::cerr << "Error: no value given for " << arg << std::endl; + return DRVRERR__MISSING_OPTION; + } + + // TODO-TEMPLATE: Handle inputs which provide values (e.g. "-i in.txt"). // Template code will set in_file and out_file in DrvrParams based on -i // and -o options. It will also set DrvrParams.DBG based on a -dbg flag - // and will call Version() or Help() respectively if -v or -h is given. if (arg == "-i") { params.in_file = argv[i + 1]; i++; } else if (arg == "-o") { params.out_file = argv[i + 1]; i++; - } else if (arg == "-dbg") { - params.DBG = true; - } else if (arg == "-v") { - Version(); - return DRVR__RETURN_SUCCESS; - } else if (arg == "-h") { - Help(); - return DRVR__RETURN_SUCCESS; - } else { - std::cerr << "Unknown option: " << argv[i] << std::endl; - return DRVRERR__INVALID_OPTION; } } @@ -112,6 +129,9 @@ void Help(std::ostream &os) { << ".exe -i inputs.txt -t terrain.txt -o results.txt" << std::endl; os << "\t[LINUX] .\\" << DRIVER_NAME << " -i inputs.txt -t terrain.txt -o results.txt" << std::endl; + os << "Other Options (which don't run the model)" << std::endl; + os << "\t-h :: Display this help message" << std::endl; + os << "\t-v :: Display program version information" << std::endl; os << std::endl; }; @@ -125,12 +145,13 @@ void Help(std::ostream &os) { * @return Return code ******************************************************************************/ int ValidateInputs(const DrvrParams ¶ms) { + DrvrParams not_set; // TODO-TEMPLATE: Check that required inputs were provided. // This template code checks that input/output files were given with -i and -o - if (params.in_file.length() == 0) + if (params.in_file == not_set.in_file) return Validate_RequiredErrMsgHelper("-i", DRVRERR__VALIDATION_IN_FILE); - if (params.out_file.length() == 0) + if (params.out_file == not_set.out_file) return Validate_RequiredErrMsgHelper( "-o", DRVRERR__VALIDATION_OUT_FILE ); diff --git a/app/src/DriverUtils.cpp b/app/src/DriverUtils.cpp index b189770..6775406 100644 --- a/app/src/DriverUtils.cpp +++ b/app/src/DriverUtils.cpp @@ -119,4 +119,15 @@ std::string GetDatetimeString() { return "Could not format datetime string"; } return std::string(mbstr); +} + +/****************************************************************************** + * Convert a string to lowercase. + * + * @param[in, out] str The string to convert + ******************************************************************************/ +void StringToLower(std::string &str) { + std::transform(str.begin(), str.end(), str.begin(), [](const char c) { + return static_cast(std::tolower(c)); + }); } \ No newline at end of file diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index d9b11e2..0fa91d7 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -8,7 +8,6 @@ add_executable( ${DRIVER_TEST_NAME} "TempTextFile.cpp" "TestDriver.cpp" - "TestDriverUtils.cpp" "TempTextFile.h" "TestDriver.h" "${DRIVER_HEADERS}/Driver.h" @@ -24,8 +23,9 @@ target_include_directories( # Link the library to the executable target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME}) -# Make driver executable name available to source +# Make driver executable name and location available to source add_definitions(-DDRIVER_NAME="${DRIVER_NAME}") +add_definitions(-DDRIVER_LOCATION="${DRIVER_EXE_DIR}/${CMAKE_BUILD_TYPE}") set_target_properties( ${DRIVER_TEST_NAME} PROPERTIES @@ -33,3 +33,17 @@ set_target_properties( ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" ) + +########################################### +## SET UP AND DISCOVER TESTS +########################################### +if (NOT ${GOOGLETEST_ADDED}) + 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(GOOGLETEST_ADDED ON) +endif () + +target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME} GTest::gtest_main) +gtest_discover_tests(${DRIVER_TEST_NAME}) diff --git a/app/tests/TempTextFile.cpp b/app/tests/TempTextFile.cpp index 78ce497..80e25a2 100644 --- a/app/tests/TempTextFile.cpp +++ b/app/tests/TempTextFile.cpp @@ -1,5 +1,5 @@ /** @file TempFile.cpp - * Contains a class implementation to + * Implements a class to create and write to temporary text files */ #include "TempTextFile.h" diff --git a/app/tests/TempTextFile.h b/app/tests/TempTextFile.h index 47b3ee8..f70867d 100644 --- a/app/tests/TempTextFile.h +++ b/app/tests/TempTextFile.h @@ -1,3 +1,6 @@ +/** @file TempTextFile.h + * Header for a class which manages temporary text files. + */ #pragma once #include // for std::string diff --git a/app/tests/TestDriver.cpp b/app/tests/TestDriver.cpp index 3b6d032..91b0349 100644 --- a/app/tests/TestDriver.cpp +++ b/app/tests/TestDriver.cpp @@ -1,53 +1,61 @@ /** @file TestDriver.cpp - * The main entrypoint for the TestDriver executable + * General tests for the driver executable */ #include "TestDriver.h" -int main() { - // Driver executable needs to be in the same directory as this executable - std::string executable = std::string(DRIVER_NAME); - -#ifdef _WIN32 - executable += ".exe"; -#endif - - std::string command; - int rtn; - - // TODO-TEMPLATE - // Implement the set of arguments to use when test-running the driver. The last - // argument should be the expected return code. Example: if calling - // `MyDriver.exe -i input.txt -o output.txt` is expected to return 0, the vector - // argument would be: `{"-i", "input.txt", "-o", "output.txt", "0"}` - - // Input files must exist in the directory of this executable - std::vector> argSet = { - {"-i", "input.txt", "-o", "output.txt", "0"}, - }; - - int expected_rtn; - - for (auto args : argSet) { - expected_rtn = ParseInteger(args.back()); - args.pop_back(); - command = executable + " " + joinArguments(args); - std::cout << "Running command: " << command << std::endl; - // Suppress stdout when executable is called: -#ifdef _WIN32 - command += " > nul"; -#else - command += " > /dev/null"; -#endif - command += " 2>&1"; // Also suppress stderr - rtn = std::system(command.c_str()); - if (rtn != expected_rtn) { - std::cout << "[FAILURE] Returned " << rtn << ", expected " - << expected_rtn << std::endl; - } else { - std::cout << "[SUCCESS] Returned " << rtn << std::endl; - } - std::cout << std::endl; - } - - return SUCCESS; -} \ No newline at end of file +TEST_F(DriverTest, MissingOptionError1) { + // Test case: missing option between two provided flags + std::string cmd = executable + " -i -o out.txt"; + SuppressOutputs(cmd); + int rtn = std::system(cmd.c_str()); + EXPECT_EQ(rtn, DRVRERR__MISSING_OPTION); +} + +TEST_F(DriverTest, MissingOptionError2) { + // Test case: missing option at the end of command + std::string cmd = executable + " -i"; + SuppressOutputs(cmd); + int rtn = std::system(cmd.c_str()); + EXPECT_EQ(rtn, DRVRERR__MISSING_OPTION); +} + +TEST_F(DriverTest, InvalidOptionError) { + std::string cmd = executable + " -X"; + SuppressOutputs(cmd); + int rtn = std::system(cmd.c_str()); + EXPECT_EQ(rtn, DRVRERR__INVALID_OPTION); +} + +TEST_F(DriverTest, OpeningInputFileError) { + // TODO-TEMPLATE: Update this call to RunDriver + int rtn = RunDriver("/invalid/path/input.xyz", true, "out.txt"); + EXPECT_EQ(rtn, DRVRERR__OPENING_INPUT_FILE); +} + +TEST_F(DriverTest, OpeningOutputFileError) { + // TODO-TEMPLATE: Update this call to RunDriverWithInputFile + // Provide valid inputs but invalid output file path + std::string inputs = "template,0.0"; + int rtn = RunDriverWithInputFile(inputs, true, "/invalid/path/output.xyz"); + EXPECT_EQ(rtn, DRVRERR__OPENING_OUTPUT_FILE); +} + +TEST_F(DriverTest, ValidationInFileError) { + std::string cmd = executable + " -o out.txt"; + SuppressOutputs(cmd); + int rtn = std::system(cmd.c_str()); + EXPECT_EQ(rtn, DRVRERR__VALIDATION_IN_FILE); +} + +TEST_F(DriverTest, ValidationOutFileError) { + // Input file does not need to exist here, just has to be specified + // TODO-TEMPLATE May need to update the command here + std::string cmd = executable + " -i in.txt"; + SuppressOutputs(cmd); + int rtn = std::system(cmd.c_str()); + EXPECT_EQ(rtn, DRVRERR__VALIDATION_OUT_FILE); +} + +// TODO-TEMPLATE: Add tests for any additional validation errors + +// TODO-TEMPALTE: Add other general tests for the driver \ No newline at end of file diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index b67854c..93e1269 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -1,19 +1,168 @@ /** @file TestDriver.h - * Primary header for command line driver tests. + * Primary header and test fixture for command line driver tests. */ #pragma once #include "Driver.h" #include "TempTextFile.h" -#include // for std::system -#include // for std::ostringstream -#include // for std::string -#include +#include // for std::replace +#include // for std::remove +#include // for std::system +#include // GoogleTest +#include // for std::cout, std::endl +#include // for std::string -////////////////////// -// FUNCTIONS -std::string joinArguments(const std::vector &args); -void appendDirectorySep(std::string &str); -int ParseInteger(const std::string &str); +/******************************************************************************* + * @class DriverTest + * Test fixture for running the driver executable tests. + * + * This class extends the Google Test framework's Test class and provides + * utilities to set up, execute, and manage the output of the driver executable. + ******************************************************************************/ +class DriverTest: public ::testing::Test { + protected: + /*********************************************************************** + * Sets up the test environment. + * + * Initializes the executable path based on the platform. On Windows, + * it adjusts the path separators and appends the ".exe" extension. + **********************************************************************/ + void SetUp() override { + // Get the name of the executable to test + executable = std::string(DRIVER_LOCATION); + executable += "/" + std::string(DRIVER_NAME); +#ifdef _WIN32 + std::replace(executable.begin(), executable.end(), '/', '\\'); + executable += ".exe"; +#else + executable = "./" + executable; +#endif + } + + /*********************************************************************** + * Suppresses the output of the command. + * + * Appends redirection to suppress standard output and error based on + * the platform. + * + * @param[in, out] cmd The command string to modify + **********************************************************************/ + void SuppressOutputs(std::string &cmd) { +#ifdef _WIN32 + cmd += " > nul"; +#else + cmd += " > /dev/null"; +#endif + cmd += " 2>&1"; + } + + /*********************************************************************** + * Builds the command string to run the driver. + * + * Constructs a command string using the provided input file, model, + * and output file. Optionally suppresses outputs. + * + * @param[in] inFile The input file path + * @param[in] debug The debug flag + * @param[in] outFile The output file path + * @param[in] suppressOutputs Whether to suppress outputs (default: true) + * @return The constructed command string + **********************************************************************/ + std::string BuildCommand( + const std::string &inFile, + const bool debug, + const std::string &outFile, + const bool suppressOutputs = true + ) { + // TODO-TEMPLATE: Modify this function to accept all necessary + // parameters to construct a string to call the driver under test. + + // Construct command from parameters + std::string command = executable; + command += " -i " + inFile; + if (debug) { + command += " -DBG"; + } + command += " -o " + outFile; + + // Suppress text output of the driver, to avoid cluttering + // test outputs. + if (suppressOutputs) { + SuppressOutputs(command); + } + // Return the full command string + return command; + } + + /*********************************************************************** + * Runs the driver executable. + * + * @param[in] inFile The input file path + * @param[in] debug The debug flag value + * @param[in] outFile The output file path + * @return The return code from the driver execution + **********************************************************************/ + int RunDriver( + const std::string &inFile, + const bool debug, + const std::string &outFile + ) { + // TODO update this to use DrvrParams + // TODO-TEMPLATE: Update this function based on required driver inputs + std::string cmd = BuildCommand(inFile, debug, outFile); + int rtn = std::system(cmd.c_str()); + return rtn; + } + + /*********************************************************************** + * Runs the driver using the specified input file contents. + * + * Creates a temporary file with the given contents and executes the + * driver, then cleans up the output file. + * + * @param[in] inFileContents The contents to write to the input file + * @param[in] debug The debug flag value + * @param[in] outFile Output file path (default is "tmp_out.txt") + * @return Return code from the driver execution + **********************************************************************/ + int RunDriverWithInputFile( + const std::string &inFileContents, + const bool debug, + const std::string &outFile = "tmp_out.txt" + ) { + // TODO update this to use drvrparams + TempTextFile tempFile(inFileContents); + std::string inFile = tempFile.getFileName(); + int rtn = RunDriver(inFile, debug, outFile); + // Cleanup: delete output file if it was created + DeleteOutputFile(outFile); + return rtn; + } + + /*********************************************************************** + * Deletes the specified output file if it exists. + * + * Checks if the file exists and attempts to delete it. Reports any + * errors encountered during deletion. + * + * @param[in] fileName The name of the file to delete. + **********************************************************************/ + void DeleteOutputFile(const std::string &fileName) { + bool fileExists = false; +#ifdef _WIN32 + fileExists = _access(fileName.c_str(), 0) == 0; +#else + fileExists = access(fileName.c_str(), F_OK) == 0; +#endif + if (fileExists) { + if (std::remove(fileName.c_str()) != 0) { + std::perror("Error deleting output file"); + } + } + } + + // Holds the platform-dependent string to call the executable + std::string executable; +}; diff --git a/app/tests/TestDriverUtils.cpp b/app/tests/TestDriverUtils.cpp deleted file mode 100644 index 0b71813..0000000 --- a/app/tests/TestDriverUtils.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/** @file TestDriverUtils.cpp - * Utility functions for the TestDriver executable - */ - -#include "TestDriver.h" - -#include - -std::string joinArguments(const std::vector &args) { - std::ostringstream oss; - for (std::string arg : args) { - oss << arg + " "; - } - return oss.str(); -} - -void appendDirectorySep(std::string &str) { -#ifdef _WIN32 - str += "\\"; -#else - str += "/"; -#endif -} - -/******************************************************************************* - * Parse an integer value from the input string - * - * @param[in] str Input value as string - * @return Parsed integer value - ******************************************************************************/ -int ParseInteger(const std::string &str) { - int value; - try { - size_t pos; - value = std::stoi(str, &pos, 10); - - // Verify the entire string was parsed - if (pos != str.size()) { - throw std::invalid_argument( - "Input string contains non-numeric characters" - ); - } - } catch (...) { - // error parsing the input string value - throw std::runtime_error("Could not parse integer value"); - }; - - return value; -} \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index af6072a..d4b0042 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -29,5 +29,6 @@ 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(GOOGLETEST_ADDED ON) target_link_libraries(${TEST_NAME} ${LIB_NAME} GTest::gtest_main) gtest_discover_tests(${TEST_NAME}) \ No newline at end of file From e8ed000a132da3304aee1f0507b6bb109cac2123 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 24 Oct 2024 14:57:07 -0400 Subject: [PATCH 132/379] Minor formatting adjustments --- app/include/Errors.h | 4 ++-- app/src/Driver.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/include/Errors.h b/app/include/Errors.h index 30a5e34..25865ff 100644 --- a/app/include/Errors.h +++ b/app/include/Errors.h @@ -26,7 +26,7 @@ // TODO-TEMPLATE: Add driver error codes and document them in app/README.md /** Validation Errors (1200-1299) */ -#define DRVRERR__VALIDATION_IN_FILE 1200 /**< Input file not specified */ -#define DRVRERR__VALIDATION_OUT_FILE 1201 /**< Output file not specified */ +#define DRVRERR__VALIDATION_IN_FILE 1200 /**< Input file not specified */ +#define DRVRERR__VALIDATION_OUT_FILE 1201 /**< Output file not specified */ // clang-format on \ No newline at end of file diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index 86aae97..1976a1d 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -125,10 +125,10 @@ void Help(std::ostream &os) { os << "\t-dbg :: Dump intermediate values to output file [optional]" << std::endl; os << std::endl << "Examples:" << std::endl; - os << "\t[WINDOWS] " << DRIVER_NAME - << ".exe -i inputs.txt -t terrain.txt -o results.txt" << std::endl; - os << "\t[LINUX] .\\" << DRIVER_NAME - << " -i inputs.txt -t terrain.txt -o results.txt" << std::endl; + os << "\t[WINDOWS] " << DRIVER_NAME << ".exe -i in.txt -o out.txt" + << std::endl; + os << "\t[LINUX] .\\" << DRIVER_NAME << " -i in.txt -o out.txt" + << std::endl; os << "Other Options (which don't run the model)" << std::endl; os << "\t-h :: Display this help message" << std::endl; os << "\t-v :: Display program version information" << std::endl; From ddf356b600b3bcc01604020df7eed9acdf14525a Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 24 Oct 2024 16:25:32 -0400 Subject: [PATCH 133/379] Use drvrparams in driver tests --- app/tests/TestDriver.cpp | 8 +++-- app/tests/TestDriver.h | 76 ++++++++++++++++++---------------------- 2 files changed, 41 insertions(+), 43 deletions(-) diff --git a/app/tests/TestDriver.cpp b/app/tests/TestDriver.cpp index 91b0349..0d4cc61 100644 --- a/app/tests/TestDriver.cpp +++ b/app/tests/TestDriver.cpp @@ -28,7 +28,9 @@ TEST_F(DriverTest, InvalidOptionError) { TEST_F(DriverTest, OpeningInputFileError) { // TODO-TEMPLATE: Update this call to RunDriver - int rtn = RunDriver("/invalid/path/input.xyz", true, "out.txt"); + params.in_file = "/invalid/path/input.xyz"; + params.DBG = false; + int rtn = RunDriver(params); EXPECT_EQ(rtn, DRVRERR__OPENING_INPUT_FILE); } @@ -36,7 +38,9 @@ TEST_F(DriverTest, OpeningOutputFileError) { // TODO-TEMPLATE: Update this call to RunDriverWithInputFile // Provide valid inputs but invalid output file path std::string inputs = "template,0.0"; - int rtn = RunDriverWithInputFile(inputs, true, "/invalid/path/output.xyz"); + params.DBG = true; + params.out_file = "/invalid/path/output.xyz"; + int rtn = RunDriverWithInputFile(inputs, params); EXPECT_EQ(rtn, DRVRERR__OPENING_OUTPUT_FILE); } diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index 93e1269..2b2d23e 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -25,11 +25,13 @@ class DriverTest: public ::testing::Test { protected: /*********************************************************************** * Sets up the test environment. - * - * Initializes the executable path based on the platform. On Windows, - * it adjusts the path separators and appends the ".exe" extension. **********************************************************************/ void SetUp() override { + // TODO-TEMPLATE review and optionally adjust default params here + // Set the default driver params + params.DBG = false; + params.out_file = "tmp_out.txt"; + // Get the name of the executable to test executable = std::string(DRIVER_LOCATION); executable += "/" + std::string(DRIVER_NAME); @@ -61,31 +63,27 @@ class DriverTest: public ::testing::Test { /*********************************************************************** * Builds the command string to run the driver. * - * Constructs a command string using the provided input file, model, - * and output file. Optionally suppresses outputs. + * Constructs a command string using the provided driver parameters + * struct. Optionally, the command can be written such that stdout and + * are suppressed. * - * @param[in] inFile The input file path - * @param[in] debug The debug flag - * @param[in] outFile The output file path + * @param[in] params The driver parameters * @param[in] suppressOutputs Whether to suppress outputs (default: true) * @return The constructed command string **********************************************************************/ std::string BuildCommand( - const std::string &inFile, - const bool debug, - const std::string &outFile, - const bool suppressOutputs = true + const DrvrParams ¶ms, const bool suppressOutputs = true ) { - // TODO-TEMPLATE: Modify this function to accept all necessary - // parameters to construct a string to call the driver under test. + // TODO-TEMPLATE: Modify this function to correctly + // unpack the DrvrParams struct and build the command // Construct command from parameters std::string command = executable; - command += " -i " + inFile; - if (debug) { + command += " -i " + params.in_file; + if (params.DBG) { command += " -DBG"; } - command += " -o " + outFile; + command += " -o " + params.out_file; // Suppress text output of the driver, to avoid cluttering // test outputs. @@ -99,19 +97,11 @@ class DriverTest: public ::testing::Test { /*********************************************************************** * Runs the driver executable. * - * @param[in] inFile The input file path - * @param[in] debug The debug flag value - * @param[in] outFile The output file path - * @return The return code from the driver execution + * @param[in] params Parameters to parse as command line arguments + * @return Return code from the driver execution **********************************************************************/ - int RunDriver( - const std::string &inFile, - const bool debug, - const std::string &outFile - ) { - // TODO update this to use DrvrParams - // TODO-TEMPLATE: Update this function based on required driver inputs - std::string cmd = BuildCommand(inFile, debug, outFile); + int RunDriver(const DrvrParams ¶ms) { + std::string cmd = BuildCommand(params); int rtn = std::system(cmd.c_str()); return rtn; } @@ -119,25 +109,26 @@ class DriverTest: public ::testing::Test { /*********************************************************************** * Runs the driver using the specified input file contents. * - * Creates a temporary file with the given contents and executes the - * driver, then cleans up the output file. + * This method creates a temporary text file containing the contents + * of `inFileContents` and then runs the driver using the temporary + * file as the input file. The rest of the required driver parameters + * are provided in the `params` input; the `params.in_file` value is + * ignored and can be unset. If an output file was produced by the + * driver, it is deleted before this method returns. * * @param[in] inFileContents The contents to write to the input file - * @param[in] debug The debug flag value - * @param[in] outFile Output file path (default is "tmp_out.txt") + * @param[in] params A populated driver parameters struct (see above) * @return Return code from the driver execution **********************************************************************/ int RunDriverWithInputFile( - const std::string &inFileContents, - const bool debug, - const std::string &outFile = "tmp_out.txt" + const std::string &inFileContents, const DrvrParams ¶ms ) { - // TODO update this to use drvrparams + DrvrParams updated_params = params; TempTextFile tempFile(inFileContents); - std::string inFile = tempFile.getFileName(); - int rtn = RunDriver(inFile, debug, outFile); + updated_params.in_file = tempFile.getFileName(); + int rtn = RunDriver(updated_params); // Cleanup: delete output file if it was created - DeleteOutputFile(outFile); + DeleteOutputFile(updated_params.out_file); return rtn; } @@ -163,6 +154,9 @@ class DriverTest: public ::testing::Test { } } - // Holds the platform-dependent string to call the executable + // Platform-dependent string to call the executable std::string executable; + + // Driver parameters struct which may be used by tests + DrvrParams params; }; From b1f0c250972ad20e7b19b181e21d0df0419ce49b Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 24 Oct 2024 16:35:20 -0400 Subject: [PATCH 134/379] Update driver tests to use drvrparams --- app/tests/TestDriver.h | 17 ++++++++++------- app/tests/TestDriverASM.cpp | 12 ++++++++---- app/tests/TestDriverHGTCM.cpp | 12 ++++++++---- app/tests/TestDriverTSM.cpp | 11 ++++++++--- 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index 2b2d23e..d59a461 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -27,9 +27,7 @@ class DriverTest: public ::testing::Test { * Sets up the test environment. **********************************************************************/ void SetUp() override { - // TODO-TEMPLATE review and optionally adjust default params here // Set the default driver params - params.DBG = false; params.out_file = "tmp_out.txt"; // Get the name of the executable to test @@ -74,14 +72,19 @@ class DriverTest: public ::testing::Test { std::string BuildCommand( const DrvrParams ¶ms, const bool suppressOutputs = true ) { - // TODO-TEMPLATE: Modify this function to correctly - // unpack the DrvrParams struct and build the command - // Construct command from parameters std::string command = executable; command += " -i " + params.in_file; - if (params.DBG) { - command += " -DBG"; + switch (params.model) { + case P2108Model::HGTCM: + command += " -model HGTCM"; + break; + case P2108Model::TSM: + command += " -model TSM"; + break; + case P2108Model::ASM: + command += " -model ASM"; + break; } command += " -o " + params.out_file; diff --git a/app/tests/TestDriverASM.cpp b/app/tests/TestDriverASM.cpp index 9d2a9c8..0460d7b 100644 --- a/app/tests/TestDriverASM.cpp +++ b/app/tests/TestDriverASM.cpp @@ -1,16 +1,20 @@ #include "TestDriver.h" -// Test fixture for the unit tests class ASMDriverTest: public DriverTest { protected: - void - TestASM(const std::string &inFileContents, const int expected_rtn) { + void SetUp() override { + DriverTest::SetUp(); + asm_params.model = P2108Model::ASM; + asm_params.out_file = params.out_file; + } + void TestASM(const std::string &inputs, const int expected_rtn) { int asm_rtn; - asm_rtn = RunDriverWithInputFile(inFileContents, "ASM"); + asm_rtn = RunDriverWithInputFile(inputs, asm_params); EXPECT_EQ(asm_rtn, expected_rtn); } std::string ASMInputs; + DrvrParams asm_params; }; TEST_F(ASMDriverTest, TestSuccess) { diff --git a/app/tests/TestDriverHGTCM.cpp b/app/tests/TestDriverHGTCM.cpp index 467daa6..da2fff4 100644 --- a/app/tests/TestDriverHGTCM.cpp +++ b/app/tests/TestDriverHGTCM.cpp @@ -2,15 +2,19 @@ class HGTCMDriverTest: public DriverTest { protected: - void TestHGTCM( - const std::string &inFileContents, const int expected_rtn - ) { + void SetUp() override { + DriverTest::SetUp(); + hgtcm_params.model = P2108Model::HGTCM; + hgtcm_params.out_file = params.out_file; + } + void TestHGTCM(const std::string &inputs, const int expected_rtn) { int hgtcm_rtn; - hgtcm_rtn = RunDriverWithInputFile(inFileContents, "HGTCM"); + hgtcm_rtn = RunDriverWithInputFile(inputs, hgtcm_params); EXPECT_EQ(hgtcm_rtn, expected_rtn); } std::string HGTCMInputs; + DrvrParams hgtcm_params; }; TEST_F(HGTCMDriverTest, TestSuccess) { diff --git a/app/tests/TestDriverTSM.cpp b/app/tests/TestDriverTSM.cpp index 36b3e32..11aed17 100644 --- a/app/tests/TestDriverTSM.cpp +++ b/app/tests/TestDriverTSM.cpp @@ -2,14 +2,19 @@ class TSMDriverTest: public DriverTest { protected: - void - TestTSM(const std::string &inFileContents, const int expected_rtn) { + void SetUp() override { + DriverTest::SetUp(); + tsm_params.model = P2108Model::TSM; + tsm_params.out_file = params.out_file; + } + void TestTSM(const std::string &inputs, const int expected_rtn) { int tsm_rtn; - tsm_rtn = RunDriverWithInputFile(inFileContents, "TSM"); + tsm_rtn = RunDriverWithInputFile(inputs, tsm_params); EXPECT_EQ(tsm_rtn, expected_rtn); } std::string TSMInputs; + DrvrParams tsm_params; }; TEST_F(TSMDriverTest, TestSuccess) { From ddfe7efc921f9fb9a93b6106cbeeb75cbe8c32c5 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 24 Oct 2024 16:56:47 -0400 Subject: [PATCH 135/379] Update CLI readme --- app/README.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/app/README.md b/app/README.md index 6e44614..2c4692a 100644 --- a/app/README.md +++ b/app/README.md @@ -1,4 +1,4 @@ -# Command-line Driver # TODO +# Command-line Driver # This document explains the use of the included command-line driver. This is a supplemental software tool to allow a user to call the compiled propagation library @@ -9,8 +9,7 @@ from the command-line using text files to provide inputs and store outputs. Inputs to the command-line driver are specified in an ASCII text file using the common `key,value` format. Each line holds a single `key,value` combination, with the `key` representing the model input variable name and the `value` representing -its value. An example is included in this repository [here](./data/in.txt). -(TODO-TEMPLATE update the above example data info/link) +its value. ## Output Files ## @@ -24,13 +23,11 @@ documentation. Executing the command-line driver requires specifying input arguments, defined in the below table: -(TODO-TEMPLATE review and update the flags below) -| Flag | Type | Required | Description | -|--------|--------|----------|-----------------------------------------------------------------------| -| `-i` | string | True | File specifying model input parameters in `key,value` format | -| `-o` | string | True | Filename where output results should be written | -| `-t` | string | True | File containing comma-delimited terrain elevation data along the path | -| `-dbg` | N/A | False | If specified, intermediate values will be written to the output file | +| Flag | Type | Required | Description | +|----------|--------|----------|--------------------------------------------------------------| +| `-i` | string | true | File specifying model input parameters in `key,value` format | +| `-model` | string | true | Which model to run, one of `HGTCM`, `TSM`, or `ASM` | +| `-o` | string | true | Filename where output results should be written | Additional arguments are available to print help text and version information: @@ -42,18 +39,21 @@ Additional arguments are available to print help text and version information: Input arguments are not case sensitive and do not have to be specified in a certain order. A generic example of calling the command-line driver on Windows is: -(TODO-TEMPLATE: update example driver command below) ```cmd -.exe -i -o +P2108Driver.exe -i input.txt -model ASM -o output.txt ``` ### Examples ### -| Input File | Output File | Arguments | -|-------------------------------------|-------------------------------------|---------------------------------| -| [`i_asm.txt`](./data/i_asm.txt) | [`o_asm.txt`](./data/o_asm.txt) | `-i i_asm.txt -o o_asm.txt` | -| [`i_hgtcm.txt`](./data/i_hgtcm.txt) | [`o_hgtcm.txt`](./data/o_hgtcm.txt) | `-i i_hgtcm.txt -o o_hgtcm.txt` | -| [`i_tsm.txt`](./data/i_tsm.txt) | [`o_tsm.txt`](./data/o_tsm.txt) | `-i i_tsm.txt -o o_tsm.txt` | +The following files are included as references for the functionality of this software. +Using these input files and the commands specified should produce outputs identical +to the provided corresponding output files. + +| Input File | Output File | Arguments | +|-------------------------------------|-------------------------------------|----------------------------------------------| +| [`i_hgtcm.txt`](./data/i_hgtcm.txt) | [`o_hgtcm.txt`](./data/o_hgtcm.txt) | `-i i_hgtcm.txt -model HGTCM -o o_hgtcm.txt` | +| [`i_tsm.txt`](./data/i_tsm.txt) | [`o_tsm.txt`](./data/o_tsm.txt) | `-i i_tsm.txt -model TSM -o o_tsm.txt` | +| [`i_asm.txt`](./data/i_asm.txt) | [`o_asm.txt`](./data/o_asm.txt) | `-i i_asm.txt -model ASM -o o_asm.txt` | ## Command-line Driver Errors ## From f354f446fe0b538174c63857dab827178cab1400 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 24 Oct 2024 16:57:37 -0400 Subject: [PATCH 136/379] Update README.md --- app/README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/app/README.md b/app/README.md index 3851894..66bc179 100644 --- a/app/README.md +++ b/app/README.md @@ -9,8 +9,7 @@ from the command-line using text files to provide inputs and store outputs. Inputs to the command-line driver are specified in an ASCII text file using the common `key,value` format. Each line holds a single `key,value` combination, with the `key` representing the model input variable name and the `value` representing -its value. An example is included in this repository [here](./data/in.txt). -(TODO-TEMPLATE update the above example data info/link) +its value. ## Output Files ## @@ -27,10 +26,9 @@ in the below table: (TODO-TEMPLATE review and update the flags below) | Flag | Type | Required | Description | |--------|--------|----------|-----------------------------------------------------------------------| -| `-i` | string | True | File specifying model input parameters in `key,value` format | -| `-o` | string | True | Filename where output results should be written | -| `-t` | string | True | File containing comma-delimited terrain elevation data along the path | -| `-dbg` | N/A | False | If specified, intermediate values will be written to the output file | +| `-i` | string | true | File specifying model input parameters in `key,value` format | +| `-o` | string | true | Filename where output results should be written | +| `-dbg` | N/A | false | If specified, intermediate values will be written to the output file | Additional arguments are available to print help text and version information: @@ -44,11 +42,15 @@ order. A generic example of calling the command-line driver on Windows is: (TODO-TEMPLATE: update example driver command below) ```cmd -.exe -i -t -o +.exe -i -dbg -o ``` ### Examples ### +The following files are included as references for the functionality of this software. +Using these input files and the commands specified should produce outputs identical +to the provided corresponding output files. + (TODO-TEMPLATE: Provide all included examples in the table below) | Input File | Terrain File | Output File | Arguments | |---------------------------|-----------------------------------------------------|-----------------------------|----------------------------------------------------| From 6cd1cf1745197a34318fe92c4fac2d6e7373163f Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:43:54 -0400 Subject: [PATCH 137/379] Reuse stringtolower in CSV parser --- app/src/CommaSeparatedIterator.cpp | 13 ++++--------- app/tests/TempTextFile.cpp | 2 +- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/app/src/CommaSeparatedIterator.cpp b/app/src/CommaSeparatedIterator.cpp index d653f6b..ac428ed 100644 --- a/app/src/CommaSeparatedIterator.cpp +++ b/app/src/CommaSeparatedIterator.cpp @@ -1,9 +1,10 @@ /** @file CommaSeparatedIterator.cpp * Implementation of class to read comma-delimited input text streams. */ - #include "CommaSeparatedIterator.h" +#include "Driver.h" + #include // for std::ptrdiff_t #include // for std::input_iterator_tag @@ -39,14 +40,8 @@ CommaSeparatedIterator &CommaSeparatedIterator::operator++() { } // Convert both substrings to lowercase - auto toLower = [](std::string &s) { - std::transform(s.begin(), s.end(), s.begin(), [](const char c) { - return static_cast(std::tolower(c)); - }); - }; - toLower(first_); - toLower(second_); - + StringToLower(first_); + StringToLower(second_); } else { if (stream_.bad()) { throw std::runtime_error("Error reading stream."); diff --git a/app/tests/TempTextFile.cpp b/app/tests/TempTextFile.cpp index 80e25a2..ab619ff 100644 --- a/app/tests/TempTextFile.cpp +++ b/app/tests/TempTextFile.cpp @@ -1,4 +1,4 @@ -/** @file TempFile.cpp +/** @file TempTextFile.cpp * Implements a class to create and write to temporary text files */ #include "TempTextFile.h" From 2bd0e2cdc731021ec1bf50a7d6c7ab81320be7a9 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 24 Oct 2024 18:04:12 -0400 Subject: [PATCH 138/379] Document driver with Doxygen --- app/include/CommaSeparatedIterator.h | 21 +++++-- app/include/Driver.h | 3 +- app/include/Errors.h | 6 +- app/include/Labels.h | 56 ++++++++++--------- app/include/Structs.h | 48 +++++++++------- app/include/Tags.h | 3 + app/src/AeronauticalStatisticalModel.cpp | 6 +- app/src/Driver.cpp | 4 ++ app/src/HeightGainTerminalCorrectionModel.cpp | 6 +- app/src/TerrestrialStatisticalModel.cpp | 6 +- app/tests/TempTextFile.cpp | 2 +- app/tests/TestDriver.cpp | 1 - app/tests/TestDriver.h | 4 +- app/tests/TestDriverASM.cpp | 7 ++- app/tests/TestDriverHGTCM.cpp | 7 ++- app/tests/TestDriverTSM.cpp | 7 ++- docs/CMakeLists.txt | 2 + docs/doxy_mainpage.md | 5 +- 18 files changed, 118 insertions(+), 76 deletions(-) diff --git a/app/include/CommaSeparatedIterator.h b/app/include/CommaSeparatedIterator.h index f1d6605..2e7b646 100644 --- a/app/include/CommaSeparatedIterator.h +++ b/app/include/CommaSeparatedIterator.h @@ -18,14 +18,27 @@ ******************************************************************************/ class CommaSeparatedIterator { public: + /** Type alias for the value returned by the iterator (pair of strings) */ using value_type = std::pair; + + /*********************************************************************** + * Constructor method + * + * @param[in] stream The input stream which will be read + **********************************************************************/ CommaSeparatedIterator(std::istream &stream); + + /** Pre-increment operator to advance the iterator to the next line */ CommaSeparatedIterator &operator++(); + + /** Dereference operator to obtain the current pair of substrings */ value_type operator*() const; + + /** Conversion to boolean to check if the iterator is valid */ explicit operator bool() const; private: - std::istream &stream_; - std::string line_; - std::string first_; - std::string second_; + std::istream &stream_; /**< Reference to the input stream */ + std::string line_; /**< Current line read from the stream */ + std::string first_; /**< First string from the current line */ + std::string second_; /**< Second string from the current line */ }; diff --git a/app/include/Driver.h b/app/include/Driver.h index b04b6e8..a0aa2d9 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -19,8 +19,9 @@ ///////////////////////////// // Macros -// Make print-to-file statements more concise in driver +/** Shortcut for concise print-to-file statements in driver */ #define PRINT << std::endl << std::left << std::setw(25) << +/** Shortcut for setting fixed whitespace padding in driver file output */ #define SETW13 << std::setw(13) << ////////////////////////////// diff --git a/app/include/Errors.h b/app/include/Errors.h index eb38cfa..fce0bae 100644 --- a/app/include/Errors.h +++ b/app/include/Errors.h @@ -10,14 +10,14 @@ #define SUCCESS 0 /**< Successful execution */ #endif -/** Primary Return Codes (1000-1099) */ +// Primary Return Codes (1000-1099) #define DRVR__RETURN_SUCCESS 1000 /**< Internal driver success code */ #define DRVRERR__MISSING_OPTION 1001 /**< No value provided for given argument */ #define DRVRERR__INVALID_OPTION 1002 /**< Unknown option specified */ #define DRVRERR__OPENING_INPUT_FILE 1003 /**< Failed to open the input file for reading */ #define DRVRERR__OPENING_OUTPUT_FILE 1004 /**< Failed to open the output file for writing */ -/** Input File Parsing Errors (1100-1199) */ +// Input File Parsing Errors (1100-1199) #define DRVRERR__PARSE 1100 /**< General error parsing inputs */ #define DRVRERR__PARSE_FREQ 1101 /**< Error parsing frequency value */ #define DRVRERR__PARSE_THETA 1102 /**< Error parsing theta value */ @@ -28,7 +28,7 @@ #define DRVRERR__PARSE_CLUTTER_TYPE 1107 /**< Error parsing clutter type value */ #define DRVRERR__PARSE_PATH_DIST 1108 /**< Error parsing path distance value */ -/** Validation Errors (1200-1299) */ +// Validation Errors (1200-1299) #define DRVRERR__VALIDATION_IN_FILE 1200 /**< Input file not specified */ #define DRVRERR__VALIDATION_OUT_FILE 1201 /**< Output file not specified */ #define DRVRERR__VALIDATION_MODEL 1202 /**< Model not specified */ diff --git a/app/include/Labels.h b/app/include/Labels.h index e915253..b353506 100644 --- a/app/include/Labels.h +++ b/app/include/Labels.h @@ -5,37 +5,41 @@ // clang-format off +// Skip Doxygen generation for labels +#ifndef DOXYGEN_SHOULD_SKIP + /** Labels */ -#define LBL__SUCCESS "Success - No Errors" -#define LBL__SUCCESS_WITH_WARNINGS "Success - but with warnings" -#define LBL__NO_WARNINGS "No Warnings" +#define LBL__SUCCESS "Success - No Errors" +#define LBL__SUCCESS_WITH_WARNINGS "Success - but with warnings" +#define LBL__NO_WARNINGS "No Warnings" /** P.2108 Model Names */ -#define LBL__HGTCM "Height Gain Terminal Correction Model" -#define LBL__TSM "Terrestrial Statistical Model" -#define LBL__ASM "Aeronautical Statistical Model" +#define LBL__HGTCM "Height Gain Terminal Correction Model" +#define LBL__TSM "Terrestrial Statistical Model" +#define LBL__ASM "Aeronautical Statistical Model" /** Height Gain Terminal Correction Clutter Types */ -#define LBL__CLUTTERTYPE_WATER_SEA "Water/sea clutter type" -#define LBL__CLUTTERTYPE_OPEN_RURAL "Open/rural clutter type" -#define LBL__CLUTTERTYPE_SUBURBAN "Suburban clutter type" -#define LBL__CLUTTERTYPE_URBAN "Urban clutter type" -#define LBL__CLUTTERTYPE_TREES_FOREST "Trees/forest clutter type" -#define LBL__CLUTTERTYPE_DENSE_URBAN "Dense urban clutter type" +#define LBL__CLUTTERTYPE_WATER_SEA "Water/sea clutter type" +#define LBL__CLUTTERTYPE_OPEN_RURAL "Open/rural clutter type" +#define LBL__CLUTTERTYPE_SUBURBAN "Suburban clutter type" +#define LBL__CLUTTERTYPE_URBAN "Urban clutter type" +#define LBL__CLUTTERTYPE_TREES_FOREST "Trees/forest clutter type" +#define LBL__CLUTTERTYPE_DENSE_URBAN "Dense urban clutter type" /** Error Definitions */ -#define LBL__ERROR_INVALID_VALUE "Invalid Value" -#define LBL__ERROR_UNKNOWN "An unknown error occurred" -#define LBL__ERROR31_FREQUENCY "Frequency must be between 0.3 and 3 GHz, inclusive" -#define LBL__ERROR31_ANTENNA_HEIGHT "Antenna height must be >= 0 meters" -#define LBL__ERROR31_STREET_WIDTH "Street width must be > 0 meters" -#define LBL__ERROR31_CLUTTER_HEIGHT "Representative clutter height must be > 0 meters" -#define LBL__ERROR31_CLUTTER_TYPE "Invalid value for clutter type" -#define LBL__ERROR32_FREQUENCY "Frequency must be between 2 and 67 GHz, inclusive" -#define LBL__ERROR32_DISTANCE "Path distance must be >= 0.25 km" -#define LBL__ERROR32_PERCENTAGE "Percentage must be between 0 and 100" -#define LBL__ERROR33_FREQUENCY "Frequency must be between 10 and 100 GHz, inclusive" -#define LBL__ERROR33_THETA "Elevation angle must be between 0 and 100 GHz, inclusive" -#define LBL__ERROR33_PERCENTAGE "Percentage must be between 0 and 100, inclusive" +#define LBL__ERROR_INVALID_VALUE "Invalid Value" +#define LBL__ERROR_UNKNOWN "An unknown error occurred" +#define LBL__ERROR31_FREQUENCY "Frequency must be between 0.3 and 3 GHz, inclusive" +#define LBL__ERROR31_ANTENNA_HEIGHT "Antenna height must be >= 0 meters" +#define LBL__ERROR31_STREET_WIDTH "Street width must be > 0 meters" +#define LBL__ERROR31_CLUTTER_HEIGHT "Representative clutter height must be > 0 meters" +#define LBL__ERROR31_CLUTTER_TYPE "Invalid value for clutter type" +#define LBL__ERROR32_FREQUENCY "Frequency must be between 2 and 67 GHz, inclusive" +#define LBL__ERROR32_DISTANCE "Path distance must be >= 0.25 km" +#define LBL__ERROR32_PERCENTAGE "Percentage must be between 0 and 100" +#define LBL__ERROR33_FREQUENCY "Frequency must be between 10 and 100 GHz, inclusive" +#define LBL__ERROR33_THETA "Elevation angle must be between 0 and 100 GHz, inclusive" +#define LBL__ERROR33_PERCENTAGE "Percentage must be between 0 and 100, inclusive" -// clang-format on \ No newline at end of file +// clang-format on +#endif \ No newline at end of file diff --git a/app/include/Structs.h b/app/include/Structs.h index db08101..991e97f 100644 --- a/app/include/Structs.h +++ b/app/include/Structs.h @@ -7,38 +7,44 @@ ///////////////////////////// // Enums +/** Valid values of "model" command line option */ enum class P2108Model { - NOT_SET = -1, - HGTCM = 1, - TSM = 2, - ASM = 3, + NOT_SET = -1, /**< Invalid model selection */ + HGTCM = 1, /**< Height Gain Terminal Correction Model */ + TSM = 2, /**< Terrestrial Statistical Model */ + ASM = 3, /**< Aeronautical Statistical Model */ }; ///////////////////////////// // Data Structures +/** Parameters provided to the command line driver */ struct DrvrParams { - std::string in_file = ""; // Input file - std::string out_file = ""; // Output file - P2108Model model = P2108Model::NOT_SET; -}; - -struct ASMParams { - double f__ghz; - double theta__deg; - double p; + std::string in_file = ""; /**< Input file */ + std::string out_file = ""; /**< Output file */ + P2108Model model = P2108Model::NOT_SET; /**< Model selection */ }; +/** Input parameters for the Height Gain Terminal Correction Model */ struct HGTCParams { - double f__ghz; - double h__meter; - double w_s__meter; - double R__meter; - ITS::ITU::PSeries::P2108::ClutterType clutter_type; + double f__ghz; /**< Frequency, in GHz */ + double h__meter; /**< Antenna height, in meters */ + double w_s__meter; /**< Street width, in meters */ + double R__meter; /**< Representative clutter height, in meters */ + ITS::ITU::PSeries::P2108::ClutterType + clutter_type; /**< Clutter type (enum value) */ }; +/** Input parameters for the Terrestrial Statistical Model */ struct TSMParams { - double f__ghz; - double d__km; - double p; + double f__ghz; /**< Frequency, in GHz */ + double d__km; /**< Path distance, in km */ + double p; /**< Percentage of locations */ +}; + +/** Input parameters for the Aeronautical Statistical Model */ +struct ASMParams { + double f__ghz; /**< Frequency, in GHz */ + double theta__deg; /**< Elevation angle, in degrees */ + double p; /**< Percentage of locations */ }; \ No newline at end of file diff --git a/app/include/Tags.h b/app/include/Tags.h index 15e46a7..a04038a 100644 --- a/app/include/Tags.h +++ b/app/include/Tags.h @@ -4,6 +4,8 @@ #pragma once // clang-format off +// Skip Doxygen generation for tags +#ifndef DOXYGEN_SHOULD_SKIP /** Input File Tags */ #define TAG__FREQ "f__ghz" @@ -24,3 +26,4 @@ #define UNITS__DB "(dB)" // clang-format on +#endif \ No newline at end of file diff --git a/app/src/AeronauticalStatisticalModel.cpp b/app/src/AeronauticalStatisticalModel.cpp index 8a4dce2..20d88a3 100644 --- a/app/src/AeronauticalStatisticalModel.cpp +++ b/app/src/AeronauticalStatisticalModel.cpp @@ -8,7 +8,7 @@ /******************************************************************************* * Top-level control function for Aeronautical Statistical Model operation * - * @param[out] asm_params Aeronautical Statistical Model input parameter struct + * @param[in] asm_params Aeronautical Statistical Model input parameter struct * @param[out] L_ces__db Basic transmission loss, in dB * @return Return code ******************************************************************************/ @@ -81,8 +81,8 @@ int ParseASMInputFile(const std::string &in_file, ASMParams &asm_params) { /******************************************************************************* * Write Aeronautical Statistical Model inputs to the report file * - * @param[in] fp Output stream, a text file open for writing - * @param[in] asm_params ASM input parameter struct + * @param[in] fp Output stream, a text file open for writing + * @param[in] params ASM input parameter struct ******************************************************************************/ void WriteASMInputs(std::ofstream &fp, const ASMParams ¶ms) { fp PRINT TAG__FREQ SETW13 params.f__ghz << UNITS__GHZ; diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index 1ef3814..d4d5fdd 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -10,6 +10,10 @@ /******************************************************************************* * Main function of the driver executable + * + * @param[in] argc Number of arguments entered on the command line + * @param[in] argv Array containing the provided command-line arguments + * @return Return code ******************************************************************************/ int main(int argc, char **argv) { int rtn; diff --git a/app/src/HeightGainTerminalCorrectionModel.cpp b/app/src/HeightGainTerminalCorrectionModel.cpp index ecf1d16..8701c9c 100644 --- a/app/src/HeightGainTerminalCorrectionModel.cpp +++ b/app/src/HeightGainTerminalCorrectionModel.cpp @@ -8,7 +8,7 @@ /******************************************************************************* * Top-level control function for Height Gain Terminal Correction Model * - * @param[out] hgtc_params Height Gain Terminal Correction Model input struct + * @param[in] hgtc_params Height Gain Terminal Correction Model input struct * @param[out] A_h__db Additional loss (clutter loss), in dB * @return Return code ******************************************************************************/ @@ -101,8 +101,8 @@ int ParseHGTCInputFile(const std::string &in_file, HGTCParams &hgtc_params) { /******************************************************************************* * Write Height Gain Terminal Correction Model inputs to the report file * - * @param[in] fp Output stream, a text file open for writing - * @param[in] hgtc_params HGTC input parameter struct + * @param[in] fp Output stream, a text file open for writing + * @param[in] params HGTCM input parameter struct ******************************************************************************/ void WriteHGTCInputs(std::ofstream &fp, const HGTCParams ¶ms) { fp PRINT TAG__FREQ SETW13 params.f__ghz << UNITS__GHZ; diff --git a/app/src/TerrestrialStatisticalModel.cpp b/app/src/TerrestrialStatisticalModel.cpp index 2e00551..d2fa289 100644 --- a/app/src/TerrestrialStatisticalModel.cpp +++ b/app/src/TerrestrialStatisticalModel.cpp @@ -8,7 +8,7 @@ /******************************************************************************* * Top-level control function for Terrestrial Statistical Model operation * - * @param[out] tsm_params Terrestrial Statistical Model input parameter struct + * @param[in] tsm_params Terrestrial Statistical Model input parameter struct * @param[out] L_ctt__db Additional loss (clutter loss), in dB * @return Return code ******************************************************************************/ @@ -83,8 +83,8 @@ int ParseTSMInputFile(const std::string &in_file, TSMParams &tsm_params) { /******************************************************************************* * Write Terrestrial Statistical Model inputs to the report file * - * @param[in] fp Output stream, a text file open for writing - * @param[in] tsm_params TSM input parameter struct + * @param[in] fp Output stream, a text file open for writing + * @param[in] params TSM input parameter struct ******************************************************************************/ void WriteTSMInputs(std::ofstream &fp, const TSMParams ¶ms) { fp PRINT TAG__FREQ SETW13 params.f__ghz << UNITS__GHZ; diff --git a/app/tests/TempTextFile.cpp b/app/tests/TempTextFile.cpp index 80e25a2..ab619ff 100644 --- a/app/tests/TempTextFile.cpp +++ b/app/tests/TempTextFile.cpp @@ -1,4 +1,4 @@ -/** @file TempFile.cpp +/** @file TempTextFile.cpp * Implements a class to create and write to temporary text files */ #include "TempTextFile.h" diff --git a/app/tests/TestDriver.cpp b/app/tests/TestDriver.cpp index e27cca6..1337943 100644 --- a/app/tests/TestDriver.cpp +++ b/app/tests/TestDriver.cpp @@ -3,7 +3,6 @@ */ #include "TestDriver.h" - TEST_F(DriverTest, MissingOptionError1) { // Test case: missing option between two provided flags std::string cmd = executable + " -i -o out.txt"; diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index d59a461..44e0ac5 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -157,9 +157,9 @@ class DriverTest: public ::testing::Test { } } - // Platform-dependent string to call the executable + /** Platform-dependent string to call the executable */ std::string executable; - // Driver parameters struct which may be used by tests + /** Driver parameters struct which may be used by tests */ DrvrParams params; }; diff --git a/app/tests/TestDriverASM.cpp b/app/tests/TestDriverASM.cpp index 0460d7b..4b7096a 100644 --- a/app/tests/TestDriverASM.cpp +++ b/app/tests/TestDriverASM.cpp @@ -1,5 +1,8 @@ #include "TestDriver.h" +/******************************************************************************* + * Driver test fixture for the Aeronautical Statistical Model + ******************************************************************************/ class ASMDriverTest: public DriverTest { protected: void SetUp() override { @@ -13,8 +16,8 @@ class ASMDriverTest: public DriverTest { EXPECT_EQ(asm_rtn, expected_rtn); } - std::string ASMInputs; - DrvrParams asm_params; + std::string ASMInputs; /**< String to hold input file contents */ + DrvrParams asm_params; /**< Default command line arguments */ }; TEST_F(ASMDriverTest, TestSuccess) { diff --git a/app/tests/TestDriverHGTCM.cpp b/app/tests/TestDriverHGTCM.cpp index da2fff4..168fc68 100644 --- a/app/tests/TestDriverHGTCM.cpp +++ b/app/tests/TestDriverHGTCM.cpp @@ -1,5 +1,8 @@ #include "TestDriver.h" +/******************************************************************************* + * Driver test fixture for the Height Gain Terminal Correction Model + ******************************************************************************/ class HGTCMDriverTest: public DriverTest { protected: void SetUp() override { @@ -13,8 +16,8 @@ class HGTCMDriverTest: public DriverTest { EXPECT_EQ(hgtcm_rtn, expected_rtn); } - std::string HGTCMInputs; - DrvrParams hgtcm_params; + std::string HGTCMInputs; /**< String to hold input file contents */ + DrvrParams hgtcm_params; /**< Default command line arguments */ }; TEST_F(HGTCMDriverTest, TestSuccess) { diff --git a/app/tests/TestDriverTSM.cpp b/app/tests/TestDriverTSM.cpp index 11aed17..8447381 100644 --- a/app/tests/TestDriverTSM.cpp +++ b/app/tests/TestDriverTSM.cpp @@ -1,5 +1,8 @@ #include "TestDriver.h" +/******************************************************************************* + * Driver test fixture for the Terrestrial Statistical Model + ******************************************************************************/ class TSMDriverTest: public DriverTest { protected: void SetUp() override { @@ -13,8 +16,8 @@ class TSMDriverTest: public DriverTest { EXPECT_EQ(tsm_rtn, expected_rtn); } - std::string TSMInputs; - DrvrParams tsm_params; + std::string TSMInputs; /**< String to hold input file contents */ + DrvrParams tsm_params; /**< Default command line arguments */ }; TEST_F(TSMDriverTest, TestSuccess) { diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index 1125dc1..e5a0786 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -58,6 +58,8 @@ set(DOXYGEN_INTERNAL_DOCS "YES") doxygen_add_docs( "${CMAKE_PROJECT_NAME}-Docs" + "${PROJECT_SOURCE_DIR}/app/src" + "${PROJECT_SOURCE_DIR}/app/include" "${PROJECT_SOURCE_DIR}/src" "${PROJECT_SOURCE_DIR}/include" "${DOCS_DIR}/doxy_mainpage.md" diff --git a/docs/doxy_mainpage.md b/docs/doxy_mainpage.md index 18415f8..d02612c 100644 --- a/docs/doxy_mainpage.md +++ b/docs/doxy_mainpage.md @@ -1,8 +1,9 @@ # 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. +C++ library and associated command-line driver, 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). From 5f32198d35ed175b840c5e77ed7b1bb72537d961 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 24 Oct 2024 18:04:21 -0400 Subject: [PATCH 139/379] Document driver with Doxygen --- app/include/CommaSeparatedIterator.h | 21 +++++++++++++++++---- app/include/Driver.h | 3 ++- app/include/Errors.h | 6 +++--- app/include/Structs.h | 6 +++--- app/src/Driver.cpp | 4 ++++ app/tests/TestDriver.h | 4 ++-- docs/CMakeLists.txt | 2 ++ docs/doxy_mainpage.md | 5 +++-- 8 files changed, 36 insertions(+), 15 deletions(-) diff --git a/app/include/CommaSeparatedIterator.h b/app/include/CommaSeparatedIterator.h index f1d6605..2e7b646 100644 --- a/app/include/CommaSeparatedIterator.h +++ b/app/include/CommaSeparatedIterator.h @@ -18,14 +18,27 @@ ******************************************************************************/ class CommaSeparatedIterator { public: + /** Type alias for the value returned by the iterator (pair of strings) */ using value_type = std::pair; + + /*********************************************************************** + * Constructor method + * + * @param[in] stream The input stream which will be read + **********************************************************************/ CommaSeparatedIterator(std::istream &stream); + + /** Pre-increment operator to advance the iterator to the next line */ CommaSeparatedIterator &operator++(); + + /** Dereference operator to obtain the current pair of substrings */ value_type operator*() const; + + /** Conversion to boolean to check if the iterator is valid */ explicit operator bool() const; private: - std::istream &stream_; - std::string line_; - std::string first_; - std::string second_; + std::istream &stream_; /**< Reference to the input stream */ + std::string line_; /**< Current line read from the stream */ + std::string first_; /**< First string from the current line */ + std::string second_; /**< Second string from the current line */ }; diff --git a/app/include/Driver.h b/app/include/Driver.h index a2bee78..ed91de2 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -21,8 +21,9 @@ ///////////////////////////// // Macros -// Make print-to-file statements more concise in driver +/** Shortcut for concise print-to-file statements in driver */ #define PRINT << std::endl << std::left << std::setw(25) << +/** Shortcut for setting fixed whitespace padding in driver file output */ #define SETW13 << std::setw(13) << ////////////////////////////// diff --git a/app/include/Errors.h b/app/include/Errors.h index 25865ff..cdbe0a8 100644 --- a/app/include/Errors.h +++ b/app/include/Errors.h @@ -13,19 +13,19 @@ #define SUCCESS 0 /**< Successful execution */ #endif -/** Primary Return Codes (1000-1099) */ +// Primary Return Codes (1000-1099) #define DRVR__RETURN_SUCCESS 1000 /**< Internal driver success code */ #define DRVRERR__MISSING_OPTION 1001 /**< No value provided for given argument */ #define DRVRERR__INVALID_OPTION 1002 /**< Unknown option specified */ #define DRVRERR__OPENING_INPUT_FILE 1003 /**< Failed to open the input file for reading */ #define DRVRERR__OPENING_OUTPUT_FILE 1004 /**< Failed to open the output file for writing */ -/** Input File Parsing Errors (1100-1199) */ +// Input File Parsing Errors (1100-1199) #define DRVRERR__PARSE 1100 /**< General error parsing inputs */ // TODO-TEMPLATE: Add driver error codes and document them in app/README.md -/** Validation Errors (1200-1299) */ +// Validation Errors (1200-1299) #define DRVRERR__VALIDATION_IN_FILE 1200 /**< Input file not specified */ #define DRVRERR__VALIDATION_OUT_FILE 1201 /**< Output file not specified */ diff --git a/app/include/Structs.h b/app/include/Structs.h index 93bdd79..caea4ed 100644 --- a/app/include/Structs.h +++ b/app/include/Structs.h @@ -10,7 +10,7 @@ // TODO-TEMPLATE: Basic struct provided to hold input/output file names and DBG flag struct DrvrParams { - std::string in_file = ""; // Input file - std::string out_file = ""; // Output file - bool DBG = false; // Dump intermediate values to file? + std::string in_file = ""; /**< Input file */ + std::string out_file = ""; /**< Output file */ + bool DBG = false; /**< Dump intermediate values to file? */ }; \ No newline at end of file diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index 1976a1d..2952553 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -5,6 +5,10 @@ /******************************************************************************* * Main function of the driver executable + * + * @param[in] argc Number of arguments entered on the command line + * @param[in] argv Array containing the provided command-line arguments + * @return Return code ******************************************************************************/ int main(int argc, char **argv) { int rtn; diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index 2b2d23e..677fba1 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -154,9 +154,9 @@ class DriverTest: public ::testing::Test { } } - // Platform-dependent string to call the executable + /** Platform-dependent string to call the executable */ std::string executable; - // Driver parameters struct which may be used by tests + /** Driver parameters struct which may be used by tests */ DrvrParams params; }; diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index 1125dc1..e5a0786 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -58,6 +58,8 @@ set(DOXYGEN_INTERNAL_DOCS "YES") doxygen_add_docs( "${CMAKE_PROJECT_NAME}-Docs" + "${PROJECT_SOURCE_DIR}/app/src" + "${PROJECT_SOURCE_DIR}/app/include" "${PROJECT_SOURCE_DIR}/src" "${PROJECT_SOURCE_DIR}/include" "${DOCS_DIR}/doxy_mainpage.md" diff --git a/docs/doxy_mainpage.md b/docs/doxy_mainpage.md index 18415f8..d02612c 100644 --- a/docs/doxy_mainpage.md +++ b/docs/doxy_mainpage.md @@ -1,8 +1,9 @@ # 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. +C++ library and associated command-line driver, 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). From c1959d6d255d8186dfcd591351748abd7a29c22e Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 28 Oct 2024 09:26:38 -0400 Subject: [PATCH 140/379] type-safe constant for PI --- include/ITS.ITU.PSeries.P2108/P2108.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/ITS.ITU.PSeries.P2108/P2108.h b/include/ITS.ITU.PSeries.P2108/P2108.h index 8a480e6..64a62cb 100644 --- a/include/ITS.ITU.PSeries.P2108/P2108.h +++ b/include/ITS.ITU.PSeries.P2108/P2108.h @@ -34,7 +34,8 @@ using std::tan; //////////////////////////////////////////////////////////////////////////////// // Constants -#define PI 3.14159265358979323846 /**< Approximate value of @f$ \pi @f$ */ +/** Approximate value of @f$ \pi @f$ */ +constexpr double PI = 3.14159265358979323846; //////////////////////////////////////////////////////////////////////////////// // Public Functions From 21b36c820164488512f096b677db52f7f3a46fba Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:29:31 -0400 Subject: [PATCH 141/379] Use enums for return codes --- app/README.md | 42 +--------- app/include/Driver.h | 30 +++---- app/include/Errors.h | 36 --------- app/include/ReturnCodes.h | 40 ++++++++++ app/src/AeronauticalStatisticalModel.cpp | 45 ++++++----- app/src/CMakeLists.txt | 3 +- app/src/Driver.cpp | 44 +++++------ app/src/DriverUtils.cpp | 47 ++++------- app/src/HeightGainTerminalCorrectionModel.cpp | 63 +++++++-------- app/src/Reporting.cpp | 78 +++---------------- app/src/ReturnCodes.cpp | 55 +++++++++++++ app/src/TerrestrialStatisticalModel.cpp | 45 +++++------ include/ITS.ITU.PSeries.P2108/Errors.h | 40 ---------- include/ITS.ITU.PSeries.P2108/P2108.h | 14 ++-- include/ITS.ITU.PSeries.P2108/ReturnCodes.h | 45 +++++++++++ src/AeronauticalStatisticalModel.cpp | 11 ++- src/CMakeLists.txt | 6 +- src/HeightGainTerminalCorrectionModel.cpp | 6 +- src/ReturnCodes.cpp | 53 +++++++++++++ src/TerrestrialStatisticalModel.cpp | 10 +-- tests/TestAeronauticalStatisticalModel.cpp | 2 +- .../TestHeightGainTerminalCorrectionModel.cpp | 2 +- tests/TestTerrestrialStatisticalModel.cpp | 2 +- tests/TestUtils.cpp | 16 ++-- tests/TestUtils.h | 6 +- 25 files changed, 381 insertions(+), 360 deletions(-) delete mode 100644 app/include/Errors.h create mode 100644 app/include/ReturnCodes.h create mode 100644 app/src/ReturnCodes.cpp delete mode 100644 include/ITS.ITU.PSeries.P2108/Errors.h create mode 100644 include/ITS.ITU.PSeries.P2108/ReturnCodes.h create mode 100644 src/ReturnCodes.cpp diff --git a/app/README.md b/app/README.md index 2c4692a..238fe77 100644 --- a/app/README.md +++ b/app/README.md @@ -57,43 +57,7 @@ to the provided corresponding output files. ## Command-line Driver Errors ## -(TODO-TEMPLATE: Update the below tables with your driver errors. Align with app/Errors.h) - In addition to the return codes defined by the library itself, the command-line -driver implements the following return codes. - -### General Errors ### - -| Value | Const Name | Description | -|-------|--------------------------------|--------------------------------------------| -| 1000 | `DRVR__RETURN_SUCCESS` | Internal driver success code | -| 1001 | `DRVRERR__MISSING_OPTION` | No value provided for given argument | -| 1002 | `DRVRERR__INVALID_OPTION` | Unknown option specified | -| 1003 | `DRVRERR__OPENING_INPUT_FILE` | Failed to open the input file for reading | -| 1004 | `DRVRERR__OPENING_OUTPUT_FILE` | Failed to open the output file for writing | - -### Input File Parsing Errors ### - -| Value | Const Name | Description | -|-------|-------------------------------|-------------------------------------------| -| 1100 | `DRVRERR__PARSE` | General input file parsing error | -| 1101 | `DRVRERR__PARSE_FREQ` | Error parsing frequency value | -| 1102 | `DRVRERR__PARSE_THETA` | Error parsing theta value | -| 1103 | `DRVRERR__PARSE_PERCENTAGE` | Error parsing percentage value | -| 1104 | `DRVRERR__PARSE_HEIGHT` | Error parsing antenna height value | -| 1105 | `DRVRERR__PARSE_STREET_WIDTH` | Error parsing street width value | -| 1106 | `DRVRERR__PARSE_REPR_HEIGHT` | Error parsing representative height value | -| 1107 | `DRVRERR__PARSE_CLUTTER_TYPE` | Error parsing clutter type value | -| 1108 | `DRVRERR__PARSE_PATH_DIST` | Error parsing path distance value | - -### Validation Errors ### - -Driver validation errors occur when required command line arguments are missing. -These validation errors are distinct from any defined within the library itself, -which may include, e.g., parameter out-of-range errors. - -| Value | Const Name | Description | -|-------|------------------------------------|----------------------------------| -| 1200 | `DRVRERR__VALIDATION_IN_FILE` | Input file not specified | -| 1201 | `DRVRERR__VALIDATION_OUT_FILE` | Output file not specified | -| 1202 | `DRVRERR__VALIDATION_MODEL` | Model not specified | \ No newline at end of file +driver implements its own set of return codes in [`app/include/ReturnCodes.h`](./include/ReturnCodes.h). +A helper function to map human-readable status messages to these codes in +[`app/src/ReturnCodes.cpp`](./src/ReturnCodes.cpp). diff --git a/app/include/Driver.h b/app/include/Driver.h index a0aa2d9..052a3ce 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -4,8 +4,8 @@ #pragma once #include "CommaSeparatedIterator.h" -#include "Errors.h" #include "ITS.ITU.PSeries.P2108/P2108.h" +#include "ReturnCodes.h" #include "Structs.h" #include // for std::ifstream, std::ofstream @@ -30,41 +30,41 @@ using namespace ITS::ITU::PSeries::P2108; ///////////////////////////// // Functions -int ParseArguments(int argc, char **argv, DrvrParams ¶ms); +DrvrReturnCode ParseArguments(int argc, char **argv, DrvrParams ¶ms); void Help(std::ostream &os = std::cout); -int ValidateInputs(const DrvrParams ¶ms); +DrvrReturnCode ValidateInputs(const DrvrParams ¶ms); // Aeronautical Statistical Model -int CallAeronauticalStatisticalModel( +ReturnCode CallAeronauticalStatisticalModel( ASMParams &asm_params, std::vector &L_ces__db ); -int ParseASMInputFile(const std::string &in_file, ASMParams &asm_params); +DrvrReturnCode + ParseASMInputFile(const std::string &in_file, ASMParams &asm_params); void WriteASMInputs(std::ofstream &fp, const ASMParams ¶ms); // Height Gain Terminal Correction Model -int CallHeightGainTerminalCorrectionModel( +ReturnCode CallHeightGainTerminalCorrectionModel( HGTCParams &hgtc_params, std::vector &A_h__db ); -int ParseHGTCInputFile(const std::string &in_file, HGTCParams &hgtc_params); +DrvrReturnCode + ParseHGTCInputFile(const std::string &in_file, HGTCParams &hgtc_params); void WriteHGTCInputs(std::ofstream &fp, const HGTCParams ¶ms); // Terrestrial Statistical Model -int CallTerrestrialStatisticalModel( +ReturnCode CallTerrestrialStatisticalModel( TSMParams &tsm_params, std::vector &L_ctt__db ); -int ParseTSMInputFile(const std::string &in_file, TSMParams &tsm_params); +DrvrReturnCode + ParseTSMInputFile(const std::string &in_file, TSMParams &tsm_params); void WriteTSMInputs(std::ofstream &fp, const TSMParams ¶ms); // Reporting void PrintClutterTypeLabel(std::ofstream &fp, const ClutterType clutter_type); -void PrintErrorMsgLabel(std::ofstream &fp, const int err); -void PrintLabel(std::ofstream &fp, const char *lbl); +void PrintLabel(std::ofstream &fp, std::string &lbl); // Driver Utils void Version(std::ostream &os = std::cout); -int Validate_RequiredErrMsgHelper(const std::string &opt, const int err); -int ParseInteger(const std::string &str, int &value); -int ParseDouble(const std::string &str, double &value); -int ParsingErrorHelper(const int err, const std::string &msg); +DrvrReturnCode ParseInteger(const std::string &str, int &value); +DrvrReturnCode ParseDouble(const std::string &str, double &value); std::string GetDatetimeString(); void StringToLower(std::string &str); diff --git a/app/include/Errors.h b/app/include/Errors.h deleted file mode 100644 index fce0bae..0000000 --- a/app/include/Errors.h +++ /dev/null @@ -1,36 +0,0 @@ -/** @file Errors.h - * Defines return codes for the driver - */ -#pragma once - -// clang-format off - -// Define "SUCCESS" macro if not imported already -#ifndef SUCCESS -#define SUCCESS 0 /**< Successful execution */ -#endif - -// Primary Return Codes (1000-1099) -#define DRVR__RETURN_SUCCESS 1000 /**< Internal driver success code */ -#define DRVRERR__MISSING_OPTION 1001 /**< No value provided for given argument */ -#define DRVRERR__INVALID_OPTION 1002 /**< Unknown option specified */ -#define DRVRERR__OPENING_INPUT_FILE 1003 /**< Failed to open the input file for reading */ -#define DRVRERR__OPENING_OUTPUT_FILE 1004 /**< Failed to open the output file for writing */ - -// Input File Parsing Errors (1100-1199) -#define DRVRERR__PARSE 1100 /**< General error parsing inputs */ -#define DRVRERR__PARSE_FREQ 1101 /**< Error parsing frequency value */ -#define DRVRERR__PARSE_THETA 1102 /**< Error parsing theta value */ -#define DRVRERR__PARSE_PERCENTAGE 1103 /**< Error parsing percentage value */ -#define DRVRERR__PARSE_HEIGHT 1104 /**< Error parsing antenna height value */ -#define DRVRERR__PARSE_STREET_WIDTH 1105 /**< Error parsing street width value */ -#define DRVRERR__PARSE_REPR_HEIGHT 1106 /**< Error parsing representative height value */ -#define DRVRERR__PARSE_CLUTTER_TYPE 1107 /**< Error parsing clutter type value */ -#define DRVRERR__PARSE_PATH_DIST 1108 /**< Error parsing path distance value */ - -// Validation Errors (1200-1299) -#define DRVRERR__VALIDATION_IN_FILE 1200 /**< Input file not specified */ -#define DRVRERR__VALIDATION_OUT_FILE 1201 /**< Output file not specified */ -#define DRVRERR__VALIDATION_MODEL 1202 /**< Model not specified */ - -// clang-format on \ No newline at end of file diff --git a/app/include/ReturnCodes.h b/app/include/ReturnCodes.h new file mode 100644 index 0000000..c4473ac --- /dev/null +++ b/app/include/ReturnCodes.h @@ -0,0 +1,40 @@ +/** @file ReturnCodes.h + * Defines return codes for the driver + */ +#pragma once + +#include +#include + +/******************************************************************************* + * Return Codes defined by this driver software. + ******************************************************************************/ +// clang-format off +enum DrvrReturnCode { + // Primary Return Codes + DRVR__SUCCESS = 1000, /**< Successful execution */ + DRVR__RETURN_SUCCESS, /**< Indicates driver should exit successfully */ + DRVRERR__MISSING_OPTION, /**< No value provided for given argument */ + DRVRERR__INVALID_OPTION, /**< Unknown option specified */ + DRVRERR__OPENING_INPUT_FILE, /**< Failed to open the input file for reading */ + DRVRERR__OPENING_OUTPUT_FILE, /**< Failed to open the output file for writing */ + + // Input File Parsing Errors (1100-1199) + DRVRERR__PARSE = 1100, /**< Failed parsing inputs; unknown parameter */ + DRVRERR__PARSE_FREQ, /**< Failed to parse frequency value */ + DRVRERR__PARSE_THETA, /**< Failed to parse theta value */ + DRVRERR__PARSE_PERCENTAGE, /**< Failed to parse percentage value */ + DRVRERR__PARSE_HEIGHT, /**< Failed to parse antenna height value */ + DRVRERR__PARSE_STREET_WIDTH, /**< Failed to parse street width value */ + DRVRERR__PARSE_REPR_HEIGHT, /**< Failed to parse representative height value */ + DRVRERR__PARSE_CLUTTER_TYPE, /**< Failed to parse clutter type value */ + DRVRERR__PARSE_PATH_DIST, /**< Failed to parse path distance value */ + + // Validation Errors (1200-1299) + DRVRERR__VALIDATION_IN_FILE = 1200, /**< Input file not specified */ + DRVRERR__VALIDATION_OUT_FILE, /**< Output file not specified */ + DRVRERR__VALIDATION_MODEL /**< Model not specified */ +}; +// clang-format on + +std::string GetDrvrReturnStatus(int code); diff --git a/app/src/AeronauticalStatisticalModel.cpp b/app/src/AeronauticalStatisticalModel.cpp index 20d88a3..d54cbe3 100644 --- a/app/src/AeronauticalStatisticalModel.cpp +++ b/app/src/AeronauticalStatisticalModel.cpp @@ -12,10 +12,10 @@ * @param[out] L_ces__db Basic transmission loss, in dB * @return Return code ******************************************************************************/ -int CallAeronauticalStatisticalModel( +ReturnCode CallAeronauticalStatisticalModel( ASMParams &asm_params, std::vector &L_ces__db ) { - int rtn; + ReturnCode rtn; double L_ces; rtn = AeronauticalStatisticalModel( asm_params.f__ghz, asm_params.theta__deg, asm_params.p, L_ces @@ -32,34 +32,36 @@ int CallAeronauticalStatisticalModel( * @param[out] asm_params ASM input parameter struct * @return Return code ******************************************************************************/ -int ParseASMInputStream(std::istream &stream, ASMParams &asm_params) { +DrvrReturnCode + ParseASMInputStream(std::istream &stream, ASMParams &asm_params) { CommaSeparatedIterator it(stream); - std::string key, value; + DrvrReturnCode rtn = DRVR__SUCCESS; + std::string key, value, errMsg; while (it) { std::tie(key, value) = *it; if (key.compare(TAG__FREQ) == 0) { - if (ParseDouble(value, asm_params.f__ghz) == DRVRERR__PARSE) { - return ParsingErrorHelper(DRVRERR__PARSE_FREQ, TAG__FREQ); - } + rtn = ParseDouble(value, asm_params.f__ghz); + if (rtn == DRVRERR__PARSE) + rtn = DRVRERR__PARSE_FREQ; } else if (key.compare(TAG__THETA) == 0) { - if (ParseDouble(value, asm_params.theta__deg) == DRVRERR__PARSE) { - return ParsingErrorHelper(DRVRERR__PARSE_THETA, TAG__THETA); - } + rtn = ParseDouble(value, asm_params.theta__deg); + if (rtn == DRVRERR__PARSE) + rtn = DRVRERR__PARSE_THETA; } else if (key.compare(TAG__PERCENTAGE) == 0) { - if (ParseDouble(value, asm_params.p) == DRVRERR__PARSE) { - return ParsingErrorHelper( - DRVRERR__PARSE_PERCENTAGE, TAG__PERCENTAGE - ); - } + rtn = ParseDouble(value, asm_params.p); + if (rtn == DRVRERR__PARSE) + rtn = DRVRERR__PARSE_PERCENTAGE; } else { - std::cerr << "Driver Error " << DRVRERR__PARSE; - std::cerr << ": Unknown input parameter '" << key << "'" - << std::endl; - return DRVRERR__PARSE; + rtn = DRVRERR__PARSE; + } + + if (rtn != DRVR__SUCCESS) { + std::cerr << GetDrvrReturnStatus(rtn); + return rtn; } ++it; } - return SUCCESS; + return rtn; } /******************************************************************************* @@ -69,7 +71,8 @@ int ParseASMInputStream(std::istream &stream, ASMParams &asm_params) { * @param[out] asm_params ASM input parameter struct * @return Return code ******************************************************************************/ -int ParseASMInputFile(const std::string &in_file, ASMParams &asm_params) { +DrvrReturnCode + ParseASMInputFile(const std::string &in_file, ASMParams &asm_params) { std::ifstream file(in_file); if (!file) { std::cerr << "Failed to open file " << in_file << std::endl; diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index 4ba81cb..68c1cf6 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -10,10 +10,11 @@ add_executable( "DriverUtils.cpp" "HeightGainTerminalCorrectionModel.cpp" "Reporting.cpp" + "ReturnCodes.cpp" "TerrestrialStatisticalModel.cpp" "${DRIVER_HEADERS}/CommaSeparatedIterator.h" "${DRIVER_HEADERS}/Driver.h" - "${DRIVER_HEADERS}/Errors.h" + "${DRIVER_HEADERS}/ReturnCodes.h" "${DRIVER_HEADERS}/Labels.h" "${DRIVER_HEADERS}/Structs.h" "${DRIVER_HEADERS}/Tags.h" diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index d4d5fdd..d6bf1af 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -21,16 +21,16 @@ int main(int argc, char **argv) { // Parse command line arguments rtn = ParseArguments(argc, argv, params); - if (rtn == DRVR__RETURN_SUCCESS) + if (rtn == DRVR__RETURN_SUCCESS) { return SUCCESS; - if (rtn != SUCCESS) { + } else if (rtn != DRVR__SUCCESS) { Help(); return rtn; } // Ensure required options were provided rtn = ValidateInputs(params); - if (rtn != SUCCESS) { + if (rtn != DRVR__SUCCESS) { Help(); return rtn; } @@ -44,21 +44,21 @@ int main(int argc, char **argv) { switch (params.model) { case P2108Model::HGTCM: rtn = ParseHGTCInputFile(params.in_file, hgtc_params); - if (rtn != SUCCESS) { + if (rtn != DRVR__SUCCESS) { return rtn; } rtn = CallHeightGainTerminalCorrectionModel(hgtc_params, loss__db); break; case P2108Model::TSM: rtn = ParseTSMInputFile(params.in_file, tsm_params); - if (rtn != SUCCESS) { + if (rtn != DRVR__SUCCESS) { return rtn; } rtn = CallTerrestrialStatisticalModel(tsm_params, loss__db); break; case P2108Model::ASM: rtn = ParseASMInputFile(params.in_file, asm_params); - if (rtn != SUCCESS) { + if (rtn != DRVR__SUCCESS) { return rtn; } rtn = CallAeronauticalStatisticalModel(asm_params, loss__db); @@ -68,8 +68,10 @@ int main(int argc, char **argv) { } // Return driver error code if one was returned - if (rtn > DRVR__RETURN_SUCCESS) + if (rtn > DRVR__RETURN_SUCCESS) { + std::cerr << GetDrvrReturnStatus(rtn); return rtn; + } // Print results to file std::ofstream fp(params.out_file); @@ -116,11 +118,11 @@ int main(int argc, char **argv) { if (rtn != SUCCESS) { fp PRINT LIBRARY_NAME << " Error" SETW13 rtn; - PrintErrorMsgLabel(fp, rtn); + PrintLabel(fp, GetReturnStatus(rtn)); } else { fp << std::endl << std::endl << "Results"; fp PRINT "Return Code" SETW13 rtn; - PrintErrorMsgLabel(fp, rtn); + PrintLabel(fp, GetReturnStatus(rtn)); fp PRINT "Clutter loss" SETW13 std::fixed << std::setprecision(1) << loss__db.front() << UNITS__DB; } @@ -136,7 +138,7 @@ int main(int argc, char **argv) { * @param[out] params Structure with user input params * @return Return code ******************************************************************************/ -int ParseArguments(int argc, char **argv, DrvrParams ¶ms) { +DrvrReturnCode ParseArguments(int argc, char **argv, DrvrParams ¶ms) { const std::vector validArgs = {"-i", "-o", "-model", "-h", "--help", "-v", "--version"}; @@ -189,7 +191,7 @@ int ParseArguments(int argc, char **argv, DrvrParams ¶ms) { } } - return SUCCESS; + return DRVR__SUCCESS; } /******************************************************************************* @@ -223,20 +225,18 @@ void Help(std::ostream &os) { * @param[in] params Structure with user input parameters * @return Return code ******************************************************************************/ -int ValidateInputs(const DrvrParams ¶ms) { +DrvrReturnCode ValidateInputs(const DrvrParams ¶ms) { DrvrParams not_set; + DrvrReturnCode rtn = DRVR__SUCCESS; if (params.in_file == not_set.in_file) - return Validate_RequiredErrMsgHelper("-i", DRVRERR__VALIDATION_IN_FILE); - + rtn = DRVRERR__VALIDATION_IN_FILE; if (params.out_file == not_set.out_file) - return Validate_RequiredErrMsgHelper( - "-o", DRVRERR__VALIDATION_OUT_FILE - ); - + rtn = DRVRERR__VALIDATION_OUT_FILE; if (params.model == not_set.model) - return Validate_RequiredErrMsgHelper( - "-model", DRVRERR__VALIDATION_MODEL - ); + rtn = DRVRERR__VALIDATION_MODEL; - return SUCCESS; + if (rtn != DRVR__SUCCESS) + std::cerr << GetDrvrReturnStatus(rtn); + + return rtn; } \ No newline at end of file diff --git a/app/src/DriverUtils.cpp b/app/src/DriverUtils.cpp index 6775406..4d5e685 100644 --- a/app/src/DriverUtils.cpp +++ b/app/src/DriverUtils.cpp @@ -29,20 +29,6 @@ void Version(std::ostream &os) { os << std::setfill('*') << std::setw(55) << "" << std::endl; } -/******************************************************************************* - * Helper function to format and print error messages encountered during - * validation of input parameters - * - * @param[in] opt Command flag in error - * @param[in] err Error code - * @return Return code - ******************************************************************************/ -int Validate_RequiredErrMsgHelper(const std::string &opt, const int err) { - std::cerr << "Driver Error " << err << ": Option \"" << opt - << "\" is required but was not provided" << std::endl; - return err; -} - /******************************************************************************* * Parse an integer value read from the input parameter file * @@ -50,7 +36,7 @@ int Validate_RequiredErrMsgHelper(const std::string &opt, const int err) { * @param[out] value Input file value converted to int * @return Return code ******************************************************************************/ -int ParseInteger(const std::string &str, int &value) { +DrvrReturnCode ParseInteger(const std::string &str, int &value) { try { size_t pos; value = std::stoi(str, &pos, 10); @@ -64,7 +50,7 @@ int ParseInteger(const std::string &str, int &value) { return DRVRERR__PARSE; }; - return SUCCESS; + return DRVR__SUCCESS; } /******************************************************************************* @@ -74,7 +60,7 @@ int ParseInteger(const std::string &str, int &value) { * @param[out] value Input file value converted to double * @return Return code ******************************************************************************/ -int ParseDouble(const std::string &str, double &value) { +DrvrReturnCode ParseDouble(const std::string &str, double &value) { try { value = std::stod(str); } catch (...) { @@ -82,20 +68,7 @@ int ParseDouble(const std::string &str, double &value) { return DRVRERR__PARSE; } - return SUCCESS; -} - -/******************************************************************************* - * Common error handling function - * - * @param[in] err Error parsing code - * @param[in] msg Error message - * @return Error code from input param - ******************************************************************************/ -int ParsingErrorHelper(const int err, const std::string &msg) { - std::cerr << "Driver Error " << err << ": Unable to parse '" << msg - << "' value." << std::endl; - return err; + return DRVR__SUCCESS; } /****************************************************************************** @@ -130,4 +103,14 @@ void StringToLower(std::string &str) { std::transform(str.begin(), str.end(), str.begin(), [](const char c) { return static_cast(std::tolower(c)); }); -} \ No newline at end of file +} + +/******************************************************************************* + * Helper function to standardize printing of text labels to file + * + * @param[in] fp Output stream, a text file open for writing + * @param[in] lbl Text message + ******************************************************************************/ +void PrintLabel(std::ofstream &fp, std::string &lbl) { + fp << "[" << lbl << "]"; +} diff --git a/app/src/HeightGainTerminalCorrectionModel.cpp b/app/src/HeightGainTerminalCorrectionModel.cpp index 8701c9c..088c10c 100644 --- a/app/src/HeightGainTerminalCorrectionModel.cpp +++ b/app/src/HeightGainTerminalCorrectionModel.cpp @@ -12,10 +12,10 @@ * @param[out] A_h__db Additional loss (clutter loss), in dB * @return Return code ******************************************************************************/ -int CallHeightGainTerminalCorrectionModel( +ReturnCode CallHeightGainTerminalCorrectionModel( HGTCParams &hgtc_params, std::vector &A_h__db ) { - int rtn; + ReturnCode rtn; double A_h; rtn = HeightGainTerminalCorrectionModel( hgtc_params.f__ghz, @@ -37,49 +37,49 @@ int CallHeightGainTerminalCorrectionModel( * @param[out] hgtc_params HGTC input parameter struct * @return Return code ******************************************************************************/ -int ParseHGTCInputStream(std::istream &stream, HGTCParams &hgtc_params) { +DrvrReturnCode + ParseHGTCInputStream(std::istream &stream, HGTCParams &hgtc_params) { CommaSeparatedIterator it(stream); + DrvrReturnCode rtn = DRVR__SUCCESS; std::string key, value; while (it) { std::tie(key, value) = *it; if (key.compare(TAG__FREQ) == 0) { - if (ParseDouble(value, hgtc_params.f__ghz) == DRVRERR__PARSE) { - return ParsingErrorHelper(DRVRERR__PARSE_FREQ, TAG__FREQ); - } + rtn = ParseDouble(value, hgtc_params.f__ghz); + if (rtn == DRVRERR__PARSE) + rtn = DRVRERR__PARSE_FREQ; } else if (key.compare(TAG__HEIGHT) == 0) { - if (ParseDouble(value, hgtc_params.h__meter) == DRVRERR__PARSE) { - return ParsingErrorHelper(DRVRERR__PARSE_HEIGHT, TAG__HEIGHT); - } + rtn = ParseDouble(value, hgtc_params.h__meter); + if (rtn == DRVRERR__PARSE) + rtn = DRVRERR__PARSE_HEIGHT; } else if (key.compare(TAG__STREET_WIDTH) == 0) { - if (ParseDouble(value, hgtc_params.w_s__meter) == DRVRERR__PARSE) { - return ParsingErrorHelper( - DRVRERR__PARSE_STREET_WIDTH, TAG__STREET_WIDTH - ); - } + rtn = ParseDouble(value, hgtc_params.w_s__meter); + if (rtn == DRVRERR__PARSE) + rtn = DRVRERR__PARSE_STREET_WIDTH; } else if (key.compare(TAG__REPR_HEIGHT) == 0) { - if (ParseDouble(value, hgtc_params.R__meter) == DRVRERR__PARSE) { - return ParsingErrorHelper( - DRVRERR__PARSE_REPR_HEIGHT, TAG__REPR_HEIGHT - ); - } + rtn = ParseDouble(value, hgtc_params.R__meter); + if (rtn == DRVRERR__PARSE) + rtn = DRVRERR__PARSE_REPR_HEIGHT; } else if (key.compare(TAG__CLUTTER_TYPE) == 0) { int clutter_type_int; - if (ParseInteger(value, clutter_type_int) == DRVRERR__PARSE) { - return ParsingErrorHelper( - DRVRERR__PARSE_CLUTTER_TYPE, TAG__CLUTTER_TYPE - ); + rtn = ParseInteger(value, clutter_type_int); + if (rtn == DRVRERR__PARSE) { + rtn = DRVRERR__PARSE_CLUTTER_TYPE; + } else { + hgtc_params.clutter_type + = static_cast(clutter_type_int); } - hgtc_params.clutter_type - = static_cast(clutter_type_int); } else { - std::cerr << "Driver Error " << DRVRERR__PARSE; - std::cerr << ": Unknown input parameter '" << key << "'" - << std::endl; - return DRVRERR__PARSE; + rtn = DRVRERR__PARSE; + } + + if (rtn != DRVR__SUCCESS) { + std::cerr << GetDrvrReturnStatus(rtn); + return rtn; } ++it; } - return SUCCESS; + return rtn; } /******************************************************************************* @@ -89,7 +89,8 @@ int ParseHGTCInputStream(std::istream &stream, HGTCParams &hgtc_params) { * @param[out] hgtc_params HGTC input parameter struct * @return Return code ******************************************************************************/ -int ParseHGTCInputFile(const std::string &in_file, HGTCParams &hgtc_params) { +DrvrReturnCode + ParseHGTCInputFile(const std::string &in_file, HGTCParams &hgtc_params) { std::ifstream file(in_file); if (!file) { std::cerr << "Failed to open file " << in_file << std::endl; diff --git a/app/src/Reporting.cpp b/app/src/Reporting.cpp index cf5d6be..f820d64 100644 --- a/app/src/Reporting.cpp +++ b/app/src/Reporting.cpp @@ -12,87 +12,29 @@ * @param[in] clutter_type Height Gain Terminal Correction Model clutter type ******************************************************************************/ void PrintClutterTypeLabel(std::ofstream &fp, const ClutterType clutter_type) { + std::string label; switch (clutter_type) { case ClutterType::WATER_SEA: - PrintLabel(fp, LBL__CLUTTERTYPE_WATER_SEA); + label = LBL__CLUTTERTYPE_WATER_SEA; break; case ClutterType::OPEN_RURAL: - PrintLabel(fp, LBL__CLUTTERTYPE_OPEN_RURAL); + label = LBL__CLUTTERTYPE_OPEN_RURAL; break; case ClutterType::SUBURBAN: - PrintLabel(fp, LBL__CLUTTERTYPE_SUBURBAN); + label = LBL__CLUTTERTYPE_SUBURBAN; break; case ClutterType::URBAN: - PrintLabel(fp, LBL__CLUTTERTYPE_URBAN); + label = LBL__CLUTTERTYPE_URBAN; break; case ClutterType::TREES_FOREST: - PrintLabel(fp, LBL__CLUTTERTYPE_TREES_FOREST); + label = LBL__CLUTTERTYPE_TREES_FOREST; break; case ClutterType::DENSE_URBAN: - PrintLabel(fp, LBL__CLUTTERTYPE_DENSE_URBAN); + label = LBL__CLUTTERTYPE_DENSE_URBAN; break; default: - PrintLabel(fp, LBL__ERROR_INVALID_VALUE); + label = LBL__ERROR_INVALID_VALUE; break; } -} - -/******************************************************************************* - * Print text messages corresponding to error codes - * - * @param[in] fp Output stream, a text file open for writing - * @param[in] err Error code - ******************************************************************************/ -void PrintErrorMsgLabel(std::ofstream &fp, const int err) { - switch (err) { - case SUCCESS: - PrintLabel(fp, LBL__SUCCESS); - break; - case ERROR31__FREQUENCY: - PrintLabel(fp, LBL__ERROR31_FREQUENCY); - break; - case ERROR31__ANTENNA_HEIGHT: - PrintLabel(fp, LBL__ERROR31_ANTENNA_HEIGHT); - break; - case ERROR31__STREET_WIDTH: - PrintLabel(fp, LBL__ERROR31_STREET_WIDTH); - break; - case ERROR31__CLUTTER_HEIGHT: - PrintLabel(fp, LBL__ERROR31_CLUTTER_HEIGHT); - break; - case ERROR31__CLUTTER_TYPE: - PrintLabel(fp, LBL__ERROR31_CLUTTER_TYPE); - break; - case ERROR32__FREQUENCY: - PrintLabel(fp, LBL__ERROR32_FREQUENCY); - break; - case ERROR32__DISTANCE: - PrintLabel(fp, LBL__ERROR32_DISTANCE); - break; - case ERROR32__PERCENTAGE: - PrintLabel(fp, LBL__ERROR32_PERCENTAGE); - break; - case ERROR33__FREQUENCY: - PrintLabel(fp, LBL__ERROR33_FREQUENCY); - break; - case ERROR33__THETA: - PrintLabel(fp, LBL__ERROR33_THETA); - break; - case ERROR33__PERCENTAGE: - PrintLabel(fp, LBL__ERROR33_PERCENTAGE); - break; - default: - PrintLabel(fp, LBL__ERROR_UNKNOWN); - break; - } -} - -/******************************************************************************* - * Helper function to standardize printing of text labels to file - * - * @param[in] fp Output stream, a text file open for writing - * @param[in] lbl Text message - ******************************************************************************/ -void PrintLabel(std::ofstream &fp, const char *lbl) { - fp << "[" << lbl << "]"; -} + PrintLabel(fp, label); +} \ No newline at end of file diff --git a/app/src/ReturnCodes.cpp b/app/src/ReturnCodes.cpp new file mode 100644 index 0000000..041790d --- /dev/null +++ b/app/src/ReturnCodes.cpp @@ -0,0 +1,55 @@ +/** @file ReturnCodes.cpp + * Maps status message strings to driver return codes. + */ + +#include "ReturnCodes.h" + +/******************************************************************************* + * Get an error message string from a return code. + * + * @param[in] code Driver return code. + * @return A status message corresponding to the input code. + ******************************************************************************/ +std::string GetDrvrReturnStatus(int code) { + static const std::unordered_map messages + = {{DRVR__SUCCESS, "Successful execution"}, + {DRVR__RETURN_SUCCESS, "Internal driver success"}, + {DRVRERR__MISSING_OPTION, "No value provided for given argument"}, + {DRVRERR__INVALID_OPTION, "Unknown option specified"}, + {DRVRERR__OPENING_INPUT_FILE, + "Failed to open the input file for reading"}, + {DRVRERR__OPENING_OUTPUT_FILE, + "Failed to open the output file for writing"}, + {DRVRERR__PARSE, "Failed parsing inputs; unknown parameter"}, + {DRVRERR__PARSE_FREQ, "Failed to parse frequency value"}, + {DRVRERR__PARSE_THETA, "Failed to parse theta value"}, + {DRVRERR__PARSE_PERCENTAGE, "Failed to parse percentage value"}, + {DRVRERR__PARSE_HEIGHT, "Failed to parse antenna height value"}, + {DRVRERR__PARSE_STREET_WIDTH, "Failed to parse street width value"}, + {DRVRERR__PARSE_REPR_HEIGHT, + "Failed to parse representative height value"}, + {DRVRERR__PARSE_CLUTTER_TYPE, "Failed to parse clutter type value"}, + {DRVRERR__PARSE_PATH_DIST, "Failed to parse path distance value"}, + {DRVRERR__VALIDATION_IN_FILE, + "Option -i is required but was not provided"}, + {DRVRERR__VALIDATION_OUT_FILE, + "Option -o is required but was not provided"}, + {DRVRERR__VALIDATION_MODEL, + "Option -model is required but was not provided"}}; + + // Construct status message + std::string msg = DRIVER_NAME; + if (code == DRVR__SUCCESS) { + msg += " Status: "; + } else { + msg += " Error: "; + } + + auto it = messages.find(static_cast(code)); + if (it != messages.end()) { + msg += it->second + "\n"; + } else { + msg += "Undefined return code\n"; + } + return msg; +} diff --git a/app/src/TerrestrialStatisticalModel.cpp b/app/src/TerrestrialStatisticalModel.cpp index d2fa289..0bc6dd7 100644 --- a/app/src/TerrestrialStatisticalModel.cpp +++ b/app/src/TerrestrialStatisticalModel.cpp @@ -12,10 +12,10 @@ * @param[out] L_ctt__db Additional loss (clutter loss), in dB * @return Return code ******************************************************************************/ -int CallTerrestrialStatisticalModel( +ReturnCode CallTerrestrialStatisticalModel( TSMParams &tsm_params, std::vector &L_ctt__db ) { - int rtn; + ReturnCode rtn; double L_ctt; rtn = TerrestrialStatisticalModel( tsm_params.f__ghz, tsm_params.d__km, tsm_params.p, L_ctt @@ -32,36 +32,36 @@ int CallTerrestrialStatisticalModel( * @param[out] tsm_params TSM input parameter struct * @return Return code ******************************************************************************/ -int ParseTSMInputStream(std::istream &stream, TSMParams &tsm_params) { +DrvrReturnCode + ParseTSMInputStream(std::istream &stream, TSMParams &tsm_params) { CommaSeparatedIterator it(stream); + DrvrReturnCode rtn = DRVR__SUCCESS; std::string key, value; while (it) { std::tie(key, value) = *it; if (key.compare(TAG__FREQ) == 0) { - if (ParseDouble(value, tsm_params.f__ghz) == DRVRERR__PARSE) { - return ParsingErrorHelper(DRVRERR__PARSE_FREQ, TAG__FREQ); - } + rtn = ParseDouble(value, tsm_params.f__ghz); + if (rtn == DRVRERR__PARSE) + rtn = DRVRERR__PARSE_FREQ; } else if (key.compare(TAG__PATH_DIST) == 0) { - if (ParseDouble(value, tsm_params.d__km) == DRVRERR__PARSE) { - return ParsingErrorHelper( - DRVRERR__PARSE_PATH_DIST, TAG__PATH_DIST - ); - } + rtn = ParseDouble(value, tsm_params.d__km); + if (rtn == DRVRERR__PARSE) + rtn = DRVRERR__PARSE_PATH_DIST; } else if (key.compare(TAG__PERCENTAGE) == 0) { - if (ParseDouble(value, tsm_params.p) == DRVRERR__PARSE) { - return ParsingErrorHelper( - DRVRERR__PARSE_PERCENTAGE, TAG__PERCENTAGE - ); - } + rtn = ParseDouble(value, tsm_params.p); + if (rtn == DRVRERR__PARSE) + rtn = DRVRERR__PARSE_PERCENTAGE; } else { - std::cerr << "Driver Error " << DRVRERR__PARSE; - std::cerr << ": Unknown input parameter '" << key << "'" - << std::endl; - return DRVRERR__PARSE; + rtn = DRVRERR__PARSE; + } + + if (rtn != DRVR__SUCCESS) { + std::cerr << GetDrvrReturnStatus(rtn); + return rtn; } ++it; } - return SUCCESS; + return rtn; } /******************************************************************************* @@ -71,7 +71,8 @@ int ParseTSMInputStream(std::istream &stream, TSMParams &tsm_params) { * @param[out] tsm_params TSM input parameter struct * @return Return code ******************************************************************************/ -int ParseTSMInputFile(const std::string &in_file, TSMParams &tsm_params) { +DrvrReturnCode + ParseTSMInputFile(const std::string &in_file, TSMParams &tsm_params) { std::ifstream file(in_file); if (!file) { std::cerr << "Failed to open file " << in_file << std::endl; diff --git a/include/ITS.ITU.PSeries.P2108/Errors.h b/include/ITS.ITU.PSeries.P2108/Errors.h deleted file mode 100644 index ac9e72d..0000000 --- a/include/ITS.ITU.PSeries.P2108/Errors.h +++ /dev/null @@ -1,40 +0,0 @@ -/** @file Errors.h - * Contains return codes used by this software - */ -#pragma once - -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 diff --git a/include/ITS.ITU.PSeries.P2108/P2108.h b/include/ITS.ITU.PSeries.P2108/P2108.h index 64a62cb..cab1f6d 100644 --- a/include/ITS.ITU.PSeries.P2108/P2108.h +++ b/include/ITS.ITU.PSeries.P2108/P2108.h @@ -4,7 +4,7 @@ #pragma once #include "Enums.h" -#include "Errors.h" +#include "ReturnCodes.h" #include // For atan, fmin, log10, pow, sqrt, tan @@ -39,16 +39,16 @@ constexpr double PI = 3.14159265358979323846; //////////////////////////////////////////////////////////////////////////////// // Public Functions -EXPORTED int AeronauticalStatisticalModel( +EXPORTED ReturnCode AeronauticalStatisticalModel( const double f__ghz, const double theta__deg, const double p, double &L_ces__db ); -EXPORTED int TerrestrialStatisticalModel( +EXPORTED ReturnCode TerrestrialStatisticalModel( const double f__ghz, const double d__km, const double p, double &L_ctt__db ); -EXPORTED int HeightGainTerminalCorrectionModel( +EXPORTED ReturnCode HeightGainTerminalCorrectionModel( const double f__ghz, const double h__meter, const double w_s__meter, @@ -65,16 +65,16 @@ double Equation_2a(const double nu); double Equation_2b( const double K_h2, const double h__meter, const double R__meter ); -int Section3p1_InputValidation( +ReturnCode Section3p1_InputValidation( const double f__ghz, const double h__meter, const double w_s__meter, const double R__meter ); -int Section3p2_InputValidation( +ReturnCode Section3p2_InputValidation( const double f__ghz, const double d__km, const double p ); -int Section3p3_InputValidation( +ReturnCode Section3p3_InputValidation( const double f__ghz, const double theta__deg, const double p ); double TerrestrialStatisticalModelHelper( diff --git a/include/ITS.ITU.PSeries.P2108/ReturnCodes.h b/include/ITS.ITU.PSeries.P2108/ReturnCodes.h new file mode 100644 index 0000000..9cf6567 --- /dev/null +++ b/include/ITS.ITU.PSeries.P2108/ReturnCodes.h @@ -0,0 +1,45 @@ +/** @file ReturnCodes.h + * Contains return codes used by this software + */ +#pragma once + +#include +#include + +namespace ITS { +namespace ITU { +namespace PSeries { +namespace P2108 { + +/******************************************************************************* + * Return Codes defined by this software. + ******************************************************************************/ +// clang-format off +enum ReturnCode { + SUCCESS = 0, /**< Successful execution */ + + // Section 3.1 Error Codes + ERROR31__FREQUENCY = 3100, /**< Frequency must be between 0.3 and 3 GHz */ + ERROR31__ANTENNA_HEIGHT, /**< Antenna height must be @f$ \geq @f$ 0 meters */ + ERROR31__STREET_WIDTH, /**< Street width must be @f$ > @f$ 0 meters */ + ERROR31__CLUTTER_HEIGHT, /**< Representative clutter height must be @f$ > @f$ 0 meters */ + ERROR31__CLUTTER_TYPE, /**< Invalid value for clutter type */ + + // Section 3.2 Error Codes + ERROR32__FREQUENCY = 3200, /**< Frequency must be between 2 and 67 GHz */ + ERROR32__DISTANCE, /**< Path distance must be @f$ \geq @f$ 0.25 km */ + ERROR32__PERCENTAGE, /**< Percentage must be between 0 and 100 */ + + // Section 3.3 Error Codes + ERROR33__FREQUENCY = 3300, /**< Frequency must be between 10 and 100 GHz */ + ERROR33__THETA, /**< Elevation angle must be between 0 and 100 GHz */ + ERROR33__PERCENTAGE, /**< Percentage must be between 0 and 100 */ +}; +// clang-format on + +std::string GetReturnStatus(int code); + +} // namespace P2108 +} // namespace PSeries +} // namespace ITU +} // namespace ITS diff --git a/src/AeronauticalStatisticalModel.cpp b/src/AeronauticalStatisticalModel.cpp index ef66577..603a2bf 100644 --- a/src/AeronauticalStatisticalModel.cpp +++ b/src/AeronauticalStatisticalModel.cpp @@ -26,17 +26,17 @@ namespace P2108 { * @param[out] L_ces__db Additional loss (clutter loss), in dB * @return Return code ******************************************************************************/ -int AeronauticalStatisticalModel( +ReturnCode 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); + ReturnCode rtn = Section3p3_InputValidation(f__ghz, theta__deg, p); if (rtn != SUCCESS) return rtn; - const double A_1 = 0.05; + constexpr double A_1 = 0.05; const double K_1 = 93 * pow(f__ghz, 0.175); const double part1 = log(1 - p / 100.0); @@ -47,8 +47,7 @@ int AeronauticalStatisticalModel( = 0.6 * InverseComplementaryCumulativeDistribution(p / 100); L_ces__db = pow(-K_1 * part1 * cot(part2), part3) - 1 - part4; - - return SUCCESS; + return rtn; } /******************************************************************************* @@ -67,7 +66,7 @@ int AeronauticalStatisticalModel( * @param[in] p Percentage of locations, in % * @return Return code ******************************************************************************/ -int Section3p3_InputValidation( +ReturnCode Section3p3_InputValidation( const double f__ghz, const double theta__deg, const double p ) { if (f__ghz < 10 || f__ghz > 100) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 61a26f2..b66eb2c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,14 +9,18 @@ add_library( "HeightGainTerminalCorrectionModel.cpp" "InverseComplementaryCumulativeDistribution.cpp" "TerrestrialStatisticalModel.cpp" + "ReturnCodes.cpp" "${PROJECT_HEADERS}/Enums.h" - "${PROJECT_HEADERS}/Errors.h" + "${PROJECT_HEADERS}/ReturnCodes.h" "${PROJECT_HEADERS}/${LIB_NAME}.h" ) # Add the include directory target_include_directories(${LIB_NAME} PUBLIC "${PROJECT_SOURCE_DIR}/include") +# Add definition to get the library name inside the library +add_definitions(-DLIBRARY_NAME="${LIB_NAME}") + # Set minimum C++ version to C++11 target_compile_features(${LIB_NAME} PUBLIC cxx_std_11) diff --git a/src/HeightGainTerminalCorrectionModel.cpp b/src/HeightGainTerminalCorrectionModel.cpp index f2a0561..9e0af0d 100644 --- a/src/HeightGainTerminalCorrectionModel.cpp +++ b/src/HeightGainTerminalCorrectionModel.cpp @@ -28,7 +28,7 @@ namespace P2108 { * @param[out] A_h__db Additional loss (clutter loss), in dB * @return Return code ******************************************************************************/ -int HeightGainTerminalCorrectionModel( +ReturnCode HeightGainTerminalCorrectionModel( const double f__ghz, const double h__meter, const double w_s__meter, @@ -36,7 +36,7 @@ int HeightGainTerminalCorrectionModel( const ClutterType clutter_type, double &A_h__db ) { - const int rtn + const ReturnCode rtn = Section3p1_InputValidation(f__ghz, h__meter, w_s__meter, R__meter); if (rtn != SUCCESS) return rtn; @@ -89,7 +89,7 @@ int HeightGainTerminalCorrectionModel( * @param[in] R__meter Representative clutter height, in meters * @return Return code ******************************************************************************/ -int Section3p1_InputValidation( +ReturnCode Section3p1_InputValidation( const double f__ghz, const double h__meter, const double w_s__meter, diff --git a/src/ReturnCodes.cpp b/src/ReturnCodes.cpp new file mode 100644 index 0000000..c0b7645 --- /dev/null +++ b/src/ReturnCodes.cpp @@ -0,0 +1,53 @@ +/** @file ReturnCodes.cpp + * Maps status messages to library return codes + */ + +#include "ITS.ITU.PSeries.P2108/ReturnCodes.h" + +namespace ITS { +namespace ITU { +namespace PSeries { +namespace P2108 { + +/******************************************************************************* + * Get an error message string from a return code. + * + * @param[in] code Integer return code. + * @return A status message corresponding to the input code. + ******************************************************************************/ +std::string GetReturnStatus(int code) { + static const std::unordered_map messages + = {{SUCCESS, "Successful execution"}, + {ERROR31__FREQUENCY, "Frequency must be between 0.3 and 3 GHz"}, + {ERROR31__ANTENNA_HEIGHT, "Antenna height must be >= 0 meters"}, + {ERROR31__STREET_WIDTH, "Street width must be > 0 meters"}, + {ERROR31__CLUTTER_HEIGHT, + "Representative clutter height must be > 0 meters"}, + {ERROR31__CLUTTER_TYPE, "Invalid value for clutter type"}, + {ERROR32__FREQUENCY, "Frequency must be between 2 and 67 GHz"}, + {ERROR32__DISTANCE, "Path distance must be >= 0.25 km"}, + {ERROR32__PERCENTAGE, "Percentage must be between 0 and 100"}, + {ERROR33__FREQUENCY, "Frequency must be between 10 and 100 GHz"}, + {ERROR33__THETA, "Elevation angle must be between 0 and 100 GHz"}, + {ERROR33__PERCENTAGE, "Percentage must be between 0 and 100"}}; + // Construct status message + std::string msg = LIBRARY_NAME; + if (code == SUCCESS) { + msg += " Status: "; + } else { + msg += " Error: "; + } + + auto it = messages.find(static_cast(code)); + if (it != messages.end()) { + msg += it->second + "\n"; + } else { + msg += "Undefined return code\n"; + } + return msg; +} + +} // 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 fa69369..2691738 100644 --- a/src/TerrestrialStatisticalModel.cpp +++ b/src/TerrestrialStatisticalModel.cpp @@ -25,10 +25,10 @@ namespace P2108 { * @param[out] L_ctt__db Additional loss (clutter loss), in dB * @return Return code ******************************************************************************/ -int TerrestrialStatisticalModel( +ReturnCode 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); + const ReturnCode rtn = Section3p2_InputValidation(f__ghz, d__km, p); if (rtn != SUCCESS) return rtn; @@ -58,12 +58,12 @@ double TerrestrialStatisticalModelHelper( const double f__ghz, const double d__km, const double p ) { // Equations 4a and 4b - const double sigma_l__db = 4; + constexpr 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 - const double sigma_s__db = 6; + constexpr double sigma_s__db = 6; const double L_s__db = 32.98 + 23.9 * log10(d__km) + 3 * log10(f__ghz); // Equation 3b @@ -90,7 +90,7 @@ double TerrestrialStatisticalModelHelper( * @param[in] p Percentage of locations, in % * @return Return code ******************************************************************************/ -int Section3p2_InputValidation( +ReturnCode Section3p2_InputValidation( const double f__ghz, const double d__km, const double p ) { if (f__ghz < 0.5 || f__ghz > 67) diff --git a/tests/TestAeronauticalStatisticalModel.cpp b/tests/TestAeronauticalStatisticalModel.cpp index 114e135..49506c8 100644 --- a/tests/TestAeronauticalStatisticalModel.cpp +++ b/tests/TestAeronauticalStatisticalModel.cpp @@ -19,7 +19,7 @@ TEST_F(AeronauticalStatisticalModelTest, TestAeronauticalStatisticalModel) { // Ensure test data was loaded EXPECT_NE(static_cast(testData.size()), 0); double L_ces__db; - int rtn; + ReturnCode rtn; for (const auto &data : testData) { rtn = AeronauticalStatisticalModel( data.f__ghz, data.theta__deg, data.p, L_ces__db diff --git a/tests/TestHeightGainTerminalCorrectionModel.cpp b/tests/TestHeightGainTerminalCorrectionModel.cpp index dc06b0a..b33c023 100644 --- a/tests/TestHeightGainTerminalCorrectionModel.cpp +++ b/tests/TestHeightGainTerminalCorrectionModel.cpp @@ -23,7 +23,7 @@ TEST_F( // Ensure test data was loaded EXPECT_NE(static_cast(testData.size()), 0); double A_h__db; - int rtn; + ReturnCode rtn; for (const auto &data : testData) { rtn = HeightGainTerminalCorrectionModel( data.f__ghz, diff --git a/tests/TestTerrestrialStatisticalModel.cpp b/tests/TestTerrestrialStatisticalModel.cpp index fc5ca79..7e6b32d 100644 --- a/tests/TestTerrestrialStatisticalModel.cpp +++ b/tests/TestTerrestrialStatisticalModel.cpp @@ -19,7 +19,7 @@ TEST_F(TerrestrialStatisticalModelTest, TestTerrestrialStatisticalModel) { // Ensure test data was loaded EXPECT_NE(static_cast(testData.size()), 0); double L_ctt__db; - int rtn; + ReturnCode rtn; for (const auto &data : testData) { rtn = TerrestrialStatisticalModel( data.f__ghz, data.d__km, data.p, L_ctt__db diff --git a/tests/TestUtils.cpp b/tests/TestUtils.cpp index f235e4c..b36b773 100644 --- a/tests/TestUtils.cpp +++ b/tests/TestUtils.cpp @@ -29,10 +29,12 @@ std::vector // struct to store data from a single line of CSV: AeronauticalStatisticalModelTestData d; char c; // single-character representing the comma (delimiter) + int rtn_value; 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) { + if (iss >> d.f__ghz >> c >> d.theta__deg >> c >> d.p >> c >> rtn_value + >> c >> d.L_ces__db) { + d.rtn = static_cast(rtn_value); testData.push_back(d); } } @@ -49,13 +51,15 @@ std::vector HeightGainTerminalCorrectionModelTestData d; char c; // single-character representing the comma (delimiter) int clutter_type_value; + int rtn_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.R__meter >> c >> clutter_type_value >> c >> rtn_value >> c >> d.A_h__db) { - // Convert integer to ClutterType enum + // Convert integers to enum d.clutter_type = static_cast(clutter_type_value); + d.rtn = static_cast(rtn_value); testData.push_back(d); } } @@ -71,10 +75,12 @@ std::vector // struct to store data from a single line of CSV: TerrestrialStatisticalModelTestData d; char c; // single-character representing the comma (delimiter) + int rtn_value; 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 + if (iss >> d.f__ghz >> c >> d.d__km >> c >> d.p >> c >> rtn_value >> c >> d.L_ctt__db) { + d.rtn = static_cast(rtn_value); testData.push_back(d); } } diff --git a/tests/TestUtils.h b/tests/TestUtils.h index e56e5d4..49ac701 100644 --- a/tests/TestUtils.h +++ b/tests/TestUtils.h @@ -15,7 +15,7 @@ struct AeronauticalStatisticalModelTestData { double f__ghz; double theta__deg; double p; - int rtn; + ReturnCode rtn; double L_ces__db; }; @@ -25,7 +25,7 @@ struct HeightGainTerminalCorrectionModelTestData { double w_s__meter; double R__meter; ClutterType clutter_type; - int rtn; + ReturnCode rtn; double A_h__db; }; @@ -33,7 +33,7 @@ struct TerrestrialStatisticalModelTestData { double f__ghz; double d__km; double p; - int rtn; + ReturnCode rtn; double L_ctt__db; }; From 5587408eed77d0631efc927a95380555a0d36930 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:35:32 -0400 Subject: [PATCH 142/379] Remove label macros --- app/include/Labels.h | 45 ------------------- app/include/Tags.h | 7 --- app/src/AeronauticalStatisticalModel.cpp | 6 +-- app/src/CMakeLists.txt | 1 - app/src/Driver.cpp | 8 ++-- app/src/HeightGainTerminalCorrectionModel.cpp | 8 ++-- app/src/Reporting.cpp | 14 +++--- app/src/TerrestrialStatisticalModel.cpp | 6 +-- 8 files changed, 21 insertions(+), 74 deletions(-) delete mode 100644 app/include/Labels.h diff --git a/app/include/Labels.h b/app/include/Labels.h deleted file mode 100644 index b353506..0000000 --- a/app/include/Labels.h +++ /dev/null @@ -1,45 +0,0 @@ -/** @file Labels.h - * Defines text labels for errors, warnings, and enums - */ -#pragma once - -// clang-format off - -// Skip Doxygen generation for labels -#ifndef DOXYGEN_SHOULD_SKIP - -/** Labels */ -#define LBL__SUCCESS "Success - No Errors" -#define LBL__SUCCESS_WITH_WARNINGS "Success - but with warnings" -#define LBL__NO_WARNINGS "No Warnings" - -/** P.2108 Model Names */ -#define LBL__HGTCM "Height Gain Terminal Correction Model" -#define LBL__TSM "Terrestrial Statistical Model" -#define LBL__ASM "Aeronautical Statistical Model" - -/** Height Gain Terminal Correction Clutter Types */ -#define LBL__CLUTTERTYPE_WATER_SEA "Water/sea clutter type" -#define LBL__CLUTTERTYPE_OPEN_RURAL "Open/rural clutter type" -#define LBL__CLUTTERTYPE_SUBURBAN "Suburban clutter type" -#define LBL__CLUTTERTYPE_URBAN "Urban clutter type" -#define LBL__CLUTTERTYPE_TREES_FOREST "Trees/forest clutter type" -#define LBL__CLUTTERTYPE_DENSE_URBAN "Dense urban clutter type" - -/** Error Definitions */ -#define LBL__ERROR_INVALID_VALUE "Invalid Value" -#define LBL__ERROR_UNKNOWN "An unknown error occurred" -#define LBL__ERROR31_FREQUENCY "Frequency must be between 0.3 and 3 GHz, inclusive" -#define LBL__ERROR31_ANTENNA_HEIGHT "Antenna height must be >= 0 meters" -#define LBL__ERROR31_STREET_WIDTH "Street width must be > 0 meters" -#define LBL__ERROR31_CLUTTER_HEIGHT "Representative clutter height must be > 0 meters" -#define LBL__ERROR31_CLUTTER_TYPE "Invalid value for clutter type" -#define LBL__ERROR32_FREQUENCY "Frequency must be between 2 and 67 GHz, inclusive" -#define LBL__ERROR32_DISTANCE "Path distance must be >= 0.25 km" -#define LBL__ERROR32_PERCENTAGE "Percentage must be between 0 and 100" -#define LBL__ERROR33_FREQUENCY "Frequency must be between 10 and 100 GHz, inclusive" -#define LBL__ERROR33_THETA "Elevation angle must be between 0 and 100 GHz, inclusive" -#define LBL__ERROR33_PERCENTAGE "Percentage must be between 0 and 100, inclusive" - -// clang-format on -#endif \ No newline at end of file diff --git a/app/include/Tags.h b/app/include/Tags.h index a04038a..5a83e5b 100644 --- a/app/include/Tags.h +++ b/app/include/Tags.h @@ -17,13 +17,6 @@ #define TAG__CLUTTER_TYPE "clutter_type" #define TAG__PATH_DIST "d__km" -/** Unit Labels */ -#define UNITS__GHZ "(gigahertz)" -#define UNITS__DEGREES "(degrees)" -#define UNITS__PERCENT "(%)" -#define UNITS__METER "(meters)" -#define UNITS__KM "(kilometers)" -#define UNITS__DB "(dB)" // clang-format on #endif \ No newline at end of file diff --git a/app/src/AeronauticalStatisticalModel.cpp b/app/src/AeronauticalStatisticalModel.cpp index d54cbe3..491d799 100644 --- a/app/src/AeronauticalStatisticalModel.cpp +++ b/app/src/AeronauticalStatisticalModel.cpp @@ -88,7 +88,7 @@ DrvrReturnCode * @param[in] params ASM input parameter struct ******************************************************************************/ void WriteASMInputs(std::ofstream &fp, const ASMParams ¶ms) { - fp PRINT TAG__FREQ SETW13 params.f__ghz << UNITS__GHZ; - fp PRINT TAG__THETA SETW13 params.theta__deg << UNITS__DEGREES; - fp PRINT TAG__PERCENTAGE SETW13 params.p << UNITS__PERCENT; + fp PRINT TAG__FREQ SETW13 params.f__ghz << "(gigahertz)"; + fp PRINT TAG__THETA SETW13 params.theta__deg << "(degrees)"; + fp PRINT TAG__PERCENTAGE SETW13 params.p << "(%)"; } \ No newline at end of file diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index 68c1cf6..b8611d6 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -15,7 +15,6 @@ add_executable( "${DRIVER_HEADERS}/CommaSeparatedIterator.h" "${DRIVER_HEADERS}/Driver.h" "${DRIVER_HEADERS}/ReturnCodes.h" - "${DRIVER_HEADERS}/Labels.h" "${DRIVER_HEADERS}/Structs.h" "${DRIVER_HEADERS}/Tags.h" ) diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index d6bf1af..60c04da 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -83,13 +83,13 @@ int main(int argc, char **argv) { fp PRINT "Model Variant"; switch (params.model) { case P2108Model::HGTCM: - fp << LBL__HGTCM; + fp << "Height Gain Terminal Correction Model"; break; case P2108Model::TSM: - fp << LBL__TSM; + fp << "Terrestrial Statistical Model"; break; case P2108Model::ASM: - fp << LBL__ASM; + fp << "Aeronautical Statistical Model"; break; // Validation above ensures one of these cases evaluates } @@ -124,7 +124,7 @@ int main(int argc, char **argv) { fp PRINT "Return Code" SETW13 rtn; PrintLabel(fp, GetReturnStatus(rtn)); fp PRINT "Clutter loss" SETW13 std::fixed - << std::setprecision(1) << loss__db.front() << UNITS__DB; + << std::setprecision(1) << loss__db.front() << "(dB)"; } fp.close(); return SUCCESS; diff --git a/app/src/HeightGainTerminalCorrectionModel.cpp b/app/src/HeightGainTerminalCorrectionModel.cpp index 088c10c..37d9257 100644 --- a/app/src/HeightGainTerminalCorrectionModel.cpp +++ b/app/src/HeightGainTerminalCorrectionModel.cpp @@ -106,10 +106,10 @@ DrvrReturnCode * @param[in] params HGTCM input parameter struct ******************************************************************************/ void WriteHGTCInputs(std::ofstream &fp, const HGTCParams ¶ms) { - fp PRINT TAG__FREQ SETW13 params.f__ghz << UNITS__GHZ; - fp PRINT TAG__HEIGHT SETW13 params.h__meter << UNITS__METER; - fp PRINT TAG__STREET_WIDTH SETW13 params.w_s__meter << UNITS__METER; - fp PRINT TAG__REPR_HEIGHT SETW13 params.R__meter << UNITS__METER; + fp PRINT TAG__FREQ SETW13 params.f__ghz << "(gigahertz)"; + fp PRINT TAG__HEIGHT SETW13 params.h__meter << "(meters)"; + fp PRINT TAG__STREET_WIDTH SETW13 params.w_s__meter << "(meters)"; + fp PRINT TAG__REPR_HEIGHT SETW13 params.R__meter << "(meters)"; fp PRINT TAG__CLUTTER_TYPE SETW13 static_cast(params.clutter_type); PrintClutterTypeLabel(fp, params.clutter_type); } diff --git a/app/src/Reporting.cpp b/app/src/Reporting.cpp index f820d64..06f233a 100644 --- a/app/src/Reporting.cpp +++ b/app/src/Reporting.cpp @@ -15,25 +15,25 @@ void PrintClutterTypeLabel(std::ofstream &fp, const ClutterType clutter_type) { std::string label; switch (clutter_type) { case ClutterType::WATER_SEA: - label = LBL__CLUTTERTYPE_WATER_SEA; + label = "Water/sea clutter type"; break; case ClutterType::OPEN_RURAL: - label = LBL__CLUTTERTYPE_OPEN_RURAL; + label = "Open/rural clutter type"; break; case ClutterType::SUBURBAN: - label = LBL__CLUTTERTYPE_SUBURBAN; + label = "Suburban clutter type"; break; case ClutterType::URBAN: - label = LBL__CLUTTERTYPE_URBAN; + label = "Urban clutter type"; break; case ClutterType::TREES_FOREST: - label = LBL__CLUTTERTYPE_TREES_FOREST; + label = "Trees/forest clutter type"; break; case ClutterType::DENSE_URBAN: - label = LBL__CLUTTERTYPE_DENSE_URBAN; + label = "Dense urban clutter type"; break; default: - label = LBL__ERROR_INVALID_VALUE; + label = "Invalid clutter type"; break; } PrintLabel(fp, label); diff --git a/app/src/TerrestrialStatisticalModel.cpp b/app/src/TerrestrialStatisticalModel.cpp index 0bc6dd7..6503187 100644 --- a/app/src/TerrestrialStatisticalModel.cpp +++ b/app/src/TerrestrialStatisticalModel.cpp @@ -88,7 +88,7 @@ DrvrReturnCode * @param[in] params TSM input parameter struct ******************************************************************************/ void WriteTSMInputs(std::ofstream &fp, const TSMParams ¶ms) { - fp PRINT TAG__FREQ SETW13 params.f__ghz << UNITS__GHZ; - fp PRINT TAG__THETA SETW13 params.d__km << UNITS__KM; - fp PRINT TAG__PERCENTAGE SETW13 params.p << UNITS__PERCENT; + fp PRINT TAG__FREQ SETW13 params.f__ghz << "(gigahertz)"; + fp PRINT TAG__THETA SETW13 params.d__km << "(kilometers)"; + fp PRINT TAG__PERCENTAGE SETW13 params.p << "(%)"; } \ No newline at end of file From ca7766b4192ed946141e4c3c11f98f01ca43edec Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:36:00 -0400 Subject: [PATCH 143/379] Rename HGTCParams to HGTCMParams --- app/include/Driver.h | 6 +++--- app/include/Structs.h | 2 +- app/src/Driver.cpp | 2 +- app/src/HeightGainTerminalCorrectionModel.cpp | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/include/Driver.h b/app/include/Driver.h index 052a3ce..3d8072f 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -44,11 +44,11 @@ void WriteASMInputs(std::ofstream &fp, const ASMParams ¶ms); // Height Gain Terminal Correction Model ReturnCode CallHeightGainTerminalCorrectionModel( - HGTCParams &hgtc_params, std::vector &A_h__db + HGTCMParams &hgtc_params, std::vector &A_h__db ); DrvrReturnCode - ParseHGTCInputFile(const std::string &in_file, HGTCParams &hgtc_params); -void WriteHGTCInputs(std::ofstream &fp, const HGTCParams ¶ms); + ParseHGTCInputFile(const std::string &in_file, HGTCMParams &hgtc_params); +void WriteHGTCInputs(std::ofstream &fp, const HGTCMParams ¶ms); // Terrestrial Statistical Model ReturnCode CallTerrestrialStatisticalModel( diff --git a/app/include/Structs.h b/app/include/Structs.h index 991e97f..50c4859 100644 --- a/app/include/Structs.h +++ b/app/include/Structs.h @@ -26,7 +26,7 @@ struct DrvrParams { }; /** Input parameters for the Height Gain Terminal Correction Model */ -struct HGTCParams { +struct HGTCMParams { double f__ghz; /**< Frequency, in GHz */ double h__meter; /**< Antenna height, in meters */ double w_s__meter; /**< Street width, in meters */ diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index 60c04da..7489ee7 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -36,7 +36,7 @@ int main(int argc, char **argv) { } // Initialize model inputs/outputs - HGTCParams hgtc_params; + HGTCMParams hgtc_params; TSMParams tsm_params; ASMParams asm_params; std::vector loss__db; // Use for any model diff --git a/app/src/HeightGainTerminalCorrectionModel.cpp b/app/src/HeightGainTerminalCorrectionModel.cpp index 37d9257..8d867d8 100644 --- a/app/src/HeightGainTerminalCorrectionModel.cpp +++ b/app/src/HeightGainTerminalCorrectionModel.cpp @@ -13,7 +13,7 @@ * @return Return code ******************************************************************************/ ReturnCode CallHeightGainTerminalCorrectionModel( - HGTCParams &hgtc_params, std::vector &A_h__db + HGTCMParams &hgtc_params, std::vector &A_h__db ) { ReturnCode rtn; double A_h; @@ -38,7 +38,7 @@ ReturnCode CallHeightGainTerminalCorrectionModel( * @return Return code ******************************************************************************/ DrvrReturnCode - ParseHGTCInputStream(std::istream &stream, HGTCParams &hgtc_params) { + ParseHGTCInputStream(std::istream &stream, HGTCMParams &hgtc_params) { CommaSeparatedIterator it(stream); DrvrReturnCode rtn = DRVR__SUCCESS; std::string key, value; @@ -90,7 +90,7 @@ DrvrReturnCode * @return Return code ******************************************************************************/ DrvrReturnCode - ParseHGTCInputFile(const std::string &in_file, HGTCParams &hgtc_params) { + ParseHGTCInputFile(const std::string &in_file, HGTCMParams &hgtc_params) { std::ifstream file(in_file); if (!file) { std::cerr << "Failed to open file " << in_file << std::endl; @@ -105,7 +105,7 @@ DrvrReturnCode * @param[in] fp Output stream, a text file open for writing * @param[in] params HGTCM input parameter struct ******************************************************************************/ -void WriteHGTCInputs(std::ofstream &fp, const HGTCParams ¶ms) { +void WriteHGTCInputs(std::ofstream &fp, const HGTCMParams ¶ms) { fp PRINT TAG__FREQ SETW13 params.f__ghz << "(gigahertz)"; fp PRINT TAG__HEIGHT SETW13 params.h__meter << "(meters)"; fp PRINT TAG__STREET_WIDTH SETW13 params.w_s__meter << "(meters)"; From 687db5b1d46c99eb04f719f201766558d0d41f2e Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:44:27 -0400 Subject: [PATCH 144/379] Replace input key tags with typed structs --- app/include/Structs.h | 29 ++++++++++++++++++- app/include/Tags.h | 22 -------------- app/src/AeronauticalStatisticalModel.cpp | 12 ++++---- app/src/CMakeLists.txt | 1 - app/src/HeightGainTerminalCorrectionModel.cpp | 22 +++++++------- app/src/TerrestrialStatisticalModel.cpp | 12 ++++---- 6 files changed, 52 insertions(+), 46 deletions(-) delete mode 100644 app/include/Tags.h diff --git a/app/include/Structs.h b/app/include/Structs.h index 50c4859..1615464 100644 --- a/app/include/Structs.h +++ b/app/include/Structs.h @@ -35,6 +35,17 @@ struct HGTCMParams { clutter_type; /**< Clutter type (enum value) */ }; +/** Key names for Height Gain Terminal Correction Model input file parameters */ +struct HGTCMInputKeys { + std::string f__ghz = "f__ghz"; /**< Frequency, in GHz */ + std::string h__meter = "h__meter"; /**< Antenna height, in meters */ + std::string w_s__meter = "w_s__meter"; /**< Street width, in meters */ + std::string R__meter + = "r__meter"; /**< Representative clutter height, in meters */ + std::string clutter_type + = "clutter_type"; /**< Clutter type (enum value) */ +}; + /** Input parameters for the Terrestrial Statistical Model */ struct TSMParams { double f__ghz; /**< Frequency, in GHz */ @@ -42,9 +53,25 @@ struct TSMParams { double p; /**< Percentage of locations */ }; +/** Key names for Terrestrial Statistical Model input file parameters */ +struct TSMInputKeys { + std::string f__ghz = "f__ghz"; /**< Frequency, in GHz */ + std::string d__km = "d__km"; /**< Path distance, in km */ + std::string p = "p"; /**< Percentage of locations */ +}; + + /** Input parameters for the Aeronautical Statistical Model */ struct ASMParams { double f__ghz; /**< Frequency, in GHz */ double theta__deg; /**< Elevation angle, in degrees */ double p; /**< Percentage of locations */ -}; \ No newline at end of file +}; + +/** Key names for Aeronautical Statistical Model input file parameters */ +struct ASMInputKeys { + std::string f__ghz = "f__ghz"; /**< Frequency, in GHz */ + std::string theta__deg + = "theta__deg"; /**< Elevation angle, in degrees */ + std::string p = "p"; /**< Percentage of locations */ +}; diff --git a/app/include/Tags.h b/app/include/Tags.h deleted file mode 100644 index 5a83e5b..0000000 --- a/app/include/Tags.h +++ /dev/null @@ -1,22 +0,0 @@ -/** @file Tags.h - * Defines tags for input variables and macros for printing units - */ -#pragma once - -// clang-format off -// Skip Doxygen generation for tags -#ifndef DOXYGEN_SHOULD_SKIP - -/** Input File Tags */ -#define TAG__FREQ "f__ghz" -#define TAG__THETA "theta__deg" -#define TAG__PERCENTAGE "p" -#define TAG__HEIGHT "h__meter" -#define TAG__STREET_WIDTH "w_s__meter" -#define TAG__REPR_HEIGHT "r__meter" -#define TAG__CLUTTER_TYPE "clutter_type" -#define TAG__PATH_DIST "d__km" - - -// clang-format on -#endif \ No newline at end of file diff --git a/app/src/AeronauticalStatisticalModel.cpp b/app/src/AeronauticalStatisticalModel.cpp index 491d799..076ead2 100644 --- a/app/src/AeronauticalStatisticalModel.cpp +++ b/app/src/AeronauticalStatisticalModel.cpp @@ -39,15 +39,15 @@ DrvrReturnCode std::string key, value, errMsg; while (it) { std::tie(key, value) = *it; - if (key.compare(TAG__FREQ) == 0) { + if (key.compare(ASMInputKeys.f__ghz) == 0) { rtn = ParseDouble(value, asm_params.f__ghz); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_FREQ; - } else if (key.compare(TAG__THETA) == 0) { + } else if (key.compare(ASMInputKeys.theta__deg) == 0) { rtn = ParseDouble(value, asm_params.theta__deg); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_THETA; - } else if (key.compare(TAG__PERCENTAGE) == 0) { + } else if (key.compare(ASMInputKeys.p) == 0) { rtn = ParseDouble(value, asm_params.p); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_PERCENTAGE; @@ -88,7 +88,7 @@ DrvrReturnCode * @param[in] params ASM input parameter struct ******************************************************************************/ void WriteASMInputs(std::ofstream &fp, const ASMParams ¶ms) { - fp PRINT TAG__FREQ SETW13 params.f__ghz << "(gigahertz)"; - fp PRINT TAG__THETA SETW13 params.theta__deg << "(degrees)"; - fp PRINT TAG__PERCENTAGE SETW13 params.p << "(%)"; + fp PRINT ASMInputKeys.f__ghz SETW13 params.f__ghz << "(gigahertz)"; + fp PRINT ASMInputKeys.theta__deg SETW13 params.theta__deg << "(degrees)"; + fp PRINT ASMInputKeys.p SETW13 params.p << "(%)"; } \ No newline at end of file diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index b8611d6..5cc82e1 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -16,7 +16,6 @@ add_executable( "${DRIVER_HEADERS}/Driver.h" "${DRIVER_HEADERS}/ReturnCodes.h" "${DRIVER_HEADERS}/Structs.h" - "${DRIVER_HEADERS}/Tags.h" ) # Add the include directory diff --git a/app/src/HeightGainTerminalCorrectionModel.cpp b/app/src/HeightGainTerminalCorrectionModel.cpp index 8d867d8..00c779a 100644 --- a/app/src/HeightGainTerminalCorrectionModel.cpp +++ b/app/src/HeightGainTerminalCorrectionModel.cpp @@ -44,23 +44,23 @@ DrvrReturnCode std::string key, value; while (it) { std::tie(key, value) = *it; - if (key.compare(TAG__FREQ) == 0) { + if (key.compare(HGTCMInputKeys.f__ghz) == 0) { rtn = ParseDouble(value, hgtc_params.f__ghz); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_FREQ; - } else if (key.compare(TAG__HEIGHT) == 0) { + } else if (key.compare(HGTCMInputKeys.h__meter) == 0) { rtn = ParseDouble(value, hgtc_params.h__meter); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_HEIGHT; - } else if (key.compare(TAG__STREET_WIDTH) == 0) { + } else if (key.compare(HGTCMInputKeys.w_s__meter) == 0) { rtn = ParseDouble(value, hgtc_params.w_s__meter); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_STREET_WIDTH; - } else if (key.compare(TAG__REPR_HEIGHT) == 0) { + } else if (key.compare(HGTCMInputKeys.R__meter) == 0) { rtn = ParseDouble(value, hgtc_params.R__meter); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_REPR_HEIGHT; - } else if (key.compare(TAG__CLUTTER_TYPE) == 0) { + } else if (key.compare(HGTCMInputKeys.clutter_type) == 0) { int clutter_type_int; rtn = ParseInteger(value, clutter_type_int); if (rtn == DRVRERR__PARSE) { @@ -106,10 +106,12 @@ DrvrReturnCode * @param[in] params HGTCM input parameter struct ******************************************************************************/ void WriteHGTCInputs(std::ofstream &fp, const HGTCMParams ¶ms) { - fp PRINT TAG__FREQ SETW13 params.f__ghz << "(gigahertz)"; - fp PRINT TAG__HEIGHT SETW13 params.h__meter << "(meters)"; - fp PRINT TAG__STREET_WIDTH SETW13 params.w_s__meter << "(meters)"; - fp PRINT TAG__REPR_HEIGHT SETW13 params.R__meter << "(meters)"; - fp PRINT TAG__CLUTTER_TYPE SETW13 static_cast(params.clutter_type); + fp PRINT HGTCMInputKeys.f__ghz SETW13 params.f__ghz << "(gigahertz)"; + fp PRINT HGTCMInputKeys.h__meter SETW13 params.h__meter << "(meters)"; + fp PRINT HGTCMInputKeys.w_s__meter SETW13 params.w_s__meter << "(meters)"; + fp PRINT HGTCMInputKeys.R__meter SETW13 params.R__meter << "(meters)"; + fp PRINT HGTCMInputKeys.clutter_type SETW13 static_cast( + params.clutter_type + ); PrintClutterTypeLabel(fp, params.clutter_type); } diff --git a/app/src/TerrestrialStatisticalModel.cpp b/app/src/TerrestrialStatisticalModel.cpp index 6503187..d5999e5 100644 --- a/app/src/TerrestrialStatisticalModel.cpp +++ b/app/src/TerrestrialStatisticalModel.cpp @@ -39,15 +39,15 @@ DrvrReturnCode std::string key, value; while (it) { std::tie(key, value) = *it; - if (key.compare(TAG__FREQ) == 0) { + if (key.compare(TSMInputKeys.f__ghz) == 0) { rtn = ParseDouble(value, tsm_params.f__ghz); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_FREQ; - } else if (key.compare(TAG__PATH_DIST) == 0) { + } else if (key.compare(TSMInputKeys.d__km) == 0) { rtn = ParseDouble(value, tsm_params.d__km); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_PATH_DIST; - } else if (key.compare(TAG__PERCENTAGE) == 0) { + } else if (key.compare(TSMInputKeys.p) == 0) { rtn = ParseDouble(value, tsm_params.p); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_PERCENTAGE; @@ -88,7 +88,7 @@ DrvrReturnCode * @param[in] params TSM input parameter struct ******************************************************************************/ void WriteTSMInputs(std::ofstream &fp, const TSMParams ¶ms) { - fp PRINT TAG__FREQ SETW13 params.f__ghz << "(gigahertz)"; - fp PRINT TAG__THETA SETW13 params.d__km << "(kilometers)"; - fp PRINT TAG__PERCENTAGE SETW13 params.p << "(%)"; + fp PRINT TSMInputKeys.f__ghz SETW13 params.f__ghz << "(gigahertz)"; + fp PRINT TSMInputKeys.d__km SETW13 params.d__km << "(kilometers)"; + fp PRINT TSMInputKeys.p SETW13 params.p << "(%)"; } \ No newline at end of file From a30c31b22aa1f05e61c6c2029a3defb94f87ff69 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:46:33 -0400 Subject: [PATCH 145/379] Switch library enum class to enum --- app/include/Structs.h | 3 +-- include/ITS.ITU.PSeries.P2108/Enums.h | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/include/Structs.h b/app/include/Structs.h index 1615464..9b3b667 100644 --- a/app/include/Structs.h +++ b/app/include/Structs.h @@ -31,8 +31,7 @@ struct HGTCMParams { double h__meter; /**< Antenna height, in meters */ double w_s__meter; /**< Street width, in meters */ double R__meter; /**< Representative clutter height, in meters */ - ITS::ITU::PSeries::P2108::ClutterType - clutter_type; /**< Clutter type (enum value) */ + ClutterType clutter_type; /**< Clutter type (enum value) */ }; /** Key names for Height Gain Terminal Correction Model input file parameters */ diff --git a/include/ITS.ITU.PSeries.P2108/Enums.h b/include/ITS.ITU.PSeries.P2108/Enums.h index f63d635..2ce86db 100644 --- a/include/ITS.ITU.PSeries.P2108/Enums.h +++ b/include/ITS.ITU.PSeries.P2108/Enums.h @@ -9,7 +9,7 @@ namespace PSeries { namespace P2108 { /** Clutter type enum, based on Table 3 in Section 3.1 */ -enum class ClutterType { +enum ClutterType { WATER_SEA = 1, /**< Water/sea clutter type */ OPEN_RURAL = 2, /**< Open/rural clutter type */ SUBURBAN = 3, /**< Suburban clutter type */ @@ -25,7 +25,7 @@ enum class ClutterType { * These should be used as inputs to the height gain terminal * correction model when local information is not available. */ -enum class RepresentativeClutterHeight { +enum 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 */ From 5500b55f89899b6cdcdce727f78e88b155213e04 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:47:16 -0400 Subject: [PATCH 146/379] Remove include statements for deleted files --- app/src/AeronauticalStatisticalModel.cpp | 2 -- app/src/Driver.cpp | 3 --- app/src/HeightGainTerminalCorrectionModel.cpp | 2 -- app/src/Reporting.cpp | 2 -- app/src/TerrestrialStatisticalModel.cpp | 2 -- 5 files changed, 11 deletions(-) diff --git a/app/src/AeronauticalStatisticalModel.cpp b/app/src/AeronauticalStatisticalModel.cpp index 076ead2..975944b 100644 --- a/app/src/AeronauticalStatisticalModel.cpp +++ b/app/src/AeronauticalStatisticalModel.cpp @@ -2,8 +2,6 @@ * Implements top-level functions for running the Aeronautical Statistical Model. */ #include "Driver.h" -#include "Tags.h" - /******************************************************************************* * Top-level control function for Aeronautical Statistical Model operation diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index 7489ee7..35d6c85 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -3,9 +3,6 @@ */ #include "Driver.h" -#include "Labels.h" -#include "Tags.h" - #include // for std::find /******************************************************************************* diff --git a/app/src/HeightGainTerminalCorrectionModel.cpp b/app/src/HeightGainTerminalCorrectionModel.cpp index 00c779a..d0d77fb 100644 --- a/app/src/HeightGainTerminalCorrectionModel.cpp +++ b/app/src/HeightGainTerminalCorrectionModel.cpp @@ -2,8 +2,6 @@ * Implements functions for running the Height Gain Terminal Correction Model. */ #include "Driver.h" -#include "Tags.h" - /******************************************************************************* * Top-level control function for Height Gain Terminal Correction Model diff --git a/app/src/Reporting.cpp b/app/src/Reporting.cpp index 06f233a..c5065a4 100644 --- a/app/src/Reporting.cpp +++ b/app/src/Reporting.cpp @@ -2,8 +2,6 @@ * Implements utility functions for printing driver results */ #include "Driver.h" -#include "Labels.h" -#include "Tags.h" /******************************************************************************* * Print text message corresponding to clutter type enum value diff --git a/app/src/TerrestrialStatisticalModel.cpp b/app/src/TerrestrialStatisticalModel.cpp index d5999e5..05783a9 100644 --- a/app/src/TerrestrialStatisticalModel.cpp +++ b/app/src/TerrestrialStatisticalModel.cpp @@ -2,8 +2,6 @@ * Implements top-level functions for running the Terrestrial Statistical Model. */ #include "Driver.h" -#include "Tags.h" - /******************************************************************************* * Top-level control function for Terrestrial Statistical Model operation From b320b36b30b864591f9dcc0db2bd98b396b19c87 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:48:55 -0400 Subject: [PATCH 147/379] Avoid name conflict in enums --- include/ITS.ITU.PSeries.P2108/Enums.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/ITS.ITU.PSeries.P2108/Enums.h b/include/ITS.ITU.PSeries.P2108/Enums.h index 2ce86db..769af60 100644 --- a/include/ITS.ITU.PSeries.P2108/Enums.h +++ b/include/ITS.ITU.PSeries.P2108/Enums.h @@ -26,12 +26,12 @@ enum ClutterType { * correction model when local information is not available. */ enum 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 */ + R__WATER_SEA = 10, /**< @f$ R @f$ for the trees/forest clutter type */ + R__OPEN_RURAL = 10, /**< @f$ R @f$ for the open/rural clutter type */ + R__SUBURBAN = 10, /**< @f$ R @f$ for the suburban clutter type */ + R__URBAN = 15, /**< @f$ R @f$ for the urban clutter type */ + R__TREES_FOREST = 15, /**< @f$ R @f$ for the trees/forest clutter type */ + R__DENSE_URBAN = 20, /**< @f$ R @f$ for the dense urban clutter type */ }; } // namespace P2108 From d7333af47f5210733cd8ed3455c1fc94bd61b483 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:55:21 -0400 Subject: [PATCH 148/379] Make input key structs constants --- app/include/Structs.h | 38 +++++++++++-------- app/src/AeronauticalStatisticalModel.cpp | 12 +++--- app/src/HeightGainTerminalCorrectionModel.cpp | 20 +++++----- app/src/TerrestrialStatisticalModel.cpp | 12 +++--- 4 files changed, 45 insertions(+), 37 deletions(-) diff --git a/app/include/Structs.h b/app/include/Structs.h index 9b3b667..4c0106a 100644 --- a/app/include/Structs.h +++ b/app/include/Structs.h @@ -36,14 +36,18 @@ struct HGTCMParams { /** Key names for Height Gain Terminal Correction Model input file parameters */ struct HGTCMInputKeys { - std::string f__ghz = "f__ghz"; /**< Frequency, in GHz */ - std::string h__meter = "h__meter"; /**< Antenna height, in meters */ - std::string w_s__meter = "w_s__meter"; /**< Street width, in meters */ - std::string R__meter - = "r__meter"; /**< Representative clutter height, in meters */ - std::string clutter_type - = "clutter_type"; /**< Clutter type (enum value) */ + static const std::string f__ghz; /**< Frequency, in GHz */ + static const std::string h__meter; /**< Antenna height, in meters */ + static const std::string w_s__meter; /**< Street width, in meters */ + static const std::string + R__meter; /**< Representative clutter height, in meters */ + static const std::string clutter_type; /**< Clutter type (enum value) */ }; +const std::string HGTCMInputKeys::f__ghz = "f__ghz"; +const std::string HGTCMInputKeys::h__meter = "h__meter"; +const std::string HGTCMInputKeys::w_s__meter = "w_s__meter"; +const std::string HGTCMInputKeys::R__meter = "r__meter"; +const std::string HGTCMInputKeys::clutter_type = "clutter_type"; /** Input parameters for the Terrestrial Statistical Model */ struct TSMParams { @@ -54,11 +58,13 @@ struct TSMParams { /** Key names for Terrestrial Statistical Model input file parameters */ struct TSMInputKeys { - std::string f__ghz = "f__ghz"; /**< Frequency, in GHz */ - std::string d__km = "d__km"; /**< Path distance, in km */ - std::string p = "p"; /**< Percentage of locations */ + static const std::string f__ghz; /**< Frequency, in GHz */ + static const std::string d__km; /**< Path distance, in km */ + static const std::string p; /**< Percentage of locations */ }; - +const std::string TSMInputKeys::f__ghz = "f__ghz"; +const std::string TSMInputKeys::d__km = "d__km"; +const std::string TSMInputKeys::p = "p"; /** Input parameters for the Aeronautical Statistical Model */ struct ASMParams { @@ -69,8 +75,10 @@ struct ASMParams { /** Key names for Aeronautical Statistical Model input file parameters */ struct ASMInputKeys { - std::string f__ghz = "f__ghz"; /**< Frequency, in GHz */ - std::string theta__deg - = "theta__deg"; /**< Elevation angle, in degrees */ - std::string p = "p"; /**< Percentage of locations */ + static const std::string f__ghz; /**< Frequency, in GHz */ + static const std::string theta__deg; /**< Elevation angle, in degrees */ + static const std::string p; /**< Percentage of locations */ }; +const std::string ASMInputKeys::f__ghz = "f__ghz"; +const std::string ASMInputKeys::theta__deg = "theta__deg"; +const std::string ASMInputKeys::p = "p"; \ No newline at end of file diff --git a/app/src/AeronauticalStatisticalModel.cpp b/app/src/AeronauticalStatisticalModel.cpp index 975944b..e7739c9 100644 --- a/app/src/AeronauticalStatisticalModel.cpp +++ b/app/src/AeronauticalStatisticalModel.cpp @@ -37,15 +37,15 @@ DrvrReturnCode std::string key, value, errMsg; while (it) { std::tie(key, value) = *it; - if (key.compare(ASMInputKeys.f__ghz) == 0) { + if (key.compare(ASMInputKeys::f__ghz) == 0) { rtn = ParseDouble(value, asm_params.f__ghz); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_FREQ; - } else if (key.compare(ASMInputKeys.theta__deg) == 0) { + } else if (key.compare(ASMInputKeys::theta__deg) == 0) { rtn = ParseDouble(value, asm_params.theta__deg); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_THETA; - } else if (key.compare(ASMInputKeys.p) == 0) { + } else if (key.compare(ASMInputKeys::p) == 0) { rtn = ParseDouble(value, asm_params.p); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_PERCENTAGE; @@ -86,7 +86,7 @@ DrvrReturnCode * @param[in] params ASM input parameter struct ******************************************************************************/ void WriteASMInputs(std::ofstream &fp, const ASMParams ¶ms) { - fp PRINT ASMInputKeys.f__ghz SETW13 params.f__ghz << "(gigahertz)"; - fp PRINT ASMInputKeys.theta__deg SETW13 params.theta__deg << "(degrees)"; - fp PRINT ASMInputKeys.p SETW13 params.p << "(%)"; + fp PRINT ASMInputKeys::f__ghz SETW13 params.f__ghz << "(gigahertz)"; + fp PRINT ASMInputKeys::theta__deg SETW13 params.theta__deg << "(degrees)"; + fp PRINT ASMInputKeys::p SETW13 params.p << "(%)"; } \ No newline at end of file diff --git a/app/src/HeightGainTerminalCorrectionModel.cpp b/app/src/HeightGainTerminalCorrectionModel.cpp index d0d77fb..14cb2ca 100644 --- a/app/src/HeightGainTerminalCorrectionModel.cpp +++ b/app/src/HeightGainTerminalCorrectionModel.cpp @@ -42,23 +42,23 @@ DrvrReturnCode std::string key, value; while (it) { std::tie(key, value) = *it; - if (key.compare(HGTCMInputKeys.f__ghz) == 0) { + if (key.compare(HGTCMInputKeys::f__ghz) == 0) { rtn = ParseDouble(value, hgtc_params.f__ghz); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_FREQ; - } else if (key.compare(HGTCMInputKeys.h__meter) == 0) { + } else if (key.compare(HGTCMInputKeys::h__meter) == 0) { rtn = ParseDouble(value, hgtc_params.h__meter); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_HEIGHT; - } else if (key.compare(HGTCMInputKeys.w_s__meter) == 0) { + } else if (key.compare(HGTCMInputKeys::w_s__meter) == 0) { rtn = ParseDouble(value, hgtc_params.w_s__meter); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_STREET_WIDTH; - } else if (key.compare(HGTCMInputKeys.R__meter) == 0) { + } else if (key.compare(HGTCMInputKeys::R__meter) == 0) { rtn = ParseDouble(value, hgtc_params.R__meter); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_REPR_HEIGHT; - } else if (key.compare(HGTCMInputKeys.clutter_type) == 0) { + } else if (key.compare(HGTCMInputKeys::clutter_type) == 0) { int clutter_type_int; rtn = ParseInteger(value, clutter_type_int); if (rtn == DRVRERR__PARSE) { @@ -104,11 +104,11 @@ DrvrReturnCode * @param[in] params HGTCM input parameter struct ******************************************************************************/ void WriteHGTCInputs(std::ofstream &fp, const HGTCMParams ¶ms) { - fp PRINT HGTCMInputKeys.f__ghz SETW13 params.f__ghz << "(gigahertz)"; - fp PRINT HGTCMInputKeys.h__meter SETW13 params.h__meter << "(meters)"; - fp PRINT HGTCMInputKeys.w_s__meter SETW13 params.w_s__meter << "(meters)"; - fp PRINT HGTCMInputKeys.R__meter SETW13 params.R__meter << "(meters)"; - fp PRINT HGTCMInputKeys.clutter_type SETW13 static_cast( + fp PRINT HGTCMInputKeys::f__ghz SETW13 params.f__ghz << "(gigahertz)"; + fp PRINT HGTCMInputKeys::h__meter SETW13 params.h__meter << "(meters)"; + fp PRINT HGTCMInputKeys::w_s__meter SETW13 params.w_s__meter << "(meters)"; + fp PRINT HGTCMInputKeys::R__meter SETW13 params.R__meter << "(meters)"; + fp PRINT HGTCMInputKeys::clutter_type SETW13 static_cast( params.clutter_type ); PrintClutterTypeLabel(fp, params.clutter_type); diff --git a/app/src/TerrestrialStatisticalModel.cpp b/app/src/TerrestrialStatisticalModel.cpp index 05783a9..866d465 100644 --- a/app/src/TerrestrialStatisticalModel.cpp +++ b/app/src/TerrestrialStatisticalModel.cpp @@ -37,15 +37,15 @@ DrvrReturnCode std::string key, value; while (it) { std::tie(key, value) = *it; - if (key.compare(TSMInputKeys.f__ghz) == 0) { + if (key.compare(TSMInputKeys::f__ghz) == 0) { rtn = ParseDouble(value, tsm_params.f__ghz); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_FREQ; - } else if (key.compare(TSMInputKeys.d__km) == 0) { + } else if (key.compare(TSMInputKeys::d__km) == 0) { rtn = ParseDouble(value, tsm_params.d__km); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_PATH_DIST; - } else if (key.compare(TSMInputKeys.p) == 0) { + } else if (key.compare(TSMInputKeys::p) == 0) { rtn = ParseDouble(value, tsm_params.p); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_PERCENTAGE; @@ -86,7 +86,7 @@ DrvrReturnCode * @param[in] params TSM input parameter struct ******************************************************************************/ void WriteTSMInputs(std::ofstream &fp, const TSMParams ¶ms) { - fp PRINT TSMInputKeys.f__ghz SETW13 params.f__ghz << "(gigahertz)"; - fp PRINT TSMInputKeys.d__km SETW13 params.d__km << "(kilometers)"; - fp PRINT TSMInputKeys.p SETW13 params.p << "(%)"; + fp PRINT TSMInputKeys::f__ghz SETW13 params.f__ghz << "(gigahertz)"; + fp PRINT TSMInputKeys::d__km SETW13 params.d__km << "(kilometers)"; + fp PRINT TSMInputKeys::p SETW13 params.p << "(%)"; } \ No newline at end of file From 6a96a797419774a013beabd6cbe04d47f7bbf66a Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 28 Oct 2024 19:02:28 -0400 Subject: [PATCH 149/379] Fix errors Added a missing include/namespace and fixed constant initializations --- app/include/Structs.h | 22 ++++++------------- app/src/AeronauticalStatisticalModel.cpp | 5 +++++ app/src/HeightGainTerminalCorrectionModel.cpp | 7 ++++++ app/src/TerrestrialStatisticalModel.cpp | 5 +++++ 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/app/include/Structs.h b/app/include/Structs.h index 4c0106a..bed4e06 100644 --- a/app/include/Structs.h +++ b/app/include/Structs.h @@ -3,6 +3,8 @@ */ #pragma once +#include "ITS.ITU.PSeries.P2108/Enums.h" // For ClutterType + #include // For std::string ///////////////////////////// @@ -31,7 +33,8 @@ struct HGTCMParams { double h__meter; /**< Antenna height, in meters */ double w_s__meter; /**< Street width, in meters */ double R__meter; /**< Representative clutter height, in meters */ - ClutterType clutter_type; /**< Clutter type (enum value) */ + ITS::ITU::PSeries::P2108::ClutterType + clutter_type; /**< Clutter type (enum value) */ }; /** Key names for Height Gain Terminal Correction Model input file parameters */ @@ -42,12 +45,7 @@ struct HGTCMInputKeys { static const std::string R__meter; /**< Representative clutter height, in meters */ static const std::string clutter_type; /**< Clutter type (enum value) */ -}; -const std::string HGTCMInputKeys::f__ghz = "f__ghz"; -const std::string HGTCMInputKeys::h__meter = "h__meter"; -const std::string HGTCMInputKeys::w_s__meter = "w_s__meter"; -const std::string HGTCMInputKeys::R__meter = "r__meter"; -const std::string HGTCMInputKeys::clutter_type = "clutter_type"; +}; // Constants defined in app/src/HeightGainTerminalCorrectionModel.cpp /** Input parameters for the Terrestrial Statistical Model */ struct TSMParams { @@ -61,10 +59,7 @@ struct TSMInputKeys { static const std::string f__ghz; /**< Frequency, in GHz */ static const std::string d__km; /**< Path distance, in km */ static const std::string p; /**< Percentage of locations */ -}; -const std::string TSMInputKeys::f__ghz = "f__ghz"; -const std::string TSMInputKeys::d__km = "d__km"; -const std::string TSMInputKeys::p = "p"; +}; // Constants defined in app/src/TerrestrialStatisticalModel.cpp /** Input parameters for the Aeronautical Statistical Model */ struct ASMParams { @@ -78,7 +73,4 @@ struct ASMInputKeys { static const std::string f__ghz; /**< Frequency, in GHz */ static const std::string theta__deg; /**< Elevation angle, in degrees */ static const std::string p; /**< Percentage of locations */ -}; -const std::string ASMInputKeys::f__ghz = "f__ghz"; -const std::string ASMInputKeys::theta__deg = "theta__deg"; -const std::string ASMInputKeys::p = "p"; \ No newline at end of file +}; // Constants defined in app/src/AeronauticalStatisticalModel.cpp \ No newline at end of file diff --git a/app/src/AeronauticalStatisticalModel.cpp b/app/src/AeronauticalStatisticalModel.cpp index e7739c9..d2ad323 100644 --- a/app/src/AeronauticalStatisticalModel.cpp +++ b/app/src/AeronauticalStatisticalModel.cpp @@ -3,6 +3,11 @@ */ #include "Driver.h" +// Define the input keys +const std::string ASMInputKeys::f__ghz = "f__ghz"; +const std::string ASMInputKeys::theta__deg = "theta__deg"; +const std::string ASMInputKeys::p = "p"; + /******************************************************************************* * Top-level control function for Aeronautical Statistical Model operation * diff --git a/app/src/HeightGainTerminalCorrectionModel.cpp b/app/src/HeightGainTerminalCorrectionModel.cpp index 14cb2ca..0e855bf 100644 --- a/app/src/HeightGainTerminalCorrectionModel.cpp +++ b/app/src/HeightGainTerminalCorrectionModel.cpp @@ -3,6 +3,13 @@ */ #include "Driver.h" +// Define the input keys +const std::string HGTCMInputKeys::f__ghz = "f__ghz"; +const std::string HGTCMInputKeys::h__meter = "h__meter"; +const std::string HGTCMInputKeys::w_s__meter = "w_s__meter"; +const std::string HGTCMInputKeys::R__meter = "r__meter"; +const std::string HGTCMInputKeys::clutter_type = "clutter_type"; + /******************************************************************************* * Top-level control function for Height Gain Terminal Correction Model * diff --git a/app/src/TerrestrialStatisticalModel.cpp b/app/src/TerrestrialStatisticalModel.cpp index 866d465..7947eee 100644 --- a/app/src/TerrestrialStatisticalModel.cpp +++ b/app/src/TerrestrialStatisticalModel.cpp @@ -3,6 +3,11 @@ */ #include "Driver.h" +// Define the input keys +const std::string TSMInputKeys::f__ghz = "f__ghz"; +const std::string TSMInputKeys::d__km = "d__km"; +const std::string TSMInputKeys::p = "p"; + /******************************************************************************* * Top-level control function for Terrestrial Statistical Model operation * From 23fe227239a5a8c083adbe83ebab1067f72040d5 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 28 Oct 2024 19:02:33 -0400 Subject: [PATCH 150/379] clang-format --- app/src/Driver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index 35d6c85..d4a7690 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -120,8 +120,8 @@ int main(int argc, char **argv) { fp << std::endl << std::endl << "Results"; fp PRINT "Return Code" SETW13 rtn; PrintLabel(fp, GetReturnStatus(rtn)); - fp PRINT "Clutter loss" SETW13 std::fixed - << std::setprecision(1) << loss__db.front() << "(dB)"; + fp PRINT "Clutter loss" SETW13 std::fixed << std::setprecision(1) + << loss__db.front() << "(dB)"; } fp.close(); return SUCCESS; From 2975f087b16102ffa8ba3927c522ca3c21880d89 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 28 Oct 2024 19:06:02 -0400 Subject: [PATCH 151/379] consistent acronyms "hgtc" to "hgtcm" --- app/include/Driver.h | 6 +-- app/src/Driver.cpp | 8 +-- app/src/HeightGainTerminalCorrectionModel.cpp | 50 +++++++++---------- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/app/include/Driver.h b/app/include/Driver.h index 3d8072f..0c53f3f 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -44,11 +44,11 @@ void WriteASMInputs(std::ofstream &fp, const ASMParams ¶ms); // Height Gain Terminal Correction Model ReturnCode CallHeightGainTerminalCorrectionModel( - HGTCMParams &hgtc_params, std::vector &A_h__db + HGTCMParams &hgtcm_params, std::vector &A_h__db ); DrvrReturnCode - ParseHGTCInputFile(const std::string &in_file, HGTCMParams &hgtc_params); -void WriteHGTCInputs(std::ofstream &fp, const HGTCMParams ¶ms); + ParseHGTCMInputFile(const std::string &in_file, HGTCMParams &hgtcm_params); +void WriteHGTCMInputs(std::ofstream &fp, const HGTCMParams ¶ms); // Terrestrial Statistical Model ReturnCode CallTerrestrialStatisticalModel( diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index d4a7690..860a9da 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -33,18 +33,18 @@ int main(int argc, char **argv) { } // Initialize model inputs/outputs - HGTCMParams hgtc_params; + HGTCMParams hgtcm_params; TSMParams tsm_params; ASMParams asm_params; std::vector loss__db; // Use for any model switch (params.model) { case P2108Model::HGTCM: - rtn = ParseHGTCInputFile(params.in_file, hgtc_params); + rtn = ParseHGTCMInputFile(params.in_file, hgtcm_params); if (rtn != DRVR__SUCCESS) { return rtn; } - rtn = CallHeightGainTerminalCorrectionModel(hgtc_params, loss__db); + rtn = CallHeightGainTerminalCorrectionModel(hgtcm_params, loss__db); break; case P2108Model::TSM: rtn = ParseTSMInputFile(params.in_file, tsm_params); @@ -102,7 +102,7 @@ int main(int argc, char **argv) { switch (params.model) { case P2108Model::HGTCM: - WriteHGTCInputs(fp, hgtc_params); + WriteHGTCMInputs(fp, hgtcm_params); break; case P2108Model::TSM: WriteTSMInputs(fp, tsm_params); diff --git a/app/src/HeightGainTerminalCorrectionModel.cpp b/app/src/HeightGainTerminalCorrectionModel.cpp index 0e855bf..0b2c07c 100644 --- a/app/src/HeightGainTerminalCorrectionModel.cpp +++ b/app/src/HeightGainTerminalCorrectionModel.cpp @@ -13,21 +13,21 @@ const std::string HGTCMInputKeys::clutter_type = "clutter_type"; /******************************************************************************* * Top-level control function for Height Gain Terminal Correction Model * - * @param[in] hgtc_params Height Gain Terminal Correction Model input struct - * @param[out] A_h__db Additional loss (clutter loss), in dB - * @return Return code + * @param[in] hgtcm_params Height Gain Terminal Correction Model input struct + * @param[out] A_h__db Additional loss (clutter loss), in dB + * @return Return code ******************************************************************************/ ReturnCode CallHeightGainTerminalCorrectionModel( - HGTCMParams &hgtc_params, std::vector &A_h__db + HGTCMParams &hgtcm_params, std::vector &A_h__db ) { ReturnCode rtn; double A_h; rtn = HeightGainTerminalCorrectionModel( - hgtc_params.f__ghz, - hgtc_params.h__meter, - hgtc_params.w_s__meter, - hgtc_params.R__meter, - hgtc_params.clutter_type, + hgtcm_params.f__ghz, + hgtcm_params.h__meter, + hgtcm_params.w_s__meter, + hgtcm_params.R__meter, + hgtcm_params.clutter_type, A_h ); A_h__db.push_back(A_h); @@ -36,33 +36,33 @@ ReturnCode CallHeightGainTerminalCorrectionModel( } /******************************************************************************* - * Parse input stream (file or string stream) to HGTC parameter struct. + * Parse input stream (file or string stream) to HGTCM parameter struct. * - * @param[in] stream Path to ASM input parameter file - * @param[out] hgtc_params HGTC input parameter struct - * @return Return code + * @param[in] stream Path to HGTCM input parameter file + * @param[out] hgtcm_params HGTCM input parameter struct + * @return Return code ******************************************************************************/ DrvrReturnCode - ParseHGTCInputStream(std::istream &stream, HGTCMParams &hgtc_params) { + ParseHGTCMInputStream(std::istream &stream, HGTCMParams &hgtcm_params) { CommaSeparatedIterator it(stream); DrvrReturnCode rtn = DRVR__SUCCESS; std::string key, value; while (it) { std::tie(key, value) = *it; if (key.compare(HGTCMInputKeys::f__ghz) == 0) { - rtn = ParseDouble(value, hgtc_params.f__ghz); + rtn = ParseDouble(value, hgtcm_params.f__ghz); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_FREQ; } else if (key.compare(HGTCMInputKeys::h__meter) == 0) { - rtn = ParseDouble(value, hgtc_params.h__meter); + rtn = ParseDouble(value, hgtcm_params.h__meter); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_HEIGHT; } else if (key.compare(HGTCMInputKeys::w_s__meter) == 0) { - rtn = ParseDouble(value, hgtc_params.w_s__meter); + rtn = ParseDouble(value, hgtcm_params.w_s__meter); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_STREET_WIDTH; } else if (key.compare(HGTCMInputKeys::R__meter) == 0) { - rtn = ParseDouble(value, hgtc_params.R__meter); + rtn = ParseDouble(value, hgtcm_params.R__meter); if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_REPR_HEIGHT; } else if (key.compare(HGTCMInputKeys::clutter_type) == 0) { @@ -71,7 +71,7 @@ DrvrReturnCode if (rtn == DRVRERR__PARSE) { rtn = DRVRERR__PARSE_CLUTTER_TYPE; } else { - hgtc_params.clutter_type + hgtcm_params.clutter_type = static_cast(clutter_type_int); } } else { @@ -90,18 +90,18 @@ DrvrReturnCode /******************************************************************************* * Parse Height Gain Terminal Correction Model input parameter file * - * @param[in] in_file Path to HGTC input parameter file - * @param[out] hgtc_params HGTC input parameter struct - * @return Return code + * @param[in] in_file Path to HGTCM input parameter file + * @param[out] hgtcm_params HGTCM input parameter struct + * @return Return code ******************************************************************************/ DrvrReturnCode - ParseHGTCInputFile(const std::string &in_file, HGTCMParams &hgtc_params) { + ParseHGTCMInputFile(const std::string &in_file, HGTCMParams &hgtcm_params) { std::ifstream file(in_file); if (!file) { std::cerr << "Failed to open file " << in_file << std::endl; return DRVRERR__OPENING_INPUT_FILE; } - return ParseHGTCInputStream(file, hgtc_params); + return ParseHGTCMInputStream(file, hgtcm_params); } /******************************************************************************* @@ -110,7 +110,7 @@ DrvrReturnCode * @param[in] fp Output stream, a text file open for writing * @param[in] params HGTCM input parameter struct ******************************************************************************/ -void WriteHGTCInputs(std::ofstream &fp, const HGTCMParams ¶ms) { +void WriteHGTCMInputs(std::ofstream &fp, const HGTCMParams ¶ms) { fp PRINT HGTCMInputKeys::f__ghz SETW13 params.f__ghz << "(gigahertz)"; fp PRINT HGTCMInputKeys::h__meter SETW13 params.h__meter << "(meters)"; fp PRINT HGTCMInputKeys::w_s__meter SETW13 params.w_s__meter << "(meters)"; From 9d04412732fa962110b2e2ba8ee9eeeaa7aa86ca Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 28 Oct 2024 19:11:28 -0400 Subject: [PATCH 152/379] Add const to PrintLabel --- app/include/Driver.h | 2 +- app/src/DriverUtils.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/include/Driver.h b/app/include/Driver.h index 0c53f3f..d711308 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -60,7 +60,7 @@ void WriteTSMInputs(std::ofstream &fp, const TSMParams ¶ms); // Reporting void PrintClutterTypeLabel(std::ofstream &fp, const ClutterType clutter_type); -void PrintLabel(std::ofstream &fp, std::string &lbl); +void PrintLabel(std::ofstream &fp, const std::string &lbl); // Driver Utils void Version(std::ostream &os = std::cout); diff --git a/app/src/DriverUtils.cpp b/app/src/DriverUtils.cpp index 4d5e685..55b1446 100644 --- a/app/src/DriverUtils.cpp +++ b/app/src/DriverUtils.cpp @@ -111,6 +111,6 @@ void StringToLower(std::string &str) { * @param[in] fp Output stream, a text file open for writing * @param[in] lbl Text message ******************************************************************************/ -void PrintLabel(std::ofstream &fp, std::string &lbl) { +void PrintLabel(std::ofstream &fp, const std::string &lbl) { fp << "[" << lbl << "]"; } From 5517ff115254ef66d630ca71d18e328b3d12c368 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 28 Oct 2024 19:16:33 -0400 Subject: [PATCH 153/379] Add missing libraries for portable tempfile --- app/tests/TempTextFile.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/tests/TempTextFile.cpp b/app/tests/TempTextFile.cpp index ab619ff..a86b319 100644 --- a/app/tests/TempTextFile.cpp +++ b/app/tests/TempTextFile.cpp @@ -11,7 +11,10 @@ #ifndef __STDC_WANT_LIB_EXT1__ #define __STDC_WANT_LIB_EXT1__ 1 #endif - #include // for L_tmpnam_s, tmpnam_s + #include // for L_tmpnam_s, tmpnam_s +#else // macOS and Linux + #include // for mkstemp + #include // for close #endif #include // for std::remove From 04a9ed13a73cc99889a4350f1f6939bd99520942 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 28 Oct 2024 19:23:08 -0400 Subject: [PATCH 154/379] Update Doxygen CI to 1.12.0 --- .github/workflows/ctest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index f5d58f9..ba5dbb2 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -39,7 +39,7 @@ jobs: - name: Install Doxygen uses: ssciwr/doxygen-install@v1 with: - version: "1.11.0" + version: "1.12.0" - name: "CMake: Build and Test" uses: lukka/run-cmake@v10 From e0fdec96d71c0858bb74b851ff15107af27b0599 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 28 Oct 2024 19:31:14 -0400 Subject: [PATCH 155/379] Test cross-platform driver discovery fix --- app/tests/CMakeLists.txt | 5 ++--- app/tests/TestDriver.h | 7 ------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index 52d10be..b745006 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -25,9 +25,8 @@ target_include_directories( # Link the library to the executable target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME}) -# Make driver executable name and location available to source -add_definitions(-DDRIVER_NAME="${DRIVER_NAME}") -add_definitions(-DDRIVER_LOCATION="${DRIVER_EXE_DIR}/${CMAKE_BUILD_TYPE}") +# Make driver executable location available to source +add_definitions(-DDRIVER_LOCATION="$") set_target_properties( ${DRIVER_TEST_NAME} PROPERTIES diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index 44e0ac5..dfc31c9 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -32,13 +32,6 @@ class DriverTest: public ::testing::Test { // Get the name of the executable to test executable = std::string(DRIVER_LOCATION); - executable += "/" + std::string(DRIVER_NAME); -#ifdef _WIN32 - std::replace(executable.begin(), executable.end(), '/', '\\'); - executable += ".exe"; -#else - executable = "./" + executable; -#endif } /*********************************************************************** From 2474309f81e664c0c23efb0ad7fe4e66a8e40e8f Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 28 Oct 2024 19:32:55 -0400 Subject: [PATCH 156/379] Update Doxygen action to 1.12.0 --- .github/workflows/doxygen.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index c7e9635..5e1b83c 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -32,7 +32,7 @@ jobs: - name: Install Doxygen uses: ssciwr/doxygen-install@v1 with: - version: "1.11.0" + version: "1.12.0" - name: Setup GitHub Pages if: ${{ github.event_name == 'release' }} From 43e60f05708ed1c49be09e19a794099cd1e1c09c Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 28 Oct 2024 19:47:28 -0400 Subject: [PATCH 157/379] Doxygen action debug --- .github/workflows/doxygen.yml | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index 5e1b83c..6a60125 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -66,6 +66,24 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 + - name: Check event name + run: echo "Event name: ${{ github.event_name }}"" + - name: Check repository + run: echo "Repository: ${{ github.repository }}" + - name: Debug condition + run: | + echo "Evaluating conditions..." + if [[ "${{ github.event_name }}" == "release" && "${{ github.repository }}" != "NTIA/proplib-template" ]]; then + echo "Conditions are met!" + else + echo "Conditions are NOT met." + fi + + - name: Final debug output + run: | + echo "Event Name: ${{ github.event_name }}" + echo "Repository: ${{ github.repository }}" + echo "Conditions Evaluated: ${{ github.event_name == 'release' && github.repository != 'NTIA/proplib-template' }}" + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 From e8f825707f82bf260e21c6a7d8ae14c928579878 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 29 Oct 2024 13:10:29 -0400 Subject: [PATCH 158/379] Fix typo --- .github/workflows/doxygen.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index 6a60125..57709b1 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -67,7 +67,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check event name - run: echo "Event name: ${{ github.event_name }}"" + run: echo "Event name: ${{ github.event_name }}" - name: Check repository run: echo "Repository: ${{ github.repository }}" - name: Debug condition From fbb10be1c02b3734ccc9dab80a5cbc3d0753edc2 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 29 Oct 2024 13:13:58 -0400 Subject: [PATCH 159/379] try removing erroring lines quotes --- .github/workflows/doxygen.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index 57709b1..e6ff49e 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -67,9 +67,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Check event name - run: echo "Event name: ${{ github.event_name }}" + run: echo ${{ github.event_name }} - name: Check repository - run: echo "Repository: ${{ github.repository }}" + run: echo ${{ github.repository }} - name: Debug condition run: | echo "Evaluating conditions..." From 8e1af2a5054fac0dcfb0d82007bf1017ce2f4dd9 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 29 Oct 2024 13:18:13 -0400 Subject: [PATCH 160/379] Fix doxygen action YAML --- .github/workflows/doxygen.yml | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index e6ff49e..af49c9d 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -18,7 +18,7 @@ concurrency: jobs: build: - if: github.repository != 'NTIA/proplib-template' + if: ${{ github.repository != 'NTIA/proplib-template' }} runs-on: ubuntu-latest steps: - name: Checkout repository @@ -55,7 +55,7 @@ jobs: path: ./docs/html/ deploy: - if: ${{ github.event_name == 'release'}} && github.repository != 'NTIA/proplib-template' + if: ${{ github.event_name == 'release'}} && ${{ github.repository != 'NTIA/proplib-template' }} needs: build permissions: contents: read @@ -66,24 +66,6 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest steps: - - name: Check event name - run: echo ${{ github.event_name }} - - name: Check repository - run: echo ${{ github.repository }} - - name: Debug condition - run: | - echo "Evaluating conditions..." - if [[ "${{ github.event_name }}" == "release" && "${{ github.repository }}" != "NTIA/proplib-template" ]]; then - echo "Conditions are met!" - else - echo "Conditions are NOT met." - fi - - - name: Final debug output - run: | - echo "Event Name: ${{ github.event_name }}" - echo "Repository: ${{ github.repository }}" - echo "Conditions Evaluated: ${{ github.event_name == 'release' && github.repository != 'NTIA/proplib-template' }}" - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 From 8e0908d45492df4ca9b1dd26fc61c69b0ce9e125 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 29 Oct 2024 13:20:25 -0400 Subject: [PATCH 161/379] fix combined condition --- .github/workflows/doxygen.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index af49c9d..9a2f311 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -55,7 +55,7 @@ jobs: path: ./docs/html/ deploy: - if: ${{ github.event_name == 'release'}} && ${{ github.repository != 'NTIA/proplib-template' }} + if: ${{ (github.event_name == 'release') && (github.repository != 'NTIA/proplib-template') }} needs: build permissions: contents: read From daf331592f26203dd0d3b56faa1c6d803ab317a8 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 29 Oct 2024 13:22:43 -0400 Subject: [PATCH 162/379] Fix conditions for CI jobs, update Doxygen to 1.12 --- .github/workflows/ctest.yml | 4 ++-- .github/workflows/doxygen.yml | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index f5d58f9..6987805 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -12,7 +12,7 @@ on: # Define the matrix for different operating systems jobs: build-and-test: - if: github.repository != 'NTIA/proplib-template' + if: ${{ github.repository != 'NTIA/proplib-template' }} name: ${{ matrix.os }} / CMake ${{ matrix.cmakeVersion }} runs-on: ${{ matrix.os }} strategy: @@ -39,7 +39,7 @@ jobs: - name: Install Doxygen uses: ssciwr/doxygen-install@v1 with: - version: "1.11.0" + version: "1.12.0" - name: "CMake: Build and Test" uses: lukka/run-cmake@v10 diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index c7e9635..9a2f311 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -18,7 +18,7 @@ concurrency: jobs: build: - if: github.repository != 'NTIA/proplib-template' + if: ${{ github.repository != 'NTIA/proplib-template' }} runs-on: ubuntu-latest steps: - name: Checkout repository @@ -32,7 +32,7 @@ jobs: - name: Install Doxygen uses: ssciwr/doxygen-install@v1 with: - version: "1.11.0" + version: "1.12.0" - name: Setup GitHub Pages if: ${{ github.event_name == 'release' }} @@ -55,7 +55,7 @@ jobs: path: ./docs/html/ deploy: - if: ${{ github.event_name == 'release'}} && github.repository != 'NTIA/proplib-template' + if: ${{ (github.event_name == 'release') && (github.repository != 'NTIA/proplib-template') }} needs: build permissions: contents: read @@ -66,6 +66,6 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 From 7e9a36d2e05b3477264acf4efb56d7a9fc89b3bb Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 30 Oct 2024 17:31:41 -0400 Subject: [PATCH 163/379] Update ctest.yml --- .github/workflows/ctest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index ba5dbb2..6987805 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -12,7 +12,7 @@ on: # Define the matrix for different operating systems jobs: build-and-test: - if: github.repository != 'NTIA/proplib-template' + if: ${{ github.repository != 'NTIA/proplib-template' }} name: ${{ matrix.os }} / CMake ${{ matrix.cmakeVersion }} runs-on: ${{ matrix.os }} strategy: From ae178ae8a7249f49f9a06b40c309c03e51d9eba1 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 30 Oct 2024 17:31:57 -0400 Subject: [PATCH 164/379] cleanup --- app/include/Driver.h | 8 ++++---- app/include/ReturnCodes.h | 7 +++---- app/src/ReturnCodes.cpp | 2 ++ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/include/Driver.h b/app/include/Driver.h index d711308..9b06be0 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -30,8 +30,8 @@ using namespace ITS::ITU::PSeries::P2108; ///////////////////////////// // Functions -DrvrReturnCode ParseArguments(int argc, char **argv, DrvrParams ¶ms); void Help(std::ostream &os = std::cout); +DrvrReturnCode ParseArguments(int argc, char **argv, DrvrParams ¶ms); DrvrReturnCode ValidateInputs(const DrvrParams ¶ms); // Aeronautical Statistical Model @@ -63,8 +63,8 @@ void PrintClutterTypeLabel(std::ofstream &fp, const ClutterType clutter_type); void PrintLabel(std::ofstream &fp, const std::string &lbl); // Driver Utils -void Version(std::ostream &os = std::cout); -DrvrReturnCode ParseInteger(const std::string &str, int &value); -DrvrReturnCode ParseDouble(const std::string &str, double &value); std::string GetDatetimeString(); +DrvrReturnCode ParseDouble(const std::string &str, double &value); +DrvrReturnCode ParseInteger(const std::string &str, int &value); void StringToLower(std::string &str); +void Version(std::ostream &os = std::cout); \ No newline at end of file diff --git a/app/include/ReturnCodes.h b/app/include/ReturnCodes.h index c4473ac..f56893f 100644 --- a/app/include/ReturnCodes.h +++ b/app/include/ReturnCodes.h @@ -3,15 +3,14 @@ */ #pragma once -#include -#include +#include // for std::string /******************************************************************************* * Return Codes defined by this driver software. ******************************************************************************/ // clang-format off enum DrvrReturnCode { - // Primary Return Codes + // Primary Return Codes (1000-1099) DRVR__SUCCESS = 1000, /**< Successful execution */ DRVR__RETURN_SUCCESS, /**< Indicates driver should exit successfully */ DRVRERR__MISSING_OPTION, /**< No value provided for given argument */ @@ -33,7 +32,7 @@ enum DrvrReturnCode { // Validation Errors (1200-1299) DRVRERR__VALIDATION_IN_FILE = 1200, /**< Input file not specified */ DRVRERR__VALIDATION_OUT_FILE, /**< Output file not specified */ - DRVRERR__VALIDATION_MODEL /**< Model not specified */ + DRVRERR__VALIDATION_MODEL, /**< Model not specified */ }; // clang-format on diff --git a/app/src/ReturnCodes.cpp b/app/src/ReturnCodes.cpp index 041790d..4d08a69 100644 --- a/app/src/ReturnCodes.cpp +++ b/app/src/ReturnCodes.cpp @@ -4,6 +4,8 @@ #include "ReturnCodes.h" +#include + /******************************************************************************* * Get an error message string from a return code. * From d315e2b1541f32337f225d9235ced84ea06f83aa Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 30 Oct 2024 17:36:51 -0400 Subject: [PATCH 165/379] Change newline handling in driver status message --- app/src/AeronauticalStatisticalModel.cpp | 2 +- app/src/Driver.cpp | 4 ++-- app/src/HeightGainTerminalCorrectionModel.cpp | 2 +- app/src/ReturnCodes.cpp | 4 ++-- app/src/TerrestrialStatisticalModel.cpp | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/AeronauticalStatisticalModel.cpp b/app/src/AeronauticalStatisticalModel.cpp index d2ad323..3fbebd3 100644 --- a/app/src/AeronauticalStatisticalModel.cpp +++ b/app/src/AeronauticalStatisticalModel.cpp @@ -59,7 +59,7 @@ DrvrReturnCode } if (rtn != DRVR__SUCCESS) { - std::cerr << GetDrvrReturnStatus(rtn); + std::cerr << GetDrvrReturnStatus(rtn) << std::endl; return rtn; } ++it; diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index 860a9da..0b1bf0c 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -66,7 +66,7 @@ int main(int argc, char **argv) { // Return driver error code if one was returned if (rtn > DRVR__RETURN_SUCCESS) { - std::cerr << GetDrvrReturnStatus(rtn); + std::cerr << GetDrvrReturnStatus(rtn) << std::endl; return rtn; } @@ -233,7 +233,7 @@ DrvrReturnCode ValidateInputs(const DrvrParams ¶ms) { rtn = DRVRERR__VALIDATION_MODEL; if (rtn != DRVR__SUCCESS) - std::cerr << GetDrvrReturnStatus(rtn); + std::cerr << GetDrvrReturnStatus(rtn) << std::endl; return rtn; } \ No newline at end of file diff --git a/app/src/HeightGainTerminalCorrectionModel.cpp b/app/src/HeightGainTerminalCorrectionModel.cpp index 0b2c07c..a72bc50 100644 --- a/app/src/HeightGainTerminalCorrectionModel.cpp +++ b/app/src/HeightGainTerminalCorrectionModel.cpp @@ -79,7 +79,7 @@ DrvrReturnCode } if (rtn != DRVR__SUCCESS) { - std::cerr << GetDrvrReturnStatus(rtn); + std::cerr << GetDrvrReturnStatus(rtn) << std::endl; return rtn; } ++it; diff --git a/app/src/ReturnCodes.cpp b/app/src/ReturnCodes.cpp index 4d08a69..9de5072 100644 --- a/app/src/ReturnCodes.cpp +++ b/app/src/ReturnCodes.cpp @@ -49,9 +49,9 @@ std::string GetDrvrReturnStatus(int code) { auto it = messages.find(static_cast(code)); if (it != messages.end()) { - msg += it->second + "\n"; + msg += it->second; } else { - msg += "Undefined return code\n"; + msg += "Undefined return code"; } return msg; } diff --git a/app/src/TerrestrialStatisticalModel.cpp b/app/src/TerrestrialStatisticalModel.cpp index 7947eee..f9aa631 100644 --- a/app/src/TerrestrialStatisticalModel.cpp +++ b/app/src/TerrestrialStatisticalModel.cpp @@ -59,7 +59,7 @@ DrvrReturnCode } if (rtn != DRVR__SUCCESS) { - std::cerr << GetDrvrReturnStatus(rtn); + std::cerr << GetDrvrReturnStatus(rtn) << std::endl; return rtn; } ++it; From 3a2e7af2cd90c7065705116caaefd4c16f40bad7 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 30 Oct 2024 17:37:43 -0400 Subject: [PATCH 166/379] Do not create newline in status messages --- src/ReturnCodes.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ReturnCodes.cpp b/src/ReturnCodes.cpp index c0b7645..4f82c91 100644 --- a/src/ReturnCodes.cpp +++ b/src/ReturnCodes.cpp @@ -40,9 +40,9 @@ std::string GetReturnStatus(int code) { auto it = messages.find(static_cast(code)); if (it != messages.end()) { - msg += it->second + "\n"; + msg += it->second; } else { - msg += "Undefined return code\n"; + msg += "Undefined return code"; } return msg; } From ef14e43aa4cc539df172da5617e4e4aeb34b1eed Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 30 Oct 2024 17:46:14 -0400 Subject: [PATCH 167/379] Shift all return codes into POSIX-compliant range --- app/include/ReturnCodes.h | 14 ++++----- app/tests/TestDriver.cpp | 12 ++++---- app/tests/TestDriver.h | 29 +++++++++++++++++-- include/ITS.ITU.PSeries.P2108/ReturnCodes.h | 8 ++--- .../AeronauticalStatisticalModelTestData.csv | 12 ++++---- ...ghtGainTerminalCorrectionModelTestData.csv | 10 +++---- .../TerrestrialStatisticalModelTestData.csv | 10 +++---- 7 files changed, 59 insertions(+), 36 deletions(-) diff --git a/app/include/ReturnCodes.h b/app/include/ReturnCodes.h index f56893f..c70c48f 100644 --- a/app/include/ReturnCodes.h +++ b/app/include/ReturnCodes.h @@ -6,20 +6,20 @@ #include // for std::string /******************************************************************************* - * Return Codes defined by this driver software. + * Return Codes defined by this driver software (128-255) ******************************************************************************/ // clang-format off enum DrvrReturnCode { - // Primary Return Codes (1000-1099) - DRVR__SUCCESS = 1000, /**< Successful execution */ + // Primary Return Codes + DRVR__SUCCESS = 128, /**< Successful execution */ DRVR__RETURN_SUCCESS, /**< Indicates driver should exit successfully */ DRVRERR__MISSING_OPTION, /**< No value provided for given argument */ DRVRERR__INVALID_OPTION, /**< Unknown option specified */ DRVRERR__OPENING_INPUT_FILE, /**< Failed to open the input file for reading */ DRVRERR__OPENING_OUTPUT_FILE, /**< Failed to open the output file for writing */ - // Input File Parsing Errors (1100-1199) - DRVRERR__PARSE = 1100, /**< Failed parsing inputs; unknown parameter */ + // Input File Parsing Errors + DRVRERR__PARSE = 160, /**< Failed parsing inputs; unknown parameter */ DRVRERR__PARSE_FREQ, /**< Failed to parse frequency value */ DRVRERR__PARSE_THETA, /**< Failed to parse theta value */ DRVRERR__PARSE_PERCENTAGE, /**< Failed to parse percentage value */ @@ -29,8 +29,8 @@ enum DrvrReturnCode { DRVRERR__PARSE_CLUTTER_TYPE, /**< Failed to parse clutter type value */ DRVRERR__PARSE_PATH_DIST, /**< Failed to parse path distance value */ - // Validation Errors (1200-1299) - DRVRERR__VALIDATION_IN_FILE = 1200, /**< Input file not specified */ + // Validation Errors + DRVRERR__VALIDATION_IN_FILE = 192, /**< Input file not specified */ DRVRERR__VALIDATION_OUT_FILE, /**< Output file not specified */ DRVRERR__VALIDATION_MODEL, /**< Model not specified */ }; diff --git a/app/tests/TestDriver.cpp b/app/tests/TestDriver.cpp index 1337943..d98204d 100644 --- a/app/tests/TestDriver.cpp +++ b/app/tests/TestDriver.cpp @@ -7,7 +7,7 @@ TEST_F(DriverTest, MissingOptionError1) { // Test case: missing option between two provided flags std::string cmd = executable + " -i -o out.txt"; SuppressOutputs(cmd); - int rtn = std::system(cmd.c_str()); + int rtn = RunCommand(cmd); EXPECT_EQ(rtn, DRVRERR__MISSING_OPTION); } @@ -15,14 +15,14 @@ TEST_F(DriverTest, MissingOptionError2) { // Test case: missing option at the end of command std::string cmd = executable + " -i"; SuppressOutputs(cmd); - int rtn = std::system(cmd.c_str()); + int rtn = RunCommand(cmd); EXPECT_EQ(rtn, DRVRERR__MISSING_OPTION); } TEST_F(DriverTest, InvalidOptionError) { std::string cmd = executable + " -X"; SuppressOutputs(cmd); - int rtn = std::system(cmd.c_str()); + int rtn = RunCommand(cmd); EXPECT_EQ(rtn, DRVRERR__INVALID_OPTION); } @@ -45,7 +45,7 @@ TEST_F(DriverTest, OpeningOutputFileError) { TEST_F(DriverTest, ValidationInFileError) { std::string cmd = executable + " -o out.txt -model ASM"; SuppressOutputs(cmd); - int rtn = std::system(cmd.c_str()); + int rtn = RunCommand(cmd); EXPECT_EQ(rtn, DRVRERR__VALIDATION_IN_FILE); } @@ -53,7 +53,7 @@ TEST_F(DriverTest, ValidationOutFileError) { // Input file does not need to exist here, just has to be specified std::string cmd = executable + " -i in.txt -model ASM"; SuppressOutputs(cmd); - int rtn = std::system(cmd.c_str()); + int rtn = RunCommand(cmd); EXPECT_EQ(rtn, DRVRERR__VALIDATION_OUT_FILE); } @@ -61,6 +61,6 @@ TEST_F(DriverTest, ValidationModelError) { // Input file does not need to exist here, just has to be specified std::string cmd = executable + " -i in.txt -o out.txt"; SuppressOutputs(cmd); - int rtn = std::system(cmd.c_str()); + int rtn = RunCommand(cmd); EXPECT_EQ(rtn, DRVRERR__VALIDATION_MODEL); } diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index dfc31c9..ed2f395 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -10,9 +10,13 @@ #include // for std::remove #include // for std::system #include // GoogleTest -#include // for std::cout, std::endl +#include // for std::cout, std::endl, std::flush #include // for std::string +#ifndef _WIN32 + #include // for WEXITSTATUS +#endif + /******************************************************************************* * @class DriverTest @@ -90,6 +94,26 @@ class DriverTest: public ::testing::Test { return command; } + /*********************************************************************** + * Runs the provided command (cross-platform) + * + * Note that on POSIX platforms the exit code of the command should be + * between 0 and 255. Exit codes outside this range will be shifted into + * this range and cannot be unambiguously compared to expectations. + * + * @param[in] cmd The command to run + * @return The exit code of the command. + **********************************************************************/ + int RunCommand(const std::string &cmd) { + std::cout << std::flush; + int rtn = std::system(cmd.c_str()); +#ifndef _WIN32 + rtn = WEXITSTATUS(rtn); // Get child process exit code on POSIX +#endif + return rtn; + } + + /*********************************************************************** * Runs the driver executable. * @@ -98,8 +122,7 @@ class DriverTest: public ::testing::Test { **********************************************************************/ int RunDriver(const DrvrParams ¶ms) { std::string cmd = BuildCommand(params); - int rtn = std::system(cmd.c_str()); - return rtn; + return RunCommand(cmd); } /*********************************************************************** diff --git a/include/ITS.ITU.PSeries.P2108/ReturnCodes.h b/include/ITS.ITU.PSeries.P2108/ReturnCodes.h index 9cf6567..c575593 100644 --- a/include/ITS.ITU.PSeries.P2108/ReturnCodes.h +++ b/include/ITS.ITU.PSeries.P2108/ReturnCodes.h @@ -12,26 +12,26 @@ namespace PSeries { namespace P2108 { /******************************************************************************* - * Return Codes defined by this software. + * Return Codes defined by this software (0-127) ******************************************************************************/ // clang-format off enum ReturnCode { SUCCESS = 0, /**< Successful execution */ // Section 3.1 Error Codes - ERROR31__FREQUENCY = 3100, /**< Frequency must be between 0.3 and 3 GHz */ + ERROR31__FREQUENCY = 32, /**< Frequency must be between 0.3 and 3 GHz */ ERROR31__ANTENNA_HEIGHT, /**< Antenna height must be @f$ \geq @f$ 0 meters */ ERROR31__STREET_WIDTH, /**< Street width must be @f$ > @f$ 0 meters */ ERROR31__CLUTTER_HEIGHT, /**< Representative clutter height must be @f$ > @f$ 0 meters */ ERROR31__CLUTTER_TYPE, /**< Invalid value for clutter type */ // Section 3.2 Error Codes - ERROR32__FREQUENCY = 3200, /**< Frequency must be between 2 and 67 GHz */ + ERROR32__FREQUENCY = 48, /**< Frequency must be between 2 and 67 GHz */ ERROR32__DISTANCE, /**< Path distance must be @f$ \geq @f$ 0.25 km */ ERROR32__PERCENTAGE, /**< Percentage must be between 0 and 100 */ // Section 3.3 Error Codes - ERROR33__FREQUENCY = 3300, /**< Frequency must be between 10 and 100 GHz */ + ERROR33__FREQUENCY = 64, /**< Frequency must be between 10 and 100 GHz */ ERROR33__THETA, /**< Elevation angle must be between 0 and 100 GHz */ ERROR33__PERCENTAGE, /**< Percentage must be between 0 and 100 */ }; diff --git a/tests/data/AeronauticalStatisticalModelTestData.csv b/tests/data/AeronauticalStatisticalModelTestData.csv index cdcad8f..9ed7120 100644 --- a/tests/data/AeronauticalStatisticalModelTestData.csv +++ b/tests/data/AeronauticalStatisticalModelTestData.csv @@ -6,9 +6,9 @@ f__ghz,theta_deg,p,rtn,L_ces__db 15,90,50,0,0 20,0,50,0,45.6 11.1,15.5,80.5,0,14.7 -9.9,45,45,3300,0 -100.1,45,45,3300,0 -18,-0.1,50,3301,0 -18,90.1,50,3301,0 -22,25,0,3302,0 -22,25,100,3302,0 +9.9,45,45,64,0 +100.1,45,45,64,0 +18,-0.1,50,65,0 +18,90.1,50,65,0 +22,25,0,66,0 +22,25,100,66,0 diff --git a/tests/data/HeightGainTerminalCorrectionModelTestData.csv b/tests/data/HeightGainTerminalCorrectionModelTestData.csv index c26e648..aa47f89 100644 --- a/tests/data/HeightGainTerminalCorrectionModelTestData.csv +++ b/tests/data/HeightGainTerminalCorrectionModelTestData.csv @@ -17,8 +17,8 @@ f__ghz,h__meter,w_s__meter,R__meter,clutter_type,rtn,A_h__db 0.03,2.1,24.5,9.8,3,0,5.7 1.7,30,24.5,9.8,3,0,0 1.7,30,24.5,30,3,0,0 -0.02,2,27,10,3,3100,0 -4,2,27,10,3,3100,0 -1,0,10,9,2,3101,0 -2,1,0,9,6,3102,0 -2,1,27,0,6,3103,0 \ No newline at end of file +0.02,2,27,10,3,32,0 +4,2,27,10,3,32,0 +1,0,10,9,2,33,0 +2,1,0,9,6,34,0 +2,1,27,0,6,35,0 \ No newline at end of file diff --git a/tests/data/TerrestrialStatisticalModelTestData.csv b/tests/data/TerrestrialStatisticalModelTestData.csv index 589034c..5ccadc8 100644 --- a/tests/data/TerrestrialStatisticalModelTestData.csv +++ b/tests/data/TerrestrialStatisticalModelTestData.csv @@ -6,8 +6,8 @@ f__ghz,d__km,p,rtn,L_ctt__db 67,5.4,30.5,0,30.9 3.5,1,0.1,0,16.8 3.5,1,99.9,0,42.8 -0.24,2,50,3200,0 -67.1,5,50,3200,0 -10,0.24,50,3201,0 -6,3,0,3202,0 -6,3,100,3202,0 +0.24,2,50,48,0 +67.1,5,50,48,0 +10,0.24,50,49,0 +6,3,0,50,0 +6,3,100,50,0 From 6901be62a8e3372d8367c23c5efcf63ba867bf72 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 30 Oct 2024 18:06:42 -0400 Subject: [PATCH 168/379] Try fixing googletest discovery --- CMakeLists.txt | 11 ++++++++--- app/tests/CMakeLists.txt | 12 +++--------- app/tests/TestDriver.h | 7 +++++++ tests/CMakeLists.txt | 4 ---- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 21979a0..39da6f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,17 +51,22 @@ if (NOT DOCS_ONLY) if (COPY_TO_WRAPPERS) # Copy compiled library to wrappers add_subdirectory(wrap) endif () - if (RUN_TESTS) # Build and run unit tests + if (RUN_TESTS OR RUN_DRIVER_TESTS) if (EXISTS "${PROJECT_SOURCE_DIR}/extern/googletest/CMakeLists.txt") enable_testing() - add_subdirectory(tests) + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + add_subdirectory("${PROJECT_SOURCE_DIR}/extern/googletest" "extern/googletest") + include(GoogleTest) 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 () + if (RUN_TESTS) # Build and run unit tests + add_subdirectory(tests) endif () if (BUILD_DRIVER) add_subdirectory(app) diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index b745006..549ab86 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -26,7 +26,8 @@ target_include_directories( target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME}) # Make driver executable location available to source -add_definitions(-DDRIVER_LOCATION="$") +add_definitions(-DDRIVER_NAME="${DRIVER_NAME}") +add_definitions(-DDRIVER_LOCATION="${DRIVER_EXE_DIR}/${CMAKE_BUILD_TYPE}") set_target_properties( ${DRIVER_TEST_NAME} PROPERTIES @@ -38,13 +39,6 @@ set_target_properties( ########################################### ## SET UP AND DISCOVER TESTS ########################################### -if (NOT ${GOOGLETEST_ADDED}) - 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(GOOGLETEST_ADDED ON) -endif () - +include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME} GTest::gtest_main) gtest_discover_tests(${DRIVER_TEST_NAME}) diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index ed2f395..064b45a 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -36,6 +36,13 @@ class DriverTest: public ::testing::Test { // Get the name of the executable to test executable = std::string(DRIVER_LOCATION); + executable += "/" + std::string(DRIVER_NAME); +#ifdef _WIN32 + std::replace(executable.begin(), executable.end(), '/', '\\'); + executable += ".exe"; +#else + executable = "./" + executable; +#endif } /*********************************************************************** diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ad99839..78666f5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -24,10 +24,6 @@ set_target_properties( ########################################### ## 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(GOOGLETEST_ADDED ON) target_link_libraries(${TEST_NAME} ${LIB_NAME} GTest::gtest_main) gtest_discover_tests(${TEST_NAME}) \ No newline at end of file From 01e0d43bc01cb19d239e76a74a5e14affbc2417b Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 30 Oct 2024 18:15:42 -0400 Subject: [PATCH 169/379] Test reverting storage of target location --- app/tests/CMakeLists.txt | 3 +-- app/tests/TestDriver.h | 7 ------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index 549ab86..ff54645 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -26,8 +26,7 @@ target_include_directories( target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME}) # Make driver executable location available to source -add_definitions(-DDRIVER_NAME="${DRIVER_NAME}") -add_definitions(-DDRIVER_LOCATION="${DRIVER_EXE_DIR}/${CMAKE_BUILD_TYPE}") +add_definitions(-DDRIVER_LOCATION="$") set_target_properties( ${DRIVER_TEST_NAME} PROPERTIES diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index 064b45a..ed2f395 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -36,13 +36,6 @@ class DriverTest: public ::testing::Test { // Get the name of the executable to test executable = std::string(DRIVER_LOCATION); - executable += "/" + std::string(DRIVER_NAME); -#ifdef _WIN32 - std::replace(executable.begin(), executable.end(), '/', '\\'); - executable += ".exe"; -#else - executable = "./" + executable; -#endif } /*********************************************************************** From 8eef9d41186fd9646714b324215ed33afd0a9299 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 30 Oct 2024 20:43:51 -0400 Subject: [PATCH 170/379] Return code updates --- app/include/Driver.h | 14 ++++++-------- app/include/Errors.h | 32 -------------------------------- app/include/ReturnCodes.h | 32 ++++++++++++++++++++++++++++++++ app/src/CMakeLists.txt | 3 ++- app/src/Driver.cpp | 6 +++--- 5 files changed, 43 insertions(+), 44 deletions(-) delete mode 100644 app/include/Errors.h create mode 100644 app/include/ReturnCodes.h diff --git a/app/include/Driver.h b/app/include/Driver.h index ed91de2..5baec0e 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -4,7 +4,7 @@ #pragma once #include "CommaSeparatedIterator.h" -#include "Errors.h" +#include "ReturnCodes.h" #include "Structs.h" // TODO-TEMPLATE: Include your library's main interface header @@ -33,15 +33,13 @@ ///////////////////////////// // Functions -int ParseArguments(int argc, char **argv, DrvrParams ¶ms); void Help(std::ostream &os = std::cout); -int ValidateInputs(const DrvrParams ¶ms); +DrvrReturnCode ParseArguments(int argc, char **argv, DrvrParams ¶ms); +DrvrReturnCode ValidateInputs(const DrvrParams ¶ms); // Driver Utils -void Version(std::ostream &os = std::cout); -int Validate_RequiredErrMsgHelper(const std::string &opt, const int err); -int ParseInteger(const std::string &str, int &value); -int ParseDouble(const std::string &str, double &value); -int ParsingErrorHelper(const int err, const std::string &msg); std::string GetDatetimeString(); +DrvrReturnCode ParseDouble(const std::string &str, double &value); +DrvrReturnCode ParseInteger(const std::string &str, int &value); void StringToLower(std::string &str); +void Version(std::ostream &os = std::cout); \ No newline at end of file diff --git a/app/include/Errors.h b/app/include/Errors.h deleted file mode 100644 index cdbe0a8..0000000 --- a/app/include/Errors.h +++ /dev/null @@ -1,32 +0,0 @@ -/** @file Errors.h - * Defines return codes for the driver - */ -#pragma once - -// TODO-TEMPLATE: If needed, include existing return codes defined by the library -// #include "/Errors.h" - -// clang-format off - -// Define "SUCCESS" macro if not imported already -#ifndef SUCCESS -#define SUCCESS 0 /**< Successful execution */ -#endif - -// Primary Return Codes (1000-1099) -#define DRVR__RETURN_SUCCESS 1000 /**< Internal driver success code */ -#define DRVRERR__MISSING_OPTION 1001 /**< No value provided for given argument */ -#define DRVRERR__INVALID_OPTION 1002 /**< Unknown option specified */ -#define DRVRERR__OPENING_INPUT_FILE 1003 /**< Failed to open the input file for reading */ -#define DRVRERR__OPENING_OUTPUT_FILE 1004 /**< Failed to open the output file for writing */ - -// Input File Parsing Errors (1100-1199) -#define DRVRERR__PARSE 1100 /**< General error parsing inputs */ - -// TODO-TEMPLATE: Add driver error codes and document them in app/README.md - -// Validation Errors (1200-1299) -#define DRVRERR__VALIDATION_IN_FILE 1200 /**< Input file not specified */ -#define DRVRERR__VALIDATION_OUT_FILE 1201 /**< Output file not specified */ - -// clang-format on \ No newline at end of file diff --git a/app/include/ReturnCodes.h b/app/include/ReturnCodes.h new file mode 100644 index 0000000..16eed51 --- /dev/null +++ b/app/include/ReturnCodes.h @@ -0,0 +1,32 @@ +/** @file Errors.h + * Defines return codes for the driver + */ +#pragma once + +#include // for std::string + +// TODO-TEMPLATE: Add driver return codes here and corresponding entries in app/src/ReturnCodes.cpp + +/******************************************************************************* + * Return Codes defined by this driver software. + ******************************************************************************/ +// clang-format off +enum DrvrReturnCode { + // Primary Return Codes (1000-1099) + DRVR__SUCCESS = 1000, /**< Successful execution */ + DRVR__RETURN_SUCCESS, /**< Indicates driver should exit successfully */ + DRVRERR__MISSING_OPTION, /**< No value provided for given argument */ + DRVRERR__INVALID_OPTION, /**< Unknown option specified */ + DRVRERR__OPENING_INPUT_FILE, /**< Failed to open the input file for reading */ + DRVRERR__OPENING_OUTPUT_FILE, /**< Failed to open the output file for writing */ + + // Input File Parsing Errors (1100-1199) + DRVRERR__PARSE = 1100, /**< Failed parsing inputs; unknown parameter */ + + // Validation Errors (1200-1299) + DRVRERR__VALIDATION_IN_FILE = 1200, /**< Input file not specified */ + DRVRERR__VALIDATION_OUT_FILE, /**< Output file not specified */ +}; +// clang-format on + +std::string GetDrvrReturnStatus(int code); diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index d41276d..d3ec79a 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -8,9 +8,10 @@ add_executable( "CommaSeparatedIterator.cpp" "Driver.cpp" "DriverUtils.cpp" + "ReturnCodes.cpp" "${DRIVER_HEADERS}/CommaSeparatedIterator.h" "${DRIVER_HEADERS}/Driver.h" - "${DRIVER_HEADERS}/Errors.h" + "${DRIVER_HEADERS}/ReturnCodes.h" "${DRIVER_HEADERS}/Structs.h" ) diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index 2952553..64e9754 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -16,16 +16,16 @@ int main(int argc, char **argv) { // Parse command line arguments rtn = ParseArguments(argc, argv, params); - if (rtn == DRVR__RETURN_SUCCESS) + if (rtn == DRVR__RETURN_SUCCESS) { return SUCCESS; - if (rtn != SUCCESS) { + } else if (rtn != DRVR__SUCCESS) { Help(); return rtn; } // Ensure required options were provided rtn = ValidateInputs(params); - if (rtn != SUCCESS) { + if (rtn != DRVR__SUCCESS) { Help(); return rtn; } From abd8c43a1c1587d0441008219b23cbf40fdf163c Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 30 Oct 2024 22:18:47 -0400 Subject: [PATCH 171/379] template updates --- CMakeLists.txt | 13 ++- app/README.md | 33 +----- app/data/README.md | 5 + app/include/CommaSeparatedIterator.h | 4 +- app/include/Driver.h | 11 +- app/include/ReturnCodes.h | 14 +-- app/include/Structs.h | 3 +- app/src/CMakeLists.txt | 6 +- app/src/CommaSeparatedIterator.cpp | 11 +- app/src/DriverUtils.cpp | 133 +++++++++++++----------- app/src/ReturnCodes.cpp | 48 +++++++++ app/tests/CMakeLists.txt | 20 ++-- app/tests/TempTextFile.cpp | 6 +- app/tests/TestDriver.cpp | 10 +- app/tests/TestDriver.h | 15 ++- include/ITS.TODO-TEMPLATE/ReturnCodes.h | 26 +++++ src/CMakeLists.txt | 6 +- src/ReturnCodes.cpp | 41 ++++++++ tests/CMakeLists.txt | 8 -- tests/TestUtils.cpp | 22 ++++ tests/TestUtils.h | 5 + tests/data/README.md | 4 + 22 files changed, 291 insertions(+), 153 deletions(-) create mode 100644 app/data/README.md create mode 100644 app/src/ReturnCodes.cpp create mode 100644 include/ITS.TODO-TEMPLATE/ReturnCodes.h create mode 100644 src/ReturnCodes.cpp create mode 100644 tests/TestUtils.cpp create mode 100644 tests/TestUtils.h create mode 100644 tests/data/README.md diff --git a/CMakeLists.txt b/CMakeLists.txt index 8bb5736..d038808 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,19 +52,24 @@ if (NOT DOCS_ONLY) if (COPY_TO_WRAPPERS) # Copy compiled library to wrappers add_subdirectory(wrap) endif () - if (RUN_TESTS) # Build and run unit tests + if (RUN_TESTS OR RUN_DRIVER_TESTS) if (EXISTS "${PROJECT_SOURCE_DIR}/extern/googletest/CMakeLists.txt") enable_testing() - add_subdirectory(tests) + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + add_subdirectory("${PROJECT_SOURCE_DIR}/extern/googletest" "extern/googletest") + include(GoogleTest) 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 () + if (RUN_TESTS) # Build and run unit tests + add_subdirectory(tests) endif () - if (BUILD_DRIVER) + if (BUILD_DRIVER OR RUN_DRIVER_TESTS) add_subdirectory(app) endif () endif () diff --git a/app/README.md b/app/README.md index 66bc179..a8ec020 100644 --- a/app/README.md +++ b/app/README.md @@ -58,34 +58,7 @@ to the provided corresponding output files. ## Command-line Driver Errors ## -(TODO-TEMPLATE: Update the below tables with your driver errors. Align with app/Errors.h) - In addition to the return codes defined by the library itself, the command-line -driver implements the following return codes. - -### General Errors ### - -| Value | Const Name | Description | -|-------|--------------------------------|--------------------------------------------| -| 1000 | `DRVR__RETURN_SUCCESS` | Internal driver success code | -| 1001 | `DRVRERR__MISSING_OPTION` | No value provided for given argument | -| 1002 | `DRVRERR__INVALID_OPTION` | Unknown option specified | -| 1003 | `DRVRERR__OPENING_INPUT_FILE` | Failed to open the input file for reading | -| 1004 | `DRVRERR__OPENING_OUTPUT_FILE` | Failed to open the output file for writing | - -### Input File Parsing Errors ### - -| Value | Const Name | Description | -|-------|-------------------------------|---------------------------------------| -| 1100 | `DRVRERR__PARSE` | General input file parsing error | - -### Validation Errors ### - -Driver validation errors occur when required command line arguments are missing. -These validation errors are distinct from any defined within the library itself, -which may include, e.g., parameter out-of-range errors. - -| Value | Const Name | Description | -|-------|------------------------------------|----------------------------------| -| 1200 | `DRVRERR__VALIDATION_IN_FILE` | Input file not specified | -| 1201 | `DRVRERR__VALIDATION_OUT_FILE` | Output file not specified | +driver implements its own set of return codes in [`app/include/ReturnCodes.h`](./include/ReturnCodes.h). +A helper function to map human-readable status messages to these codes in +[`app/src/ReturnCodes.cpp`](./src/ReturnCodes.cpp). diff --git a/app/data/README.md b/app/data/README.md new file mode 100644 index 0000000..329f20e --- /dev/null +++ b/app/data/README.md @@ -0,0 +1,5 @@ +# TODO-TEMPLATE + +Populate this folder with example input and output files for +use with the command-line driver. After populating this folder, +delete this README file. diff --git a/app/include/CommaSeparatedIterator.h b/app/include/CommaSeparatedIterator.h index 2e7b646..cc0b0a7 100644 --- a/app/include/CommaSeparatedIterator.h +++ b/app/include/CommaSeparatedIterator.h @@ -3,9 +3,7 @@ */ #pragma once -#include // for transform -#include // for std::tolower -#include // for std::istream +#include // for std::istream #include // For std::string #include // for std::pair diff --git a/app/include/Driver.h b/app/include/Driver.h index 5baec0e..c4f55a9 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -11,12 +11,10 @@ // #include "ITS./.h" #include // for std::ofstream -#include // for std::setw -#include // for std::cerr, std::cout, std::ostream -#include // for std::endl -#include // for std::string, std::stoi, std::stod -#include // for std::tie -#include // for std::vector +#include // for std::left, std::setw +#include // for std::cout +#include // for std::endl, std::ostream +#include // for std::string ///////////////////////////// // Macros @@ -41,5 +39,6 @@ DrvrReturnCode ValidateInputs(const DrvrParams ¶ms); std::string GetDatetimeString(); DrvrReturnCode ParseDouble(const std::string &str, double &value); DrvrReturnCode ParseInteger(const std::string &str, int &value); +void PrintLabel(std::ofstream &fp, const std::string &lbl); void StringToLower(std::string &str); void Version(std::ostream &os = std::cout); \ No newline at end of file diff --git a/app/include/ReturnCodes.h b/app/include/ReturnCodes.h index 16eed51..b393e58 100644 --- a/app/include/ReturnCodes.h +++ b/app/include/ReturnCodes.h @@ -8,23 +8,23 @@ // TODO-TEMPLATE: Add driver return codes here and corresponding entries in app/src/ReturnCodes.cpp /******************************************************************************* - * Return Codes defined by this driver software. + * Return Codes defined by this driver software (128-255) ******************************************************************************/ // clang-format off enum DrvrReturnCode { - // Primary Return Codes (1000-1099) - DRVR__SUCCESS = 1000, /**< Successful execution */ + // Primary Return Codes + DRVR__SUCCESS = 128, /**< Successful execution */ DRVR__RETURN_SUCCESS, /**< Indicates driver should exit successfully */ DRVRERR__MISSING_OPTION, /**< No value provided for given argument */ DRVRERR__INVALID_OPTION, /**< Unknown option specified */ DRVRERR__OPENING_INPUT_FILE, /**< Failed to open the input file for reading */ DRVRERR__OPENING_OUTPUT_FILE, /**< Failed to open the output file for writing */ - // Input File Parsing Errors (1100-1199) - DRVRERR__PARSE = 1100, /**< Failed parsing inputs; unknown parameter */ + // Input File Parsing Errors + DRVRERR__PARSE = 160, /**< Failed parsing inputs; unknown parameter */ - // Validation Errors (1200-1299) - DRVRERR__VALIDATION_IN_FILE = 1200, /**< Input file not specified */ + // Validation Errors + DRVRERR__VALIDATION_IN_FILE = 192, /**< Input file not specified */ DRVRERR__VALIDATION_OUT_FILE, /**< Output file not specified */ }; // clang-format on diff --git a/app/include/Structs.h b/app/include/Structs.h index caea4ed..81409a6 100644 --- a/app/include/Structs.h +++ b/app/include/Structs.h @@ -3,12 +3,13 @@ */ #pragma once -#include // For std::string +#include // for std::string ///////////////////////////// // Data Structures // TODO-TEMPLATE: Basic struct provided to hold input/output file names and DBG flag +/** Parameters provided to the command line driver */ struct DrvrParams { std::string in_file = ""; /**< Input file */ std::string out_file = ""; /**< Output file */ diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index d3ec79a..f1c4b2d 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -32,7 +32,7 @@ set_target_properties( ${DRIVER_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" + LIBRARY_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} + ARCHIVE_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} + RUNTIME_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} ) diff --git a/app/src/CommaSeparatedIterator.cpp b/app/src/CommaSeparatedIterator.cpp index ac428ed..9e665bc 100644 --- a/app/src/CommaSeparatedIterator.cpp +++ b/app/src/CommaSeparatedIterator.cpp @@ -5,8 +5,13 @@ #include "Driver.h" -#include // for std::ptrdiff_t -#include // for std::input_iterator_tag +#include // for transform +#include // for std::tolower +#include // for std::ptrdiff_t, std::size_t +#include // for std::istream +#include // for std::input_iterator_tag +#include // for std::runtime_error +#include // for std::getline, std::string CommaSeparatedIterator::CommaSeparatedIterator(std::istream &stream): stream_(stream) { @@ -30,7 +35,7 @@ CommaSeparatedIterator &CommaSeparatedIterator::operator++() { } // Parse line by comma delimiter - size_t pos = line_.find(','); + std::size_t pos = line_.find(','); if (pos != std::string::npos) { first_ = line_.substr(0, pos); second_ = line_.substr(pos + 1); diff --git a/app/src/DriverUtils.cpp b/app/src/DriverUtils.cpp index 6775406..5022a6a 100644 --- a/app/src/DriverUtils.cpp +++ b/app/src/DriverUtils.cpp @@ -13,34 +13,53 @@ #endif #endif -#include // for localtime_{s,r}, std::{time, time_t, tm, strftime} +#include // for std::transform +#include // for std::tolower +#include // for localtime_{s,r}, std::{time, time_t, tm, strftime} +#include // for std::ofstream +#include // for std::setfill, std::setw +#include // for std::cerr, std::endl +#include // for std::stod, std::stoi, std::string -/******************************************************************************* - * Print version information to the specified output stream +/****************************************************************************** + * Get a string containing the current date and time information. * - * @param[in] os Output stream for writing; defaults to `std::cout` + * @return A localized standard date and time string (locale dependent) ******************************************************************************/ -void Version(std::ostream &os) { - os << std::setfill('*') << std::setw(55) << "" << std::endl; - os << "Institute for Telecommunication Sciences - Boulder, CO" << std::endl; - os << "\tDriver Version: " << DRIVER_VERSION << std::endl; - os << "\t" << LIBRARY_NAME << " Version: " << LIBRARY_VERSION << std::endl; - os << "Time: " << GetDatetimeString() << std::endl; - os << std::setfill('*') << std::setw(55) << "" << std::endl; +std::string GetDatetimeString() { + std::time_t now = std::time(nullptr); + struct std::tm localTime; + +#ifdef _WIN32 + localtime_s(&localTime, &now); +#else + if (localtime_r(&now, &localTime) == nullptr) { + return "Date and time unknown"; + } +#endif + char mbstr[100]; + if (std::strftime(mbstr, sizeof(mbstr), "%c", &localTime) == 0) { + return "Could not format datetime string"; + } + return std::string(mbstr); } /******************************************************************************* - * Helper function to format and print error messages encountered during - * validation of input parameters + * Parse a double value read from the input parameter file * - * @param[in] opt Command flag in error - * @param[in] err Error code - * @return Return code + * @param[in] str Input file value as string + * @param[out] value Input file value converted to double + * @return Return code ******************************************************************************/ -int Validate_RequiredErrMsgHelper(const std::string &opt, const int err) { - std::cerr << "Driver Error " << err << ": Option \"" << opt - << "\" is required but was not provided" << std::endl; - return err; +int ParseDouble(const std::string &str, double &value) { + try { + value = std::stod(str); + } catch (...) { + // error parsing the input string value + return DRVRERR__PARSE; + } + + return SUCCESS; } /******************************************************************************* @@ -67,24 +86,6 @@ int ParseInteger(const std::string &str, int &value) { return SUCCESS; } -/******************************************************************************* - * Parse a double value read from the input parameter file - * - * @param[in] str Input file value as string - * @param[out] value Input file value converted to double - * @return Return code - ******************************************************************************/ -int ParseDouble(const std::string &str, double &value) { - try { - value = std::stod(str); - } catch (...) { - // error parsing the input string value - return DRVRERR__PARSE; - } - - return SUCCESS; -} - /******************************************************************************* * Common error handling function * @@ -98,29 +99,17 @@ int ParsingErrorHelper(const int err, const std::string &msg) { return err; } -/****************************************************************************** - * Get a string containing the current date and time information. +/******************************************************************************* + * Helper function to standardize printing of text labels to file * - * @return A localized standard date and time string (locale dependent) + * @param[in] fp Output stream, a text file open for writing + * @param[in] lbl Text message ******************************************************************************/ -std::string GetDatetimeString() { - std::time_t now = std::time(nullptr); - struct std::tm localTime; - -#ifdef _WIN32 - localtime_s(&localTime, &now); -#else - if (localtime_r(&now, &localTime) == nullptr) { - return "Date and time unknown"; - } -#endif - char mbstr[100]; - if (std::strftime(mbstr, sizeof(mbstr), "%c", &localTime) == 0) { - return "Could not format datetime string"; - } - return std::string(mbstr); +void PrintLabel(std::ofstream &fp, const std::string &lbl) { + fp << "[" << lbl << "]"; } + /****************************************************************************** * Convert a string to lowercase. * @@ -130,4 +119,32 @@ void StringToLower(std::string &str) { std::transform(str.begin(), str.end(), str.begin(), [](const char c) { return static_cast(std::tolower(c)); }); -} \ No newline at end of file +} + +/******************************************************************************* + * Helper function to format and print error messages encountered during + * validation of input parameters + * + * @param[in] opt Command flag in error + * @param[in] err Error code + * @return Return code + ******************************************************************************/ +int Validate_RequiredErrMsgHelper(const std::string &opt, const int err) { + std::cerr << "Driver Error " << err << ": Option \"" << opt + << "\" is required but was not provided" << std::endl; + return err; +} + +/******************************************************************************* + * Print version information to the specified output stream + * + * @param[in] os Output stream for writing; defaults to `std::cout` + ******************************************************************************/ +void Version(std::ostream &os) { + os << std::setfill('*') << std::setw(55) << "" << std::endl; + os << "Institute for Telecommunication Sciences - Boulder, CO" << std::endl; + os << "\tDriver Version: " << DRIVER_VERSION << std::endl; + os << "\t" << LIBRARY_NAME << " Version: " << LIBRARY_VERSION << std::endl; + os << "Time: " << GetDatetimeString() << std::endl; + os << std::setfill('*') << std::setw(55) << "" << std::endl; +} diff --git a/app/src/ReturnCodes.cpp b/app/src/ReturnCodes.cpp new file mode 100644 index 0000000..22a3b3c --- /dev/null +++ b/app/src/ReturnCodes.cpp @@ -0,0 +1,48 @@ +/** @file ReturnCodes.cpp + * Maps status message strings to driver return codes. + */ + +#include "ReturnCodes.h" + +#include // for std::string +#include // for std::unordered_map + +/******************************************************************************* + * Get an error message string from a return code. + * + * @param[in] code Driver return code. + * @return A status message corresponding to the input code. + ******************************************************************************/ +std::string GetDrvrReturnStatus(int code) { + static const std::unordered_map messages = { + {DRVR__SUCCESS, "Successful execution"}, + {DRVR__RETURN_SUCCESS, "Internal driver success"}, + {DRVRERR__MISSING_OPTION, "No value provided for given argument"}, + {DRVRERR__INVALID_OPTION, "Unknown option specified"}, + {DRVRERR__OPENING_INPUT_FILE, + "Failed to open the input file for reading"}, + {DRVRERR__OPENING_OUTPUT_FILE, + "Failed to open the output file for writing"}, + {DRVRERR__PARSE, "Failed parsing inputs; unknown parameter"}, + {DRVRERR__VALIDATION_IN_FILE, + "Option -i is required but was not provided"}, + {DRVRERR__VALIDATION_OUT_FILE, + "Option -o is required but was not provided"}, + }; + + // Construct status message + std::string msg = DRIVER_NAME; + if (code == DRVR__SUCCESS) { + msg += " Status: "; + } else { + msg += " Error: "; + } + + auto it = messages.find(static_cast(code)); + if (it != messages.end()) { + msg += it->second; + } else { + msg += "Undefined return code"; + } + return msg; +} diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index 0fa91d7..18a02ed 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -23,27 +23,19 @@ target_include_directories( # Link the library to the executable target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME}) -# Make driver executable name and location available to source -add_definitions(-DDRIVER_NAME="${DRIVER_NAME}") -add_definitions(-DDRIVER_LOCATION="${DRIVER_EXE_DIR}/${CMAKE_BUILD_TYPE}") +# Make driver executable location available to source +add_definitions(-DDRIVER_LOCATION="$") set_target_properties( ${DRIVER_TEST_NAME} PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" - ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" - RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" + LIBRARY_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} + ARCHIVE_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} + RUNTIME_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} ) ########################################### ## SET UP AND DISCOVER TESTS ########################################### -if (NOT ${GOOGLETEST_ADDED}) - 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(GOOGLETEST_ADDED ON) -endif () - +include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME} GTest::gtest_main) gtest_discover_tests(${DRIVER_TEST_NAME}) diff --git a/app/tests/TempTextFile.cpp b/app/tests/TempTextFile.cpp index ab619ff..ab572a5 100644 --- a/app/tests/TempTextFile.cpp +++ b/app/tests/TempTextFile.cpp @@ -11,7 +11,10 @@ #ifndef __STDC_WANT_LIB_EXT1__ #define __STDC_WANT_LIB_EXT1__ 1 #endif - #include // for L_tmpnam_s, tmpnam_s + #include // for L_tmpnam_s, tmpnam_s +#else // macOS and Linux + #include // for mkstemp + #include // for close #endif #include // for std::remove @@ -19,6 +22,7 @@ #include // for std::cerr, std::cout, std::ios::trunc #include // for std::endl #include // for std::runtime_error +#include // for std::string TempTextFile::TempTextFile(const std::string &content) { #ifdef _WIN32 diff --git a/app/tests/TestDriver.cpp b/app/tests/TestDriver.cpp index 0d4cc61..acc1313 100644 --- a/app/tests/TestDriver.cpp +++ b/app/tests/TestDriver.cpp @@ -7,7 +7,7 @@ TEST_F(DriverTest, MissingOptionError1) { // Test case: missing option between two provided flags std::string cmd = executable + " -i -o out.txt"; SuppressOutputs(cmd); - int rtn = std::system(cmd.c_str()); + int rtn = RunCommand(cmd); EXPECT_EQ(rtn, DRVRERR__MISSING_OPTION); } @@ -15,14 +15,14 @@ TEST_F(DriverTest, MissingOptionError2) { // Test case: missing option at the end of command std::string cmd = executable + " -i"; SuppressOutputs(cmd); - int rtn = std::system(cmd.c_str()); + int rtn = RunCommand(cmd); EXPECT_EQ(rtn, DRVRERR__MISSING_OPTION); } TEST_F(DriverTest, InvalidOptionError) { std::string cmd = executable + " -X"; SuppressOutputs(cmd); - int rtn = std::system(cmd.c_str()); + int rtn = RunCommand(cmd); EXPECT_EQ(rtn, DRVRERR__INVALID_OPTION); } @@ -47,7 +47,7 @@ TEST_F(DriverTest, OpeningOutputFileError) { TEST_F(DriverTest, ValidationInFileError) { std::string cmd = executable + " -o out.txt"; SuppressOutputs(cmd); - int rtn = std::system(cmd.c_str()); + int rtn = RunCommand(cmd); EXPECT_EQ(rtn, DRVRERR__VALIDATION_IN_FILE); } @@ -56,7 +56,7 @@ TEST_F(DriverTest, ValidationOutFileError) { // TODO-TEMPLATE May need to update the command here std::string cmd = executable + " -i in.txt"; SuppressOutputs(cmd); - int rtn = std::system(cmd.c_str()); + int rtn = RunCommand(cmd); EXPECT_EQ(rtn, DRVRERR__VALIDATION_OUT_FILE); } diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index 677fba1..df27a85 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -7,12 +7,16 @@ #include "TempTextFile.h" #include // for std::replace -#include // for std::remove +#include // for std::remove, std::perror #include // for std::system #include // GoogleTest -#include // for std::cout, std::endl +#include // for std::cout +#include // for std::endl, std::flush #include // for std::string +#ifndef _WIN32 + #include // for WEXITSTATUS +#endif /******************************************************************************* * @class DriverTest @@ -34,13 +38,6 @@ class DriverTest: public ::testing::Test { // Get the name of the executable to test executable = std::string(DRIVER_LOCATION); - executable += "/" + std::string(DRIVER_NAME); -#ifdef _WIN32 - std::replace(executable.begin(), executable.end(), '/', '\\'); - executable += ".exe"; -#else - executable = "./" + executable; -#endif } /*********************************************************************** diff --git a/include/ITS.TODO-TEMPLATE/ReturnCodes.h b/include/ITS.TODO-TEMPLATE/ReturnCodes.h new file mode 100644 index 0000000..e09d022 --- /dev/null +++ b/include/ITS.TODO-TEMPLATE/ReturnCodes.h @@ -0,0 +1,26 @@ +/** @file ReturnCodes.h + * Contains return codes used by this software + */ +#pragma once + +#include +#include + +namespace ITS { +// TODO-TEMPLATE put this enum in the correct namespace + +/******************************************************************************* + * Return Codes defined by this software (0-127) + ******************************************************************************/ +// clang-format off +enum ReturnCode { + SUCCESS = 0, /**< Successful execution */ + + // TODO-TEMPLATE add return codes for this software + // TODO-TEMPLATE: Add corresponding status messages in src/ReturnCodes.cpp +}; +// clang-format on + +std::string GetReturnStatus(int code); + +} // namespace ITS diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e4dbba0..f4f8926 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,13 +6,17 @@ set(PROJECT_HEADERS "${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}") add_library( ${LIB_NAME} SHARED - "TODO-TEMPLATE" + "ReturnCodes.cpp" + "{PROJECT_HEADERS}/ReturnCodes.h" "${PROJECT_HEADERS}/${LIB_NAME}.h" ) # Add the include directory target_include_directories(${LIB_NAME} PUBLIC "${PROJECT_SOURCE_DIR}/include") +# Add definition to get the library name inside the library +add_definitions(-DLIBRARY_NAME="${LIB_NAME}") + # Set minimum C++ version to C++11 target_compile_features(${LIB_NAME} PUBLIC cxx_std_11) diff --git a/src/ReturnCodes.cpp b/src/ReturnCodes.cpp new file mode 100644 index 0000000..02c7c54 --- /dev/null +++ b/src/ReturnCodes.cpp @@ -0,0 +1,41 @@ +/** @file ReturnCodes.cpp + * Maps status messages to library return codes + */ + +#include "ITS.TODO-TEMPLATE/ReturnCodes.h" + +#include // for std::string +#include // for std::unordered_map + +namespace ITS { +// TODO-TEMPLATE: put this function in this software's namespace + +/******************************************************************************* + * Get an error message string from a return code. + * + * @param[in] code Integer return code. + * @return A status message corresponding to the input code. + ******************************************************************************/ +std::string GetReturnStatus(int code) { + static const std::unordered_map messages = { + {SUCCESS, "Successful execution"} + // TODO-TEMPLATE: Add messages corresponding to all return codes here + }; + // Construct status message + std::string msg = LIBRARY_NAME; + if (code == SUCCESS) { + msg += " Status: "; + } else { + msg += " Error: "; + } + + auto it = messages.find(static_cast(code)); + if (it != messages.end()) { + msg += it->second; + } else { + msg += "Undefined return code"; + } + return msg; +} + +} // namespace ITS \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d4b0042..92583ab 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,10 +7,6 @@ set(TEST_NAME "Test${LIB_NAME}") ## TODO-TEMPLATE: See CREATING-REPOSITORIES.md for examples. add_executable( ${TEST_NAME} - "TestAeronauticalStatisticalModel.cpp" - "TestHeightGainTerminalCorrectionModel.cpp" - "TestInverseComplementaryCumulativeDistribution.cpp" - "TestTerrestrialStatisticalModel.cpp" "TestUtils.cpp" "TestUtils.h" ) @@ -25,10 +21,6 @@ set_target_properties( ########################################### ## 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(GOOGLETEST_ADDED ON) 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/TestUtils.cpp b/tests/TestUtils.cpp new file mode 100644 index 0000000..c3e40db --- /dev/null +++ b/tests/TestUtils.cpp @@ -0,0 +1,22 @@ +#include "TestUtils.h" + +#include // for std::string + +// TODO-TEMPLATE: populate this file with common utilities for tests + +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; +} \ No newline at end of file diff --git a/tests/TestUtils.h b/tests/TestUtils.h new file mode 100644 index 0000000..b551a11 --- /dev/null +++ b/tests/TestUtils.h @@ -0,0 +1,5 @@ +#pragma once + +#include // for GoogleTest + +// TODO-TEMPLATE: define any common test fixtures here \ No newline at end of file diff --git a/tests/data/README.md b/tests/data/README.md new file mode 100644 index 0000000..1ba5dab --- /dev/null +++ b/tests/data/README.md @@ -0,0 +1,4 @@ +# TODO-TEMPLATE + +Populate this folder with example input and output files for +use in unit tests. After populating this folder, delete this file. From 09c1bfa75a6351c8f392c67d475def3590adf43d Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 30 Oct 2024 22:22:04 -0400 Subject: [PATCH 172/379] cleanup remove "using" statements for math functions, better specific use of external headers, some equation rewrites for legibility, and minor cleanup --- CMakeLists.txt | 2 +- app/include/Driver.h | 9 +- app/src/AeronauticalStatisticalModel.cpp | 8 ++ app/src/CommaSeparatedIterator.cpp | 11 +- app/src/DriverUtils.cpp | 132 +++++++++++------- app/src/HeightGainTerminalCorrectionModel.cpp | 8 ++ app/src/Reporting.cpp | 3 + app/src/ReturnCodes.cpp | 3 +- app/src/TerrestrialStatisticalModel.cpp | 8 ++ app/tests/TempTextFile.cpp | 1 + app/tests/TestDriver.h | 6 +- app/tests/TestDriverASM.cpp | 2 + app/tests/TestDriverHGTCM.cpp | 2 + app/tests/TestDriverTSM.cpp | 2 + include/ITS.ITU.PSeries.P2108/P2108.h | 12 -- include/ITS.ITU.PSeries.P2108/ReturnCodes.h | 4 +- src/AeronauticalStatisticalModel.cpp | 10 +- src/HeightGainTerminalCorrectionModel.cpp | 26 ++-- ...rseComplementaryCumulativeDistribution.cpp | 5 +- src/ReturnCodes.cpp | 3 + src/TerrestrialStatisticalModel.cpp | 23 +-- tests/TestUtils.cpp | 6 +- tests/TestUtils.h | 2 +- 23 files changed, 183 insertions(+), 105 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 39da6f1..63f8107 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,7 +68,7 @@ if (NOT DOCS_ONLY) if (RUN_TESTS) # Build and run unit tests add_subdirectory(tests) endif () - if (BUILD_DRIVER) + if (BUILD_DRIVER OR RUN_DRIVER_TESTS) add_subdirectory(app) endif () endif () diff --git a/app/include/Driver.h b/app/include/Driver.h index 9b06be0..f347a2b 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -10,10 +10,9 @@ #include // for std::ifstream, std::ofstream #include // for std::setw -#include // for std::cerr, std::cout, std::ostream -#include // for std::endl -#include // for std::string, std::stoi, std::stod -#include // for std::tie +#include // for std::cout +#include // for std::endl, std::ostream +#include // for std::string #include // for std::vector ///////////////////////////// @@ -60,11 +59,11 @@ void WriteTSMInputs(std::ofstream &fp, const TSMParams ¶ms); // Reporting void PrintClutterTypeLabel(std::ofstream &fp, const ClutterType clutter_type); -void PrintLabel(std::ofstream &fp, const std::string &lbl); // Driver Utils std::string GetDatetimeString(); DrvrReturnCode ParseDouble(const std::string &str, double &value); DrvrReturnCode ParseInteger(const std::string &str, int &value); +void PrintLabel(std::ofstream &fp, const std::string &lbl); void StringToLower(std::string &str); void Version(std::ostream &os = std::cout); \ No newline at end of file diff --git a/app/src/AeronauticalStatisticalModel.cpp b/app/src/AeronauticalStatisticalModel.cpp index 3fbebd3..bbf6b2d 100644 --- a/app/src/AeronauticalStatisticalModel.cpp +++ b/app/src/AeronauticalStatisticalModel.cpp @@ -3,6 +3,14 @@ */ #include "Driver.h" +#include // for std::ifstream, std::ofstream +#include // for std::cerr +#include // for std::istream +#include // for std::endl +#include // for std::string +#include // for std::tie +#include // for std::vector + // Define the input keys const std::string ASMInputKeys::f__ghz = "f__ghz"; const std::string ASMInputKeys::theta__deg = "theta__deg"; diff --git a/app/src/CommaSeparatedIterator.cpp b/app/src/CommaSeparatedIterator.cpp index ac428ed..9e665bc 100644 --- a/app/src/CommaSeparatedIterator.cpp +++ b/app/src/CommaSeparatedIterator.cpp @@ -5,8 +5,13 @@ #include "Driver.h" -#include // for std::ptrdiff_t -#include // for std::input_iterator_tag +#include // for transform +#include // for std::tolower +#include // for std::ptrdiff_t, std::size_t +#include // for std::istream +#include // for std::input_iterator_tag +#include // for std::runtime_error +#include // for std::getline, std::string CommaSeparatedIterator::CommaSeparatedIterator(std::istream &stream): stream_(stream) { @@ -30,7 +35,7 @@ CommaSeparatedIterator &CommaSeparatedIterator::operator++() { } // Parse line by comma delimiter - size_t pos = line_.find(','); + std::size_t pos = line_.find(','); if (pos != std::string::npos) { first_ = line_.substr(0, pos); second_ = line_.substr(pos + 1); diff --git a/app/src/DriverUtils.cpp b/app/src/DriverUtils.cpp index 55b1446..5022a6a 100644 --- a/app/src/DriverUtils.cpp +++ b/app/src/DriverUtils.cpp @@ -13,20 +13,53 @@ #endif #endif -#include // for localtime_{s,r}, std::{time, time_t, tm, strftime} +#include // for std::transform +#include // for std::tolower +#include // for localtime_{s,r}, std::{time, time_t, tm, strftime} +#include // for std::ofstream +#include // for std::setfill, std::setw +#include // for std::cerr, std::endl +#include // for std::stod, std::stoi, std::string + +/****************************************************************************** + * Get a string containing the current date and time information. + * + * @return A localized standard date and time string (locale dependent) + ******************************************************************************/ +std::string GetDatetimeString() { + std::time_t now = std::time(nullptr); + struct std::tm localTime; + +#ifdef _WIN32 + localtime_s(&localTime, &now); +#else + if (localtime_r(&now, &localTime) == nullptr) { + return "Date and time unknown"; + } +#endif + char mbstr[100]; + if (std::strftime(mbstr, sizeof(mbstr), "%c", &localTime) == 0) { + return "Could not format datetime string"; + } + return std::string(mbstr); +} /******************************************************************************* - * Print version information to the specified output stream + * Parse a double value read from the input parameter file * - * @param[in] os Output stream for writing; defaults to `std::cout` + * @param[in] str Input file value as string + * @param[out] value Input file value converted to double + * @return Return code ******************************************************************************/ -void Version(std::ostream &os) { - os << std::setfill('*') << std::setw(55) << "" << std::endl; - os << "Institute for Telecommunication Sciences - Boulder, CO" << std::endl; - os << "\tDriver Version: " << DRIVER_VERSION << std::endl; - os << "\t" << LIBRARY_NAME << " Version: " << LIBRARY_VERSION << std::endl; - os << "Time: " << GetDatetimeString() << std::endl; - os << std::setfill('*') << std::setw(55) << "" << std::endl; +int ParseDouble(const std::string &str, double &value) { + try { + value = std::stod(str); + } catch (...) { + // error parsing the input string value + return DRVRERR__PARSE; + } + + return SUCCESS; } /******************************************************************************* @@ -36,7 +69,7 @@ void Version(std::ostream &os) { * @param[out] value Input file value converted to int * @return Return code ******************************************************************************/ -DrvrReturnCode ParseInteger(const std::string &str, int &value) { +int ParseInteger(const std::string &str, int &value) { try { size_t pos; value = std::stoi(str, &pos, 10); @@ -50,50 +83,33 @@ DrvrReturnCode ParseInteger(const std::string &str, int &value) { return DRVRERR__PARSE; }; - return DRVR__SUCCESS; + return SUCCESS; } /******************************************************************************* - * Parse a double value read from the input parameter file + * Common error handling function * - * @param[in] str Input file value as string - * @param[out] value Input file value converted to double - * @return Return code + * @param[in] err Error parsing code + * @param[in] msg Error message + * @return Error code from input param ******************************************************************************/ -DrvrReturnCode ParseDouble(const std::string &str, double &value) { - try { - value = std::stod(str); - } catch (...) { - // error parsing the input string value - return DRVRERR__PARSE; - } - - return DRVR__SUCCESS; +int ParsingErrorHelper(const int err, const std::string &msg) { + std::cerr << "Driver Error " << err << ": Unable to parse '" << msg + << "' value." << std::endl; + return err; } -/****************************************************************************** - * Get a string containing the current date and time information. +/******************************************************************************* + * Helper function to standardize printing of text labels to file * - * @return A localized standard date and time string (locale dependent) + * @param[in] fp Output stream, a text file open for writing + * @param[in] lbl Text message ******************************************************************************/ -std::string GetDatetimeString() { - std::time_t now = std::time(nullptr); - struct std::tm localTime; - -#ifdef _WIN32 - localtime_s(&localTime, &now); -#else - if (localtime_r(&now, &localTime) == nullptr) { - return "Date and time unknown"; - } -#endif - char mbstr[100]; - if (std::strftime(mbstr, sizeof(mbstr), "%c", &localTime) == 0) { - return "Could not format datetime string"; - } - return std::string(mbstr); +void PrintLabel(std::ofstream &fp, const std::string &lbl) { + fp << "[" << lbl << "]"; } + /****************************************************************************** * Convert a string to lowercase. * @@ -106,11 +122,29 @@ void StringToLower(std::string &str) { } /******************************************************************************* - * Helper function to standardize printing of text labels to file + * Helper function to format and print error messages encountered during + * validation of input parameters * - * @param[in] fp Output stream, a text file open for writing - * @param[in] lbl Text message + * @param[in] opt Command flag in error + * @param[in] err Error code + * @return Return code ******************************************************************************/ -void PrintLabel(std::ofstream &fp, const std::string &lbl) { - fp << "[" << lbl << "]"; +int Validate_RequiredErrMsgHelper(const std::string &opt, const int err) { + std::cerr << "Driver Error " << err << ": Option \"" << opt + << "\" is required but was not provided" << std::endl; + return err; +} + +/******************************************************************************* + * Print version information to the specified output stream + * + * @param[in] os Output stream for writing; defaults to `std::cout` + ******************************************************************************/ +void Version(std::ostream &os) { + os << std::setfill('*') << std::setw(55) << "" << std::endl; + os << "Institute for Telecommunication Sciences - Boulder, CO" << std::endl; + os << "\tDriver Version: " << DRIVER_VERSION << std::endl; + os << "\t" << LIBRARY_NAME << " Version: " << LIBRARY_VERSION << std::endl; + os << "Time: " << GetDatetimeString() << std::endl; + os << std::setfill('*') << std::setw(55) << "" << std::endl; } diff --git a/app/src/HeightGainTerminalCorrectionModel.cpp b/app/src/HeightGainTerminalCorrectionModel.cpp index a72bc50..a76cd1f 100644 --- a/app/src/HeightGainTerminalCorrectionModel.cpp +++ b/app/src/HeightGainTerminalCorrectionModel.cpp @@ -3,6 +3,14 @@ */ #include "Driver.h" +#include // for std::ifstream, std::ofstream +#include // for std::cerr +#include // for std::istream +#include // for std::endl +#include // for std::string +#include // for std::tie +#include // for std::vector + // Define the input keys const std::string HGTCMInputKeys::f__ghz = "f__ghz"; const std::string HGTCMInputKeys::h__meter = "h__meter"; diff --git a/app/src/Reporting.cpp b/app/src/Reporting.cpp index c5065a4..4d5da02 100644 --- a/app/src/Reporting.cpp +++ b/app/src/Reporting.cpp @@ -3,6 +3,9 @@ */ #include "Driver.h" +#include // for std::ofstream +#include // for std::string + /******************************************************************************* * Print text message corresponding to clutter type enum value * diff --git a/app/src/ReturnCodes.cpp b/app/src/ReturnCodes.cpp index 9de5072..d664f04 100644 --- a/app/src/ReturnCodes.cpp +++ b/app/src/ReturnCodes.cpp @@ -4,7 +4,8 @@ #include "ReturnCodes.h" -#include +#include // for std::string +#include // for std::unordered_map /******************************************************************************* * Get an error message string from a return code. diff --git a/app/src/TerrestrialStatisticalModel.cpp b/app/src/TerrestrialStatisticalModel.cpp index f9aa631..bc1bdff 100644 --- a/app/src/TerrestrialStatisticalModel.cpp +++ b/app/src/TerrestrialStatisticalModel.cpp @@ -3,6 +3,14 @@ */ #include "Driver.h" +#include // for std::ifstream, std::ofstream +#include // for std::cerr +#include // for std::istream +#include // for std::endl +#include // for std::string +#include // for std::tie +#include // for std::vector + // Define the input keys const std::string TSMInputKeys::f__ghz = "f__ghz"; const std::string TSMInputKeys::d__km = "d__km"; diff --git a/app/tests/TempTextFile.cpp b/app/tests/TempTextFile.cpp index a86b319..ab572a5 100644 --- a/app/tests/TempTextFile.cpp +++ b/app/tests/TempTextFile.cpp @@ -22,6 +22,7 @@ #include // for std::cerr, std::cout, std::ios::trunc #include // for std::endl #include // for std::runtime_error +#include // for std::string TempTextFile::TempTextFile(const std::string &content) { #ifdef _WIN32 diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index ed2f395..f8108f4 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -7,17 +7,17 @@ #include "TempTextFile.h" #include // for std::replace -#include // for std::remove +#include // for std::remove, std::perror #include // for std::system #include // GoogleTest -#include // for std::cout, std::endl, std::flush +#include // for std::cout +#include // for std::endl, std::flush #include // for std::string #ifndef _WIN32 #include // for WEXITSTATUS #endif - /******************************************************************************* * @class DriverTest * Test fixture for running the driver executable tests. diff --git a/app/tests/TestDriverASM.cpp b/app/tests/TestDriverASM.cpp index 4b7096a..e2bcb8e 100644 --- a/app/tests/TestDriverASM.cpp +++ b/app/tests/TestDriverASM.cpp @@ -1,5 +1,7 @@ #include "TestDriver.h" +#include // for std::string + /******************************************************************************* * Driver test fixture for the Aeronautical Statistical Model ******************************************************************************/ diff --git a/app/tests/TestDriverHGTCM.cpp b/app/tests/TestDriverHGTCM.cpp index 168fc68..3fd5d92 100644 --- a/app/tests/TestDriverHGTCM.cpp +++ b/app/tests/TestDriverHGTCM.cpp @@ -1,5 +1,7 @@ #include "TestDriver.h" +#include // for std::string + /******************************************************************************* * Driver test fixture for the Height Gain Terminal Correction Model ******************************************************************************/ diff --git a/app/tests/TestDriverTSM.cpp b/app/tests/TestDriverTSM.cpp index 8447381..4872360 100644 --- a/app/tests/TestDriverTSM.cpp +++ b/app/tests/TestDriverTSM.cpp @@ -1,5 +1,7 @@ #include "TestDriver.h" +#include // for std::string + /******************************************************************************* * Driver test fixture for the Terrestrial Statistical Model ******************************************************************************/ diff --git a/include/ITS.ITU.PSeries.P2108/P2108.h b/include/ITS.ITU.PSeries.P2108/P2108.h index cab1f6d..56a477f 100644 --- a/include/ITS.ITU.PSeries.P2108/P2108.h +++ b/include/ITS.ITU.PSeries.P2108/P2108.h @@ -6,8 +6,6 @@ #include "Enums.h" #include "ReturnCodes.h" -#include // For atan, fmin, log10, pow, sqrt, tan - namespace ITS { namespace ITU { namespace PSeries { @@ -22,16 +20,6 @@ namespace P2108 { #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 /** Approximate value of @f$ \pi @f$ */ diff --git a/include/ITS.ITU.PSeries.P2108/ReturnCodes.h b/include/ITS.ITU.PSeries.P2108/ReturnCodes.h index c575593..d408fd5 100644 --- a/include/ITS.ITU.PSeries.P2108/ReturnCodes.h +++ b/include/ITS.ITU.PSeries.P2108/ReturnCodes.h @@ -3,8 +3,8 @@ */ #pragma once -#include -#include +#include // for std::string +#include // for std::unordered_map namespace ITS { namespace ITU { diff --git a/src/AeronauticalStatisticalModel.cpp b/src/AeronauticalStatisticalModel.cpp index 603a2bf..8a93522 100644 --- a/src/AeronauticalStatisticalModel.cpp +++ b/src/AeronauticalStatisticalModel.cpp @@ -3,6 +3,8 @@ */ #include "ITS.ITU.PSeries.P2108/P2108.h" +#include // for std::pow, std::log, std::tan + namespace ITS { namespace ITU { namespace PSeries { @@ -37,16 +39,16 @@ ReturnCode AeronauticalStatisticalModel( return rtn; constexpr double A_1 = 0.05; - const double K_1 = 93 * pow(f__ghz, 0.175); + const double K_1 = 93 * std::pow(f__ghz, 0.175); - const double part1 = log(1 - p / 100.0); + const double part1 = std::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 = std::pow(-K_1 * part1 * cot(part2), part3) - 1 - part4; return rtn; } @@ -90,7 +92,7 @@ ReturnCode Section3p3_InputValidation( * @return Cotangent of the argument, @f$ \cot(x) @f$ ******************************************************************************/ double cot(const double x) { - return 1 / tan(x); + return 1 / std::tan(x); } } // namespace P2108 diff --git a/src/HeightGainTerminalCorrectionModel.cpp b/src/HeightGainTerminalCorrectionModel.cpp index 9e0af0d..9e60179 100644 --- a/src/HeightGainTerminalCorrectionModel.cpp +++ b/src/HeightGainTerminalCorrectionModel.cpp @@ -3,6 +3,8 @@ */ #include "ITS.ITU.PSeries.P2108/P2108.h" +#include // for std::atan, std::log10, std::pow, std::sqrt + namespace ITS { namespace ITU { namespace PSeries { @@ -48,8 +50,8 @@ ReturnCode HeightGainTerminalCorrectionModel( 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) + = std::atan(h_dif__meter / w_s__meter) * 180.0 / PI; // Equation (2e) + const double K_h2 = 21.8 + 6.2 * std::log10(f__ghz); // Equation (2f) switch (clutter_type) { case ClutterType::WATER_SEA: @@ -62,10 +64,11 @@ ReturnCode HeightGainTerminalCorrectionModel( 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) + const double K_nu = 0.342 * std::sqrt(f__ghz); // Equation (2g) + const double nu = K_nu + * std::sqrt( + h_dif__meter * theta_clut__deg + ); // Equation (2c) A_h__db = Equation_2a(nu); break; @@ -118,11 +121,12 @@ ReturnCode Section3p1_InputValidation( ******************************************************************************/ double Equation_2a(const double nu) { double J_nu__db; - if (nu <= -0.78) + 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); - + } else { + const double term1 = std::sqrt(std::pow(nu - 0.1, 2) + 1); + J_nu__db = 6.9 + 20 * std::log10(term1 + nu - 0.1); + } const double A_h__db = J_nu__db - 6.03; return A_h__db; @@ -139,7 +143,7 @@ double Equation_2a(const double nu) { 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); + const double A_h__db = -K_h2 * std::log10(h__meter / R__meter); return A_h__db; } diff --git a/src/InverseComplementaryCumulativeDistribution.cpp b/src/InverseComplementaryCumulativeDistribution.cpp index 67da705..127534b 100644 --- a/src/InverseComplementaryCumulativeDistribution.cpp +++ b/src/InverseComplementaryCumulativeDistribution.cpp @@ -3,7 +3,8 @@ */ #include "ITS.ITU.PSeries.P2108/P2108.h" -#include +#include // for std::log, std::sqrt +#include // for std::out_of_range namespace ITS { namespace ITU { @@ -38,7 +39,7 @@ double InverseComplementaryCumulativeDistribution(const double q) { if (q > 0.5) x = 1.0 - x; - const double T_x = sqrt(-2.0 * log(x)); + const double T_x = std::sqrt(-2.0 * std::log(x)); 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); diff --git a/src/ReturnCodes.cpp b/src/ReturnCodes.cpp index 4f82c91..e77c737 100644 --- a/src/ReturnCodes.cpp +++ b/src/ReturnCodes.cpp @@ -4,6 +4,9 @@ #include "ITS.ITU.PSeries.P2108/ReturnCodes.h" +#include // for std::string +#include // for std::unordered_map + namespace ITS { namespace ITU { namespace PSeries { diff --git a/src/TerrestrialStatisticalModel.cpp b/src/TerrestrialStatisticalModel.cpp index 2691738..e8c9d59 100644 --- a/src/TerrestrialStatisticalModel.cpp +++ b/src/TerrestrialStatisticalModel.cpp @@ -3,6 +3,8 @@ */ #include "ITS.ITU.PSeries.P2108/P2108.h" +#include // for std::fmin, std::log10, std::pow, std::sqrt + namespace ITS { namespace ITU { namespace PSeries { @@ -41,7 +43,7 @@ ReturnCode TerrestrialStatisticalModel( = 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 = std::fmin(L_ctt_2km__db, L_ctt_d__db); return SUCCESS; } @@ -59,23 +61,26 @@ double TerrestrialStatisticalModelHelper( ) { // Equations 4a and 4b constexpr double sigma_l__db = 4; - const double L_l__db - = -2 * log10(pow(10, -5 * log10(f__ghz) - 12.5) + pow(10, -16.5)); + const double term1 = std::pow(10, -5 * std::log10(f__ghz) - 12.5); + const double L_l__db = -2 * std::log10(term1 + std::pow(10, -16.5)); // Equations 5a and 5b constexpr double sigma_s__db = 6; - const double L_s__db = 32.98 + 23.9 * log10(d__km) + 3 * log10(f__ghz); + const double L_s__db + = 32.98 + 23.9 * std::log10(d__km) + 3 * std::log10(f__ghz); // Equation 3b - 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 numerator + = std::pow(sigma_l__db, 2) * std::pow(10, -0.2 * L_l__db) + + std::pow(sigma_s__db, 2) * std::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); + = std::pow(10, -0.2 * L_l__db) + std::pow(10, -0.2 * L_s__db); + const double sigma_cb__db = std::sqrt(numerator / denominator); // Equation 3a + const double term2 = std::pow(10, -0.2 * L_l__db); const double L_ctt__db - = -5 * log10(pow(10, -0.2 * L_l__db) + pow(10, -0.2 * L_s__db)) + = -5 * std::log10(term2 + std::pow(10, -0.2 * L_s__db)) - sigma_cb__db * InverseComplementaryCumulativeDistribution(p / 100); return L_ctt__db; diff --git a/tests/TestUtils.cpp b/tests/TestUtils.cpp index b36b773..9df6450 100644 --- a/tests/TestUtils.cpp +++ b/tests/TestUtils.cpp @@ -1,7 +1,9 @@ #include "TestUtils.h" -#include -#include +#include // for std::ifstream +#include // for std::istringstream +#include // for std::string, std::getline +#include // for std::vector void appendDirectorySep(std::string &str) { #ifdef _WIN32 diff --git a/tests/TestUtils.h b/tests/TestUtils.h index 49ac701..ce3e67a 100644 --- a/tests/TestUtils.h +++ b/tests/TestUtils.h @@ -9,7 +9,7 @@ using namespace ITS::ITU::PSeries::P2108; // Absolute tolerance for checking model outputs against test data -#define ABSTOL__DB 0.1 +constexpr double ABSTOL__DB = 0.1; struct AeronauticalStatisticalModelTestData { double f__ghz; From 69f618ffadab15e3829a946412b5d2bd484e6ba5 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 30 Oct 2024 22:30:34 -0400 Subject: [PATCH 173/379] remove template files --- app/data/README.md | 5 ----- include/ITS.TODO-TEMPLATE/ReturnCodes.h | 26 ------------------------- 2 files changed, 31 deletions(-) delete mode 100644 app/data/README.md delete mode 100644 include/ITS.TODO-TEMPLATE/ReturnCodes.h diff --git a/app/data/README.md b/app/data/README.md deleted file mode 100644 index 329f20e..0000000 --- a/app/data/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# TODO-TEMPLATE - -Populate this folder with example input and output files for -use with the command-line driver. After populating this folder, -delete this README file. diff --git a/include/ITS.TODO-TEMPLATE/ReturnCodes.h b/include/ITS.TODO-TEMPLATE/ReturnCodes.h deleted file mode 100644 index e09d022..0000000 --- a/include/ITS.TODO-TEMPLATE/ReturnCodes.h +++ /dev/null @@ -1,26 +0,0 @@ -/** @file ReturnCodes.h - * Contains return codes used by this software - */ -#pragma once - -#include -#include - -namespace ITS { -// TODO-TEMPLATE put this enum in the correct namespace - -/******************************************************************************* - * Return Codes defined by this software (0-127) - ******************************************************************************/ -// clang-format off -enum ReturnCode { - SUCCESS = 0, /**< Successful execution */ - - // TODO-TEMPLATE add return codes for this software - // TODO-TEMPLATE: Add corresponding status messages in src/ReturnCodes.cpp -}; -// clang-format on - -std::string GetReturnStatus(int code); - -} // namespace ITS From 280999f7a2aba70d9e4964efbf485cd351f206a7 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 30 Oct 2024 22:32:08 -0400 Subject: [PATCH 174/379] fix template merge deletion --- tests/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 67c9017..78666f5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -6,6 +6,10 @@ 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" ) From a4421de31f33299af900e2f11d1a62b8f967cd37 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 30 Oct 2024 22:39:01 -0400 Subject: [PATCH 175/379] delete template file --- tests/data/README.md | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 tests/data/README.md diff --git a/tests/data/README.md b/tests/data/README.md deleted file mode 100644 index 1ba5dab..0000000 --- a/tests/data/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# TODO-TEMPLATE - -Populate this folder with example input and output files for -use in unit tests. After populating this folder, delete this file. From 02c90195d4f63d7c4876e6392b5e5d2f2e59be64 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 30 Oct 2024 22:39:14 -0400 Subject: [PATCH 176/379] Fix driver utility return types --- app/src/DriverUtils.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/src/DriverUtils.cpp b/app/src/DriverUtils.cpp index 5022a6a..7e73339 100644 --- a/app/src/DriverUtils.cpp +++ b/app/src/DriverUtils.cpp @@ -13,13 +13,13 @@ #endif #endif -#include // for std::transform -#include // for std::tolower -#include // for localtime_{s,r}, std::{time, time_t, tm, strftime} -#include // for std::ofstream -#include // for std::setfill, std::setw -#include // for std::cerr, std::endl -#include // for std::stod, std::stoi, std::string +#include // for std::transform +#include // for std::tolower +#include // for localtime_{s,r}, std::{time, time_t, tm, strftime} +#include // for std::ofstream +#include // for std::setfill, std::setw +#include // for std::cerr, std::endl +#include // for std::stod, std::stoi, std::string /****************************************************************************** * Get a string containing the current date and time information. @@ -51,7 +51,7 @@ std::string GetDatetimeString() { * @param[out] value Input file value converted to double * @return Return code ******************************************************************************/ -int ParseDouble(const std::string &str, double &value) { +DrvrReturnCode ParseDouble(const std::string &str, double &value) { try { value = std::stod(str); } catch (...) { @@ -59,7 +59,7 @@ int ParseDouble(const std::string &str, double &value) { return DRVRERR__PARSE; } - return SUCCESS; + return DRVR__SUCCESS; } /******************************************************************************* @@ -69,7 +69,7 @@ int ParseDouble(const std::string &str, double &value) { * @param[out] value Input file value converted to int * @return Return code ******************************************************************************/ -int ParseInteger(const std::string &str, int &value) { +DrvrReturnCode ParseInteger(const std::string &str, int &value) { try { size_t pos; value = std::stoi(str, &pos, 10); @@ -83,7 +83,7 @@ int ParseInteger(const std::string &str, int &value) { return DRVRERR__PARSE; }; - return SUCCESS; + return DRVR__SUCCESS; } /******************************************************************************* From efd7ab522db021dadc0f9734a8e084dc2a4dfa27 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 30 Oct 2024 22:39:34 -0400 Subject: [PATCH 177/379] Fix file name --- app/include/ReturnCodes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/include/ReturnCodes.h b/app/include/ReturnCodes.h index b393e58..3a6eac7 100644 --- a/app/include/ReturnCodes.h +++ b/app/include/ReturnCodes.h @@ -1,4 +1,4 @@ -/** @file Errors.h +/** @file ReturnCodes.h * Defines return codes for the driver */ #pragma once From 7b4ba4f17a186f9af7c4eaf1c201cf735cc5b335 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 30 Oct 2024 22:39:45 -0400 Subject: [PATCH 178/379] Fix driver utility return types --- app/src/DriverUtils.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/DriverUtils.cpp b/app/src/DriverUtils.cpp index 5022a6a..b3ca958 100644 --- a/app/src/DriverUtils.cpp +++ b/app/src/DriverUtils.cpp @@ -51,7 +51,7 @@ std::string GetDatetimeString() { * @param[out] value Input file value converted to double * @return Return code ******************************************************************************/ -int ParseDouble(const std::string &str, double &value) { +DrvrReturnCode ParseDouble(const std::string &str, double &value) { try { value = std::stod(str); } catch (...) { @@ -59,7 +59,7 @@ int ParseDouble(const std::string &str, double &value) { return DRVRERR__PARSE; } - return SUCCESS; + return DRVR__SUCCESS; } /******************************************************************************* @@ -69,7 +69,7 @@ int ParseDouble(const std::string &str, double &value) { * @param[out] value Input file value converted to int * @return Return code ******************************************************************************/ -int ParseInteger(const std::string &str, int &value) { +DrvrReturnCode ParseInteger(const std::string &str, int &value) { try { size_t pos; value = std::stoi(str, &pos, 10); @@ -83,7 +83,7 @@ int ParseInteger(const std::string &str, int &value) { return DRVRERR__PARSE; }; - return SUCCESS; + return DRVR__SUCCESS; } /******************************************************************************* From e0e0a0191dc681bdf69fc4f3ef04df3a6eab45aa Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:50:46 -0400 Subject: [PATCH 179/379] FIx missing namespace qualifier --- tests/TestHeightGainTerminalCorrectionModel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/TestHeightGainTerminalCorrectionModel.cpp b/tests/TestHeightGainTerminalCorrectionModel.cpp index b33c023..d60fa87 100644 --- a/tests/TestHeightGainTerminalCorrectionModel.cpp +++ b/tests/TestHeightGainTerminalCorrectionModel.cpp @@ -114,8 +114,8 @@ TEST(Section3p1_Equation_2aTest, Section3p1_NuAboveLimit) { 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(1, 10, 5), -std::log10(2)); + EXPECT_DOUBLE_EQ(Equation_2b(5, 1, 2), -5 * std::log10(0.5)); EXPECT_DOUBLE_EQ(Equation_2b(0, 100, 100), 0); EXPECT_DOUBLE_EQ(Equation_2b(-1, 10, 1), 1); } From 4e9c01cbe14e88172bf730b92e9028c728b8301b Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:51:25 -0400 Subject: [PATCH 180/379] Make CONTIRBUTING.md pass Python linter --- CONTRIBUTING.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a193678..fc62afa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -113,11 +113,11 @@ 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/ +docs/ CMakeLists.txt # Doxygen configuration extern/ ... # External Git submodules/dependencies -include/ +include/ / # Include namespace folder, e.g. "ITS.Propagation.ITM" .h # Library header files go here, e.g. "ITM.h" and "ErrorCodes.h" src/ @@ -190,11 +190,11 @@ and the GitHub action successfully generates the documentation site. Below is an example showing the expected documentation formats. ```cpp -constexpr double = PI 3.1415; /**< Inline format, e.g. for constants or struct members */ +constexpr double = PI 3.1415; /**< Inline format, e.g. for constants */ /******************************************************************************* * 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$. From e761301a8f1b8947de904f5ced6568d32c2dfc4d Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:55:56 -0400 Subject: [PATCH 181/379] add missing cmath include --- tests/TestHeightGainTerminalCorrectionModel.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/TestHeightGainTerminalCorrectionModel.cpp b/tests/TestHeightGainTerminalCorrectionModel.cpp index d60fa87..bff10d3 100644 --- a/tests/TestHeightGainTerminalCorrectionModel.cpp +++ b/tests/TestHeightGainTerminalCorrectionModel.cpp @@ -1,5 +1,6 @@ #include "TestUtils.h" +#include // for std::log10 #include // For std::numeric_limits::max() // Test fixture for the unit tests From 00cef967ed2222661a0eab40755c57d479e78b18 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:59:42 -0400 Subject: [PATCH 182/379] Add c-typed functions for wrapper error handling --- include/ITS.ITU.PSeries.P2108/P2108.h | 4 +++ include/ITS.ITU.PSeries.P2108/ReturnCodes.h | 2 -- src/ReturnCodes.cpp | 35 ++++++++++++++++++++- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/include/ITS.ITU.PSeries.P2108/P2108.h b/include/ITS.ITU.PSeries.P2108/P2108.h index 56a477f..01cb195 100644 --- a/include/ITS.ITU.PSeries.P2108/P2108.h +++ b/include/ITS.ITU.PSeries.P2108/P2108.h @@ -44,9 +44,12 @@ EXPORTED ReturnCode HeightGainTerminalCorrectionModel( const ClutterType clutter_type, double &A_h__db ); +EXPORTED char *GetReturnStatusCharArray(const int code); +EXPORTED void FreeReturnStatusCharArray(char *c_msg); //////////////////////////////////////////////////////////////////////////////// // Private Functions +std::string GetReturnStatus(const int code); double cot(const double x); double InverseComplementaryCumulativeDistribution(const double q); double Equation_2a(const double nu); @@ -69,6 +72,7 @@ double TerrestrialStatisticalModelHelper( const double f__ghz, const double d__km, const double p ); + } // namespace P2108 } // namespace PSeries } // namespace ITU diff --git a/include/ITS.ITU.PSeries.P2108/ReturnCodes.h b/include/ITS.ITU.PSeries.P2108/ReturnCodes.h index d408fd5..1bf32c1 100644 --- a/include/ITS.ITU.PSeries.P2108/ReturnCodes.h +++ b/include/ITS.ITU.PSeries.P2108/ReturnCodes.h @@ -37,8 +37,6 @@ enum ReturnCode { }; // clang-format on -std::string GetReturnStatus(int code); - } // namespace P2108 } // namespace PSeries } // namespace ITU diff --git a/src/ReturnCodes.cpp b/src/ReturnCodes.cpp index e77c737..08168f8 100644 --- a/src/ReturnCodes.cpp +++ b/src/ReturnCodes.cpp @@ -4,6 +4,17 @@ #include "ITS.ITU.PSeries.P2108/ReturnCodes.h" +#ifdef _WIN32 + // Ensure strcpy_s is available on Windows + #ifndef __STDC_LIB_EXT1__ + #define __STDC_LIB_EXT1__ + #endif + #ifndef __STDC_WANT_LIB_EXT1__ + #define __STDC_WANT_LIB_EXT1__ 1 + #endif +#endif + +#include // for strcpy_s #include // for std::string #include // for std::unordered_map @@ -18,7 +29,7 @@ namespace P2108 { * @param[in] code Integer return code. * @return A status message corresponding to the input code. ******************************************************************************/ -std::string GetReturnStatus(int code) { +std::string GetReturnStatus(const int code) { static const std::unordered_map messages = {{SUCCESS, "Successful execution"}, {ERROR31__FREQUENCY, "Frequency must be between 0.3 and 3 GHz"}, @@ -50,6 +61,28 @@ std::string GetReturnStatus(int code) { return msg; } +/******************************************************************************* + * Get an error message string (as C-style string) from a return code. + * + * @param[in] code Integer return code. + * @return A status message corresponding to the input code. + ******************************************************************************/ +char *GetReturnStatusCharArray(const int code) { + std::string msg = GetReturnStatus(code); + char *c_msg = new char[msg.size() + 1]; + strcpy_s(c_msg, msg.size() + 1, msg.c_str()); + return c_msg; +} + +/******************************************************************************* + * Free the memory allocated by GetReturnStatusCharArray + * + * @param[in] c_msg The status message C-style string to delete + ******************************************************************************/ +void FreeReturnStatusCharArray(char *c_msg) { + delete[] c_msg; +} + } // namespace P2108 } // namespace PSeries } // namespace ITU From 7392ca43981e12fee12ecf895690a4169cfc656c Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:00:05 -0400 Subject: [PATCH 183/379] template wrapper error handling functions --- include/ITS.TODO-TEMPLATE/TODO-TEMPLATE.h | 37 +++++++++++++++++++++++ src/ReturnCodes.cpp | 34 ++++++++++++++++++++- 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 include/ITS.TODO-TEMPLATE/TODO-TEMPLATE.h diff --git a/include/ITS.TODO-TEMPLATE/TODO-TEMPLATE.h b/include/ITS.TODO-TEMPLATE/TODO-TEMPLATE.h new file mode 100644 index 0000000..20491a5 --- /dev/null +++ b/include/ITS.TODO-TEMPLATE/TODO-TEMPLATE.h @@ -0,0 +1,37 @@ +/** @file TODO-TEMPLATE.h + * Interface header for this library + * // TODO-TEMPLATE: Rename this file to your ${LIB_NAME} + */ +#pragma once + +// TODO-TEMPLATE add other local includes here +#include "ReturnCodes.h" + +namespace ITS { +// TODO-TEMPLATE: Use your library's namespace + +// Define cross-platform EXPORTED +#ifndef DOXYGEN_SHOULD_SKIP + #ifdef _WIN32 + #define EXPORTED extern "C" __declspec(dllexport) + #else + #define EXPORTED extern "C" + #endif +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Constants +// TODO-TEMPLATE define any global constants here (use constexpr!) + +//////////////////////////////////////////////////////////////////////////////// +// Public Functions +// TODO-TEMPLATE: Add functions which should be exported in the DLL +EXPORTED char *GetReturnStatusCharArray(const int code); +EXPORTED void FreeReturnStatusCharArray(char *c_msg); + +//////////////////////////////////////////////////////////////////////////////// +// Private Functions +// TODO-TEMPLATE: Add other/internal functions here (no need for "EXPORTED") +std::string GetReturnStatus(const int code); + +} // namespace ITS diff --git a/src/ReturnCodes.cpp b/src/ReturnCodes.cpp index 02c7c54..8ea03cf 100644 --- a/src/ReturnCodes.cpp +++ b/src/ReturnCodes.cpp @@ -4,11 +4,21 @@ #include "ITS.TODO-TEMPLATE/ReturnCodes.h" +#ifdef _WIN32 + // Ensure strcpy_s is available on Windows + #ifndef __STDC_LIB_EXT1__ + #define __STDC_LIB_EXT1__ + #endif + #ifndef __STDC_WANT_LIB_EXT1__ + #define __STDC_WANT_LIB_EXT1__ 1 + #endif +#endif +#include // for strcpy_s #include // for std::string #include // for std::unordered_map namespace ITS { -// TODO-TEMPLATE: put this function in this software's namespace +// TODO-TEMPLATE: put these functions in this software's namespace /******************************************************************************* * Get an error message string from a return code. @@ -38,4 +48,26 @@ std::string GetReturnStatus(int code) { return msg; } +/******************************************************************************* + * Get an error message string (as C-style string) from a return code. + * + * @param[in] code Integer return code. + * @return A status message corresponding to the input code. + ******************************************************************************/ +char *GetReturnStatusCharArray(const int code) { + std::string msg = GetReturnStatus(code); + char *c_msg = new char[msg.size() + 1]; + strcpy_s(c_msg, msg.size() + 1, msg.c_str()); + return c_msg; +} + +/******************************************************************************* + * Free the memory allocated by GetReturnStatusCharArray + * + * @param[in] c_msg The status message C-style string to delete + ******************************************************************************/ +void FreeReturnStatusCharArray(char *c_msg) { + delete[] c_msg; +} + } // namespace ITS \ No newline at end of file From c2b81c910193f0affaee5d2505c691a6187817d7 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:02:11 -0400 Subject: [PATCH 184/379] Delete template file --- include/ITS.TODO-TEMPLATE/TODO-TEMPLATE.h | 37 ----------------------- 1 file changed, 37 deletions(-) delete mode 100644 include/ITS.TODO-TEMPLATE/TODO-TEMPLATE.h diff --git a/include/ITS.TODO-TEMPLATE/TODO-TEMPLATE.h b/include/ITS.TODO-TEMPLATE/TODO-TEMPLATE.h deleted file mode 100644 index 20491a5..0000000 --- a/include/ITS.TODO-TEMPLATE/TODO-TEMPLATE.h +++ /dev/null @@ -1,37 +0,0 @@ -/** @file TODO-TEMPLATE.h - * Interface header for this library - * // TODO-TEMPLATE: Rename this file to your ${LIB_NAME} - */ -#pragma once - -// TODO-TEMPLATE add other local includes here -#include "ReturnCodes.h" - -namespace ITS { -// TODO-TEMPLATE: Use your library's namespace - -// Define cross-platform EXPORTED -#ifndef DOXYGEN_SHOULD_SKIP - #ifdef _WIN32 - #define EXPORTED extern "C" __declspec(dllexport) - #else - #define EXPORTED extern "C" - #endif -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Constants -// TODO-TEMPLATE define any global constants here (use constexpr!) - -//////////////////////////////////////////////////////////////////////////////// -// Public Functions -// TODO-TEMPLATE: Add functions which should be exported in the DLL -EXPORTED char *GetReturnStatusCharArray(const int code); -EXPORTED void FreeReturnStatusCharArray(char *c_msg); - -//////////////////////////////////////////////////////////////////////////////// -// Private Functions -// TODO-TEMPLATE: Add other/internal functions here (no need for "EXPORTED") -std::string GetReturnStatus(const int code); - -} // namespace ITS From e59813c6ed0366bf5b49427e53455787147c7acf Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:02:21 -0400 Subject: [PATCH 185/379] Update formatting --- src/ReturnCodes.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ReturnCodes.cpp b/src/ReturnCodes.cpp index 8ea03cf..c1a3590 100644 --- a/src/ReturnCodes.cpp +++ b/src/ReturnCodes.cpp @@ -13,6 +13,7 @@ #define __STDC_WANT_LIB_EXT1__ 1 #endif #endif + #include // for strcpy_s #include // for std::string #include // for std::unordered_map From 103b771fdf6e250a24ec5a65e246ba904c83057d Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:04:53 -0400 Subject: [PATCH 186/379] Fix strcpy on POSIX --- src/ReturnCodes.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ReturnCodes.cpp b/src/ReturnCodes.cpp index 08168f8..f43ee05 100644 --- a/src/ReturnCodes.cpp +++ b/src/ReturnCodes.cpp @@ -70,7 +70,11 @@ std::string GetReturnStatus(const int code) { char *GetReturnStatusCharArray(const int code) { std::string msg = GetReturnStatus(code); char *c_msg = new char[msg.size() + 1]; +#ifndef _WIN32 strcpy_s(c_msg, msg.size() + 1, msg.c_str()); +#else + strcpy(c_msg, msg.c_str()); +#endif return c_msg; } From 26ab33b5244c9f7a0a522376c51c3921aff820e2 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:10:01 -0400 Subject: [PATCH 187/379] Add const to string to convert --- src/ReturnCodes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ReturnCodes.cpp b/src/ReturnCodes.cpp index f43ee05..b367ead 100644 --- a/src/ReturnCodes.cpp +++ b/src/ReturnCodes.cpp @@ -68,7 +68,7 @@ std::string GetReturnStatus(const int code) { * @return A status message corresponding to the input code. ******************************************************************************/ char *GetReturnStatusCharArray(const int code) { - std::string msg = GetReturnStatus(code); + const std::string msg = GetReturnStatus(code); char *c_msg = new char[msg.size() + 1]; #ifndef _WIN32 strcpy_s(c_msg, msg.size() + 1, msg.c_str()); From e39e0026d100e99aa487c7491146222ff922a7c1 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:10:57 -0400 Subject: [PATCH 188/379] Improve safety of return status function --- src/ReturnCodes.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ReturnCodes.cpp b/src/ReturnCodes.cpp index c1a3590..4f02c47 100644 --- a/src/ReturnCodes.cpp +++ b/src/ReturnCodes.cpp @@ -56,9 +56,13 @@ std::string GetReturnStatus(int code) { * @return A status message corresponding to the input code. ******************************************************************************/ char *GetReturnStatusCharArray(const int code) { - std::string msg = GetReturnStatus(code); + const std::string msg = GetReturnStatus(code); char *c_msg = new char[msg.size() + 1]; +#ifdef _WIN32 strcpy_s(c_msg, msg.size() + 1, msg.c_str()); +#else + strcpy(c_msg, msg.c_str()); +#endif return c_msg; } From f74a78782f06eed5438553211ff3e65e5e3f21ed Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 14:12:37 -0400 Subject: [PATCH 189/379] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 7bf54fb..5adefc6 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ 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) +* [P2108 Wiki Page](https://ntia.github.io/propagation-library-wiki/models/P2108) * [`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) @@ -90,5 +91,3 @@ contains an extensive set of example values which are useful as validation cases ## Contact ## For technical questions, contact . - ---> From 9359454faaedcd6de9eafc5ca9a995a00e6c5c95 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 17:32:58 -0400 Subject: [PATCH 190/379] Set library version to 2-digit format --- .zenodo.json | 2 +- CMakeLists.txt | 2 +- app/CMakeLists.txt | 2 +- app/data/o_asm.txt | 8 ++++---- app/data/o_hgtcm.txt | 8 ++++---- app/data/o_tsm.txt | 10 +++++----- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.zenodo.json b/.zenodo.json index e1256ec..db16d5e 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -14,6 +14,6 @@ "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", + "version": "1.0", "keywords": ["Study Group 3", "ITU-R", "P2108", "clutter", "propagation"] } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 63f8107..87c6fa5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ 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 + VERSION 1.0 DESCRIPTION "Recommendation ITU-R P.2108, U.S. Reference Implementation" HOMEPAGE_URL "https://ntia.github.io/propagation-library-wiki/models/P2108" LANGUAGES "CXX" diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 9458d46..4fdacdc 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -2,7 +2,7 @@ ## CONFIGURE THE COMMAND LINE DRIVER ########################################### set(DRIVER_NAME "${LIB_NAME}Driver") -set(DRIVER_VERSION ${PROJECT_VERSION}) # May be set manually instead +set(DRIVER_VERSION ${PROJECT_VERSION}.0) set(DRIVER_HEADERS "${PROJECT_SOURCE_DIR}/app/include") set(DRIVER_EXE_DIR "${PROJECT_SOURCE_DIR}/bin") diff --git a/app/data/o_asm.txt b/app/data/o_asm.txt index cd771cf..f93be43 100644 --- a/app/data/o_asm.txt +++ b/app/data/o_asm.txt @@ -1,9 +1,9 @@ Model P2108 Model Variant Aeronautical Statistical Model -Library Version v1.0.0 +Library Version v1.0 Driver Version v1.0.0 -Date Generated Fri Oct 11 15:51:57 2024 -Input Arguments -i .\i_asm.txt -o .\o_asm.txt -model ASM +Date Generated Thu Oct 31 17:30:41 2024 +Input Arguments -i .\i_asm.txt -model ASM -o .\o_asm.txt Inputs f__ghz 10 (gigahertz) @@ -11,5 +11,5 @@ theta__deg 10.5 (degrees) p 45 (%) Results -Return Code 0 [Success - No Errors] +Return Code 0 [P2108 Status: Successful execution] Clutter loss 12.4 (dB) \ No newline at end of file diff --git a/app/data/o_hgtcm.txt b/app/data/o_hgtcm.txt index e504dbe..70f3fe7 100644 --- a/app/data/o_hgtcm.txt +++ b/app/data/o_hgtcm.txt @@ -1,9 +1,9 @@ Model P2108 Model Variant Height Gain Terminal Correction Model -Library Version v1.0.0 +Library Version v1.0 Driver Version v1.0.0 -Date Generated Fri Oct 11 15:54:02 2024 -Input Arguments -i .\i_hgtcm.txt -o .\o_hgtcm.txt -model HGTCM +Date Generated Thu Oct 31 17:31:08 2024 +Input Arguments -i .\i_hgtcm.txt -model HGTCM -o .\o_hgtcm.txt Inputs f__ghz 1.5 (gigahertz) @@ -13,5 +13,5 @@ r__meter 15 (meters) clutter_type 4 [Urban clutter type] Results -Return Code 0 [Success - No Errors] +Return Code 0 [P2108 Status: Successful execution] Clutter loss 24.5 (dB) \ No newline at end of file diff --git a/app/data/o_tsm.txt b/app/data/o_tsm.txt index 3d18956..8fe530c 100644 --- a/app/data/o_tsm.txt +++ b/app/data/o_tsm.txt @@ -1,15 +1,15 @@ Model P2108 Model Variant Terrestrial Statistical Model -Library Version v1.0.0 +Library Version v1.0 Driver Version v1.0.0 -Date Generated Fri Oct 11 15:54:26 2024 -Input Arguments -i .\i_tsm.txt -o .\o_tsm.txt -model TSM +Date Generated Thu Oct 31 17:30:55 2024 +Input Arguments -i .\i_tsm.txt -model TSM -o .\o_tsm.txt Inputs f__ghz 26.6 (gigahertz) -theta__deg 15.8 (kilometers) +d__km 15.8 (kilometers) p 45 (%) Results -Return Code 0 [Success - No Errors] +Return Code 0 [P2108 Status: Successful execution] Clutter loss 32.5 (dB) \ No newline at end of file From d3575b363b0a32628f0c2272b8adbe77dd236e22 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 17:33:38 -0400 Subject: [PATCH 191/379] Use 2-digit library version --- CMakeLists.txt | 2 +- app/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d038808..f92cb33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ set(LIB_NAME "TODO-TEMPLATE: YOUR-LIBRARY") # Name of library/target set(LIB_NAMESPACE "TODO-TEMPLATE: ITS.YOUR-NAMESPACE") # Namespace for the named library project( "${LIB_NAMESPACE}.${LIB_NAME}" - VERSION 1.0.0 + VERSION 1.0 DESCRIPTION "TODO-TEMPLATE: BRIEF DESCRIPTION OF YOUR SOFTWARE" HOMEPAGE_URL "TODO-TEMPLATE: HOMEPAGE LINK, E.G. TO RELEVANT PAGE ON PROPLIB WIKI" LANGUAGES "CXX" diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index fb3cd29..780d972 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -2,7 +2,7 @@ ## CONFIGURE THE COMMAND LINE DRIVER ########################################### set(DRIVER_NAME "${LIB_NAME}Driver") -set(DRIVER_VERSION ${PROJECT_VERSION}) # May be set manually instead +set(DRIVER_VERSION ${PROJECT_VERSION}.0) set(DRIVER_HEADERS "${PROJECT_SOURCE_DIR}/app/include") ########################################### From 2338b79e7edcd27feb09e3c999c5e9bc0bcd18ba Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 17:43:36 -0400 Subject: [PATCH 192/379] Include version in DLL name --- src/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b66eb2c..67e632c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,7 +33,8 @@ endif () set_target_properties( ${LIB_NAME} PROPERTIES VERSION ${PROJECT_VERSION} - SOVERSION ${PROJECT_VERSION_MAJOR} # Include version in .so output filenames + SOVERSION ${PROJECT_VERSION} # Include version in .so output filenames + OUTPUT_NAME ${LIB_NAME}-${PROJECT_VERSION} LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" From 10ae7522149f6bcf0c0f564be58e442672210849 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 17:47:07 -0400 Subject: [PATCH 193/379] include version number in DLL name --- src/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f4f8926..8cbce24 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -29,7 +29,8 @@ endif () set_target_properties( ${LIB_NAME} PROPERTIES VERSION ${PROJECT_VERSION} - SOVERSION ${PROJECT_VERSION_MAJOR} # Include version in .so output filenames + SOVERSION ${PROJECT_VERSION} # Include version in .so output filenames + OUTPUT_NAME ${PROJECT_VERSION} LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" From 3f1bc5a89264ea078e70577dc50e330cb250cb56 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 17:47:55 -0400 Subject: [PATCH 194/379] fix output_name --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8cbce24..2f9e434 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,7 +30,7 @@ set_target_properties( ${LIB_NAME} PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION} # Include version in .so output filenames - OUTPUT_NAME ${PROJECT_VERSION} + OUTPUT_NAME ${LIB_NAME}-${PROJECT_VERSION} LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" From 2edece44890caaeea02e34ded78a61883350c7b2 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 17:57:13 -0400 Subject: [PATCH 195/379] add version to driver output name --- app/src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index f1c4b2d..2c01c27 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -31,7 +31,7 @@ add_definitions(-DDRIVER_NAME="${DRIVER_NAME}") set_target_properties( ${DRIVER_NAME} PROPERTIES VERSION ${PROJECT_VERSION} - SOVERSION ${PROJECT_VERSION_MAJOR} # Include version in .so output filenames + OUTPUT_NAME ${DRIVER_NAME}-${DRIVER_VERSION} LIBRARY_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} ARCHIVE_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} RUNTIME_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} From be63f6a1e9c32fdc1e7a61d1fb05f5becdfffea0 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 18:04:12 -0400 Subject: [PATCH 196/379] use only major version for SOVERSION --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2f9e434..c8eb333 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -29,7 +29,7 @@ endif () set_target_properties( ${LIB_NAME} PROPERTIES VERSION ${PROJECT_VERSION} - SOVERSION ${PROJECT_VERSION} # Include version in .so output filenames + SOVERSION ${PROJECT_VERSION_MAJOR} # Include version in .so output filenames OUTPUT_NAME ${LIB_NAME}-${PROJECT_VERSION} LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" From aa52c137c7c29c823d823c8eb36eaa52d303aed2 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 18:07:10 -0400 Subject: [PATCH 197/379] make filenames consistent across platforms --- app/src/CMakeLists.txt | 1 - src/CMakeLists.txt | 2 -- 2 files changed, 3 deletions(-) diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index 2c01c27..49a9a91 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -30,7 +30,6 @@ add_definitions(-DDRIVER_NAME="${DRIVER_NAME}") # Set some target metadata set_target_properties( ${DRIVER_NAME} PROPERTIES - VERSION ${PROJECT_VERSION} OUTPUT_NAME ${DRIVER_NAME}-${DRIVER_VERSION} LIBRARY_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} ARCHIVE_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c8eb333..0c425b9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,8 +28,6 @@ endif () # Set some target metadata set_target_properties( ${LIB_NAME} PROPERTIES - VERSION ${PROJECT_VERSION} - SOVERSION ${PROJECT_VERSION_MAJOR} # Include version in .so output filenames OUTPUT_NAME ${LIB_NAME}-${PROJECT_VERSION} LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" From b3553fc0e79019a8cf0c934c440f0684a0ba6427 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 18:17:10 -0400 Subject: [PATCH 198/379] make output file names consistent across platforms --- src/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0c425b9..56ef51e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,6 +24,10 @@ target_compile_features(${LIB_NAME} PUBLIC cxx_std_11) if (WIN32) set_target_properties(${LIB_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS true) endif () +if (UNIX) + # avoid prefixing "lib" to the output file, for cross-platform consistency + set(CMAKE_SHARED_LIBRARY_PREFIX "") +endif () # Set some target metadata set_target_properties( From 36884a06de7f9d8f2b6a38ea088edef758bbcda7 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 31 Oct 2024 18:25:09 -0400 Subject: [PATCH 199/379] correctly export functions across platforms --- src/ReturnCodes.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ReturnCodes.cpp b/src/ReturnCodes.cpp index 4f02c47..9332930 100644 --- a/src/ReturnCodes.cpp +++ b/src/ReturnCodes.cpp @@ -2,7 +2,7 @@ * Maps status messages to library return codes */ -#include "ITS.TODO-TEMPLATE/ReturnCodes.h" +#include "ITS.TODO-TEMPLATE/TODO-TEMPLATE.h" #ifdef _WIN32 // Ensure strcpy_s is available on Windows @@ -55,7 +55,7 @@ std::string GetReturnStatus(int code) { * @param[in] code Integer return code. * @return A status message corresponding to the input code. ******************************************************************************/ -char *GetReturnStatusCharArray(const int code) { +EXPORTED char *GetReturnStatusCharArray(const int code) { const std::string msg = GetReturnStatus(code); char *c_msg = new char[msg.size() + 1]; #ifdef _WIN32 @@ -71,7 +71,7 @@ char *GetReturnStatusCharArray(const int code) { * * @param[in] c_msg The status message C-style string to delete ******************************************************************************/ -void FreeReturnStatusCharArray(char *c_msg) { +EXPORTED void FreeReturnStatusCharArray(char *c_msg) { delete[] c_msg; } From 6166224f952bae7a01da13dec756fae46a9d75f4 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 1 Nov 2024 16:57:23 -0400 Subject: [PATCH 200/379] Update CONTRIBUTING.md --- CONTRIBUTING.md | 159 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 121 insertions(+), 38 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fc62afa..65f6843 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,6 +17,7 @@ If you are instead interested in usage documentation, please refer to the - [Notes on Code Style](#notes-on-code-style) - [Project Structure and CMake](#project-structure-and-cmake) - [Documenting Code](#documenting-code) +- [Testing Code](#testing-code) ## Found a Bug? @@ -40,10 +41,18 @@ resources to help you get started with open source contributions: - [Basic explanation of Git submodules](https://gist.github.com/gitaarik/8735255) by [**@gitaarik**](https://github.com/gitaarik) +### Git Branches + +Our repositories use the following approach to organize and keep track of branches. +The `main` branch typically represents the most recently released version of the software. +The `dev` branch stages changes before they are merged into `main` and a new release is created. +New features or bug fixes should be developed on individual "feature branches" with descriptive names. +When complete, features branches should merge into `dev`. + ### 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, +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). @@ -69,17 +78,22 @@ or [using the command line](https://docs.github.com/en/github/getting-started-wi 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, +1. When you're done making changes, create a pull request (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. +Once you submit your PR, a maintainer will review your changes to determine +whether or not they should be merged. We may ask questions or request additional +changes which must be addressed. For example, we may request changes so that the code +meets structure, formatting, accuracy, or testing requirements. + +If your PR is approved and merged, your changes will be a part of the `dev` +branch of the repository, where they will stay until a new release is made. At that +point, `dev` will merge into `main` and a new release will be created. The maintainers +of a repository hold the authority on when a new release should be created. For example, +important bug fixes may take higher priority, while small improvements may stay on `dev` +for a while. Rest assured, even if a new release is not immediately made, your approved +changes will be always packaged into the next release. ## Notes on Code Style @@ -104,7 +118,9 @@ It is recommended to use this tool to autoformat Python code when checked in. 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 +handle cross-platform C++ build configuration, C++ unit tests (with +[GoogleTest](https://github.com/google/googletest) and +[CTest](https://cmake.org/cmake/help/latest/manual/ctest.1.html)), 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. @@ -113,8 +129,16 @@ 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 +app/ # The command-line driver which can run the library + data/ # Example input and output files for use with the driver + include/ # Headers used by the command-line driver + src/ # Source code for the command-line driver + tests/ # Header and source files for testing the command-line driver + CMakeLists.txt # Configuration for the command-line driver and its tests + README.md # Usage information for the command-line driver docs/ CMakeLists.txt # Doxygen configuration + ... # Static files (images, HTML, CS, Markdown) used by Doxygen extern/ ... # External Git submodules/dependencies include/ @@ -128,7 +152,7 @@ tests/ .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. + CMakeLists.txt # CTest+GTest config. Files containing tests must be included here. wrap/ dotnet/ # C#/.NET wrapper submodule. Should contain CMakeLists.txt matlab/ # MATLAB wrapper submodule. Should contain CMakeLists.txt @@ -138,34 +162,62 @@ CMakePresets.json # Presets for CMake, e.g. "release", "debug", etc. ... ``` +### CMake Options and CMake Presets + 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: +example, the `tests/CMakeLists.txt` file configures unit tests using CMake. The +top-level `CMakeLists.txt` stores the primary project configuration and includes +the lower-level configurations based on the preset or specified CMake options. + +The following CMake options are used for top-level project configuration: + +| Option | Default | Definition | +|--------------------|---------|----------------------|--------------------| +| `BUILD_DOCS` | `ON` | Generate documentation site with Doxygen | +| `BUILD_DRIVER` | `ON` | Build the command-line driver executable | +| `RUN_DRIVER_TESTS` | `ON` | Test the command-line driver executable | +| `DOCS_ONLY` | `OFF` | Skip all steps _except_ generating the documentation site | +| `RUN_TESTS` | `ON` | Run unit tests for the main library | +| `COPY_TO_WRAPPERS` | `ON` | Copy the compiled shared library into wrapper submodules | + +[CMake Presets](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html) are +provided to support common build configurations. These are specified in the +`CMakePresets.json` file. The `release` preset will compile the library and driver +with optimizations, build the documentation site, and run all unit tests. The `debug` preset +will skip building the documentation site, driver, and driver tests, which can be useful for +rapid development and testing. Additionally, the Debug configuration will attempt to pass +debug flags to the compiler. Finally, the "docsOnly" preset skips all steps except for +generating the Doxygen documentation site. + +| Option | `release` preset | `debug` preset | `docsOnly` preset | +|--------------------|------------------|----------------|-------------------| +| `DOCS_ONLY` | `OFF` | `OFF` | `ON` | +| `RUN_TESTS` | `ON` | `ON` | `OFF` | +| `CMAKE_BUILD_TYPE` | `Release` | `Debug` | not set | +| `BUILD_DOCS` | `ON` | `OFF` | `ON` | +| `BUILD_DRIVER` | `ON` | `OFF` | `OFF` | +| `RUN_DRIVER_TESTS` | `ON` | `OFF` | `OFF` | + +Below are some examples of how CMake can be called to compile this software. ```bash -cmake -S . -B build -cmake --build build -``` +# Configure and compile in release configuration +cmake --preset release +cmake --build --preset release -After compiling the library, you can run unit tests as follows. First, change your -working directory to the `build` directory, then run: +# Use the release configuration but don't build Doxygen docs +cmake --preset release -DBUILD_DOCS=OFF +cmake --build --preset release -```bash -ctest -``` +# Configure and compile in debug configuration +cmake --preset debug +cmake --build --preset debug -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. +# Use the release configuration but don't run driver tests +cmake --preset release -DRUN_DRIVER_TESTS=OFF +cmake --build --preset release +``` ### Supported Platforms and Build Options @@ -187,7 +239,8 @@ 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. +example showing the expected documentation formats. Except for inline documentation, +use the JavaDoc banner style [described by Doxygen](https://www.doxygen.nl/manual/docblocks.html) ```cpp constexpr double = PI 3.1415; /**< Inline format, e.g. for constants */ @@ -197,7 +250,8 @@ constexpr double = PI 3.1415; /**< Inline format, e.g. for constants */ * * 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$. + * returns a value @f$ y @f$ with @f$ y = 2x @f$. This whole documentation block + * is using the JavaDoc banner style! * * @param[in] x The input and its expected units * @return The result @f$ y = 2x @f$ @@ -208,6 +262,20 @@ double doubleTheInput(double x) } ``` +### Doxygen for C++ Libraries + +The base C++ libraries include Doxygen configurations which generate static +websites from code comments. These documentation sites are published as developer +reference documentation using GitHub Pages. When building the Doxygen site locally, +The site is generated in `docs/html/` and the main page can be accessed at `docs/html/index.html`. +When new releases are made, GitHub Actions workflows are triggered which build and deploy +the Doxygen site to GitHub Pages. + +### MATLAB Wrappers + +Most code in the MATLAB wrapper is actually written in C. In these files, the same +documentation style as noted above for C++ should be used. + ### Python Wrappers The Python wrapper code is documented in the [Sphinx](https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html) @@ -236,8 +304,9 @@ def double_the_input(x: float) -> float: ### 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 +In C#/.NET, documentation comments are written in +[XML format](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/documentation-comments) +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. @@ -265,3 +334,17 @@ public class CalculationUtils } } ``` + +## Testing Code + +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`. + +After compiling the library, you can run unit tests as follows. First, change your +working directory to the `build` directory, then run: + +```bash +ctest +``` From 9973e14501ebdffe1b7e1f528ccac61de8e59944 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 1 Nov 2024 16:59:42 -0400 Subject: [PATCH 201/379] fix migration mistakes Missed some things when copying changes into this repository. This commit fixes that. --- app/CMakeLists.txt | 1 + app/src/Driver.cpp | 19 ++++++++++--------- app/tests/TestDriver.h | 24 +++++++++++++++++++++--- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 780d972..4fdacdc 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -4,6 +4,7 @@ set(DRIVER_NAME "${LIB_NAME}Driver") set(DRIVER_VERSION ${PROJECT_VERSION}.0) set(DRIVER_HEADERS "${PROJECT_SOURCE_DIR}/app/include") +set(DRIVER_EXE_DIR "${PROJECT_SOURCE_DIR}/bin") ########################################### ## BUILD THE COMMAND LINE DRIVER diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index 64e9754..91ab35e 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -63,7 +63,7 @@ int main(int argc, char **argv) { * @param[out] params Structure with user input params * @return Return code ******************************************************************************/ -int ParseArguments(int argc, char **argv, DrvrParams ¶ms) { +DrvrReturnCode ParseArguments(int argc, char **argv, DrvrParams ¶ms) { // TODO-TEMPLATE: Populate vector with all valid arguments const std::vector validArgs = {"-i", "-o", "-h", "--help", "-v", "--version"}; @@ -111,7 +111,7 @@ int ParseArguments(int argc, char **argv, DrvrParams ¶ms) { } } - return SUCCESS; + return DRVR__SUCCESS; } /******************************************************************************* @@ -148,17 +148,18 @@ void Help(std::ostream &os) { * @param[in] params Structure with user input parameters * @return Return code ******************************************************************************/ -int ValidateInputs(const DrvrParams ¶ms) { +DrvrReturnCode ValidateInputs(const DrvrParams ¶ms) { DrvrParams not_set; + DrvrReturnCode rtn = DRVR__SUCCESS; // TODO-TEMPLATE: Check that required inputs were provided. // This template code checks that input/output files were given with -i and -o if (params.in_file == not_set.in_file) - return Validate_RequiredErrMsgHelper("-i", DRVRERR__VALIDATION_IN_FILE); - + rtn = DRVRERR__VALIDATION_IN_FILE; if (params.out_file == not_set.out_file) - return Validate_RequiredErrMsgHelper( - "-o", DRVRERR__VALIDATION_OUT_FILE - ); + rtn = DRVRERR__VALIDATION_OUT_FILE; + + if (rtn != DRVR__SUCCESS) + std::cerr << GetDrvrReturnStatus(rtn) << std::endl; - return SUCCESS; + return rtn; } diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index df27a85..6760190 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -91,6 +91,25 @@ class DriverTest: public ::testing::Test { return command; } + /*********************************************************************** + * Runs the provided command (cross-platform) + * + * Note that on POSIX platforms the exit code of the command should be + * between 0 and 255. Exit codes outside this range will be shifted into + * this range and cannot be unambiguously compared to expectations. + * + * @param[in] cmd The command to run + * @return The exit code of the command. + **********************************************************************/ + int RunCommand(const std::string &cmd) { + std::cout << std::flush; + int rtn = std::system(cmd.c_str()); +#ifndef _WIN32 + rtn = WEXITSTATUS(rtn); // Get child process exit code on POSIX +#endif + return rtn; + } + /*********************************************************************** * Runs the driver executable. * @@ -99,10 +118,9 @@ class DriverTest: public ::testing::Test { **********************************************************************/ int RunDriver(const DrvrParams ¶ms) { std::string cmd = BuildCommand(params); - int rtn = std::system(cmd.c_str()); - return rtn; + return RunCommand(cmd); } - + /*********************************************************************** * Runs the driver using the specified input file contents. * From 1c31ce3f05b015a240bc68a40442db426b8a5d30 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 1 Nov 2024 16:59:51 -0400 Subject: [PATCH 202/379] formatting --- app/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/README.md b/app/README.md index a8ec020..e314a4e 100644 --- a/app/README.md +++ b/app/README.md @@ -24,6 +24,7 @@ Executing the command-line driver requires specifying input arguments, defined in the below table: (TODO-TEMPLATE review and update the flags below) + | Flag | Type | Required | Description | |--------|--------|----------|-----------------------------------------------------------------------| | `-i` | string | true | File specifying model input parameters in `key,value` format | @@ -41,6 +42,7 @@ Input arguments are not case sensitive and do not have to be specified in a cert order. A generic example of calling the command-line driver on Windows is: (TODO-TEMPLATE: update example driver command below) + ```cmd .exe -i -dbg -o ``` @@ -52,6 +54,7 @@ Using these input files and the commands specified should produce outputs identi to the provided corresponding output files. (TODO-TEMPLATE: Provide all included examples in the table below) + | Input File | Terrain File | Output File | Arguments | |---------------------------|-----------------------------------------------------|-----------------------------|----------------------------------------------------| | [`in.txt`](./data/in.txt) | [`terrain_profile.txt`](./data/terrain_profile.txt) | [`out.txt`](./data/out.txt) | `-i in.txt -t terrain_profile.txt -o out.txt -dbg` | From 6e2f823618efb4136fcaf4f3369b2597913c7959 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 1 Nov 2024 17:00:11 -0400 Subject: [PATCH 203/379] Enable compiler warnings with cmake also fix a typo in src/CMakeLists --- app/src/CMakeLists.txt | 7 +++++++ src/CMakeLists.txt | 9 ++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index 49a9a91..2f92491 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -35,3 +35,10 @@ set_target_properties( ARCHIVE_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} RUNTIME_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} ) + +# Enable compiler warnings for common compilers +if(MSVC) + target_compile_options(${DRIVER_NAME} PRIVATE /W4 /WX) +else() + target_compile_options(${DRIVER_NAME} PRIVATE -Wall -Wextra -Wpedantic -Werror) +endif() \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 56ef51e..6771685 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,7 +7,7 @@ set(PROJECT_HEADERS "${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}") add_library( ${LIB_NAME} SHARED "ReturnCodes.cpp" - "{PROJECT_HEADERS}/ReturnCodes.h" + "${PROJECT_HEADERS}/ReturnCodes.h" "${PROJECT_HEADERS}/${LIB_NAME}.h" ) @@ -42,4 +42,11 @@ set_target_properties( if (POLICY CMP0141) cmake_policy(SET CMP0141 NEW) set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") +endif() + +# Enable compiler warnings for common compilers +if(MSVC) + target_compile_options(${LIB_NAME} PRIVATE /W4 /WX) +else() + target_compile_options(${LIB_NAME} PRIVATE -Wall -Wextra -Wpedantic -Werror) endif() \ No newline at end of file From 67d6493303a44484cf1cbd4e784a90de0fa1b2ca Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 1 Nov 2024 17:00:54 -0400 Subject: [PATCH 204/379] Change template namespace This lets the template project successfully configure with CMake, which will be helpful for catching problems in the future. --- CMakeLists.txt | 16 ++++++++-------- app/include/Driver.h | 4 ++-- .../PropLibTemplate.h} | 2 +- .../ReturnCodes.h | 0 src/ReturnCodes.cpp | 3 ++- 5 files changed, 13 insertions(+), 12 deletions(-) rename include/{ITS.TODO-TEMPLATE/TODO-TEMPLATE.h => ITS.PropLibTemplate/PropLibTemplate.h} (97%) rename include/{ITS.TODO-TEMPLATE => ITS.PropLibTemplate}/ReturnCodes.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index f92cb33..23f5b92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,8 +9,8 @@ cmake_minimum_required(VERSION 3.14 FATAL_ERROR) ## PROJECT METADATA ########################################### # TODO-TEMPLATE: Add project metadata here. See CREATING-REPOSITORIES.md for an example. -set(LIB_NAME "TODO-TEMPLATE: YOUR-LIBRARY") # Name of library/target -set(LIB_NAMESPACE "TODO-TEMPLATE: ITS.YOUR-NAMESPACE") # Namespace for the named library +set(LIB_NAME "PropLibTemplate") # Name of library/target +set(LIB_NAMESPACE "ITS") # Namespace for the named library project( "${LIB_NAMESPACE}.${LIB_NAME}" VERSION 1.0 @@ -30,12 +30,12 @@ set(PYTHON_WRAPPER_DIR "${PROJECT_SOURCE_DIR}/wrap/python") ## CMAKE OPTIONS AND DEFAULTS ########################################### # Define options. Defaults to: compile 64-bit library and driver, build docs, run tests -option(BUILD_DOCS "Generate documentation with Doxygen" ON) -option(BUILD_DRIVER "Build driver executable" ON) -option(RUN_DRIVER_TESTS "Test the driver executable" 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_DOCS "Generate documentation site with Doxygen" ON) +option(BUILD_DRIVER "Build the command-line driver executable" ON) +option(RUN_DRIVER_TESTS "Test the command-line driver executable" ON) +option(DOCS_ONLY "Skip all steps except generating the documentation site" OFF) +option(RUN_TESTS "Run unit tests for the main library" ON) +option(COPY_TO_WRAPPERS "Copy the compiled shared library into wrapper submodules" ON) ########################################### ## SETUP diff --git a/app/include/Driver.h b/app/include/Driver.h index c4f55a9..556a75d 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -8,7 +8,7 @@ #include "Structs.h" // TODO-TEMPLATE: Include your library's main interface header -// #include "ITS./.h" +#include "ITS.PropLibTemplate/PropLibTemplate.h" #include // for std::ofstream #include // for std::left, std::setw @@ -27,7 +27,7 @@ ////////////////////////////// // Library Namespace // TODO-TEMPLATE: use the namespace of your library -// using namespace ITS::YourLibraryNamespace::YourLibraryName; +using namespace ITS; ///////////////////////////// // Functions diff --git a/include/ITS.TODO-TEMPLATE/TODO-TEMPLATE.h b/include/ITS.PropLibTemplate/PropLibTemplate.h similarity index 97% rename from include/ITS.TODO-TEMPLATE/TODO-TEMPLATE.h rename to include/ITS.PropLibTemplate/PropLibTemplate.h index 20491a5..3335260 100644 --- a/include/ITS.TODO-TEMPLATE/TODO-TEMPLATE.h +++ b/include/ITS.PropLibTemplate/PropLibTemplate.h @@ -1,4 +1,4 @@ -/** @file TODO-TEMPLATE.h +/** @file PropLibTemplate.h * Interface header for this library * // TODO-TEMPLATE: Rename this file to your ${LIB_NAME} */ diff --git a/include/ITS.TODO-TEMPLATE/ReturnCodes.h b/include/ITS.PropLibTemplate/ReturnCodes.h similarity index 100% rename from include/ITS.TODO-TEMPLATE/ReturnCodes.h rename to include/ITS.PropLibTemplate/ReturnCodes.h diff --git a/src/ReturnCodes.cpp b/src/ReturnCodes.cpp index 9332930..6dd8efa 100644 --- a/src/ReturnCodes.cpp +++ b/src/ReturnCodes.cpp @@ -2,7 +2,8 @@ * Maps status messages to library return codes */ -#include "ITS.TODO-TEMPLATE/TODO-TEMPLATE.h" +// TODO-TEMPLATE include your primary library header +#include "ITS.PropLibTemplate/PropLibTemplate.h" #ifdef _WIN32 // Ensure strcpy_s is available on Windows From 245b8ddcf8125b5d497631dc145cf2ebf086b60e Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 1 Nov 2024 17:01:01 -0400 Subject: [PATCH 205/379] Update docsOnly preset --- CMakePresets.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index 7b456bf..0fdfc3a 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -56,12 +56,14 @@ { "name": "docsOnly", "displayName": "Doxygen only", - "description": "Do not build the library; only build Doxygen docs.", + "description": "Do not build or test the library or driver; only build Doxygen docs.", "inherits": "proplib-config-base", "cacheVariables": { "BUILD_DOCS": "ON", "DOCS_ONLY": "ON", - "RUN_TESTS": "OFF" + "RUN_TESTS": "OFF", + "BUILD_DRIVER": "OFF", + "RUN_DRIVER_TESTS": "OFF" } } ], From 3b19af175d825b9287defabbc15004f12c8527c7 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 1 Nov 2024 17:07:20 -0400 Subject: [PATCH 206/379] clang-format all files --- app/include/CommaSeparatedIterator.h | 6 +++--- app/src/DriverUtils.cpp | 14 +++++++------- app/tests/TestDriver.h | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/include/CommaSeparatedIterator.h b/app/include/CommaSeparatedIterator.h index cc0b0a7..a83a0ac 100644 --- a/app/include/CommaSeparatedIterator.h +++ b/app/include/CommaSeparatedIterator.h @@ -3,9 +3,9 @@ */ #pragma once -#include // for std::istream -#include // For std::string -#include // for std::pair +#include // for std::istream +#include // For std::string +#include // for std::pair /******************************************************************************* * @class CommaSeparatedIterator diff --git a/app/src/DriverUtils.cpp b/app/src/DriverUtils.cpp index b3ca958..7e73339 100644 --- a/app/src/DriverUtils.cpp +++ b/app/src/DriverUtils.cpp @@ -13,13 +13,13 @@ #endif #endif -#include // for std::transform -#include // for std::tolower -#include // for localtime_{s,r}, std::{time, time_t, tm, strftime} -#include // for std::ofstream -#include // for std::setfill, std::setw -#include // for std::cerr, std::endl -#include // for std::stod, std::stoi, std::string +#include // for std::transform +#include // for std::tolower +#include // for localtime_{s,r}, std::{time, time_t, tm, strftime} +#include // for std::ofstream +#include // for std::setfill, std::setw +#include // for std::cerr, std::endl +#include // for std::stod, std::stoi, std::string /****************************************************************************** * Get a string containing the current date and time information. diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index 6760190..a1daf12 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -120,7 +120,7 @@ class DriverTest: public ::testing::Test { std::string cmd = BuildCommand(params); return RunCommand(cmd); } - + /*********************************************************************** * Runs the driver using the specified input file contents. * From f732c7342daf4c049c9e4ed7d85ebed741d5a9d0 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 1 Nov 2024 17:26:07 -0400 Subject: [PATCH 207/379] Fix table formatting --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 65f6843..5f75e90 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -172,13 +172,13 @@ the lower-level configurations based on the preset or specified CMake options. The following CMake options are used for top-level project configuration: -| Option | Default | Definition | -|--------------------|---------|----------------------|--------------------| +| Option | Default | Definition | +|--------------------|---------|------------------------------------------| | `BUILD_DOCS` | `ON` | Generate documentation site with Doxygen | | `BUILD_DRIVER` | `ON` | Build the command-line driver executable | | `RUN_DRIVER_TESTS` | `ON` | Test the command-line driver executable | | `DOCS_ONLY` | `OFF` | Skip all steps _except_ generating the documentation site | -| `RUN_TESTS` | `ON` | Run unit tests for the main library | +| `RUN_TESTS` | `ON` | Run unit tests for the main library | | `COPY_TO_WRAPPERS` | `ON` | Copy the compiled shared library into wrapper submodules | [CMake Presets](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html) are From a9a28a13fbfb8599fa7566bf38ba1ffcd314716a Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 1 Nov 2024 17:56:39 -0400 Subject: [PATCH 208/379] Let developers handle compiler warning settings This complicates CI --- app/src/CMakeLists.txt | 7 ------- src/CMakeLists.txt | 7 ------- 2 files changed, 14 deletions(-) diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index 2f92491..49a9a91 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -35,10 +35,3 @@ set_target_properties( ARCHIVE_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} RUNTIME_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} ) - -# Enable compiler warnings for common compilers -if(MSVC) - target_compile_options(${DRIVER_NAME} PRIVATE /W4 /WX) -else() - target_compile_options(${DRIVER_NAME} PRIVATE -Wall -Wextra -Wpedantic -Werror) -endif() \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6771685..9e7ca6b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -43,10 +43,3 @@ if (POLICY CMP0141) cmake_policy(SET CMP0141 NEW) set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") endif() - -# Enable compiler warnings for common compilers -if(MSVC) - target_compile_options(${LIB_NAME} PRIVATE /W4 /WX) -else() - target_compile_options(${LIB_NAME} PRIVATE -Wall -Wextra -Wpedantic -Werror) -endif() \ No newline at end of file From 6c6a25e3dcc1f358b0eb88406f21225b0283f034 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:21:03 -0500 Subject: [PATCH 209/379] driver utils updates Add parseboolean function, remove dead code, and generalize printlabel --- app/include/Driver.h | 3 +- app/src/DriverUtils.cpp | 59 ++++++++++++++++++++-------------------- app/tests/TestDriver.cpp | 2 +- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/app/include/Driver.h b/app/include/Driver.h index 556a75d..1d735d7 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -37,8 +37,9 @@ DrvrReturnCode ValidateInputs(const DrvrParams ¶ms); // Driver Utils std::string GetDatetimeString(); +DrvrReturnCode ParseBoolean(const std::string &str, bool &value); DrvrReturnCode ParseDouble(const std::string &str, double &value); DrvrReturnCode ParseInteger(const std::string &str, int &value); -void PrintLabel(std::ofstream &fp, const std::string &lbl); +void PrintLabel(std::ostream &os, const std::string &lbl); void StringToLower(std::string &str); void Version(std::ostream &os = std::cout); \ No newline at end of file diff --git a/app/src/DriverUtils.cpp b/app/src/DriverUtils.cpp index 7e73339..e4c5766 100644 --- a/app/src/DriverUtils.cpp +++ b/app/src/DriverUtils.cpp @@ -44,6 +44,32 @@ std::string GetDatetimeString() { return std::string(mbstr); } +/******************************************************************************* + * Parse a boolean value read from the input parameter file. + * + * Supports either "true" or "false" (case-insensitive) or "0" or "1" + * + * @param[in] str Input file value as string + * @param[out] value Input file value converted to bool + * @return Return code + ******************************************************************************/ +DrvrReturnCode ParseBoolean(const std::string &str, bool &value) { + try { + std::string str_lower = str; + StringToLower(str_lower); + if (str_lower == "0" || str_lower == "false") { + value = false; + } else if (str_lower == "1" || str_lower == "true") { + value = true; + } else { + return DRVRERR__PARSE; + } + } catch (...) { + return DRVRERR__PARSE; + } + return DRVR__SUCCESS; +} + /******************************************************************************* * Parse a double value read from the input parameter file * @@ -86,27 +112,14 @@ DrvrReturnCode ParseInteger(const std::string &str, int &value) { return DRVR__SUCCESS; } -/******************************************************************************* - * Common error handling function - * - * @param[in] err Error parsing code - * @param[in] msg Error message - * @return Error code from input param - ******************************************************************************/ -int ParsingErrorHelper(const int err, const std::string &msg) { - std::cerr << "Driver Error " << err << ": Unable to parse '" << msg - << "' value." << std::endl; - return err; -} - /******************************************************************************* * Helper function to standardize printing of text labels to file * - * @param[in] fp Output stream, a text file open for writing + * @param[in] os Output stream for writing * @param[in] lbl Text message ******************************************************************************/ -void PrintLabel(std::ofstream &fp, const std::string &lbl) { - fp << "[" << lbl << "]"; +void PrintLabel(std::ostream &os, const std::string &lbl) { + os << "[" << lbl << "]"; } @@ -121,20 +134,6 @@ void StringToLower(std::string &str) { }); } -/******************************************************************************* - * Helper function to format and print error messages encountered during - * validation of input parameters - * - * @param[in] opt Command flag in error - * @param[in] err Error code - * @return Return code - ******************************************************************************/ -int Validate_RequiredErrMsgHelper(const std::string &opt, const int err) { - std::cerr << "Driver Error " << err << ": Option \"" << opt - << "\" is required but was not provided" << std::endl; - return err; -} - /******************************************************************************* * Print version information to the specified output stream * diff --git a/app/tests/TestDriver.cpp b/app/tests/TestDriver.cpp index acc1313..3134768 100644 --- a/app/tests/TestDriver.cpp +++ b/app/tests/TestDriver.cpp @@ -62,4 +62,4 @@ TEST_F(DriverTest, ValidationOutFileError) { // TODO-TEMPLATE: Add tests for any additional validation errors -// TODO-TEMPALTE: Add other general tests for the driver \ No newline at end of file +// TODO-TEMPLATE: Add other general tests for the driver \ No newline at end of file From 4854d4a2a29cee9ca7635e99c95ba27eef4a6049 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:23:28 -0500 Subject: [PATCH 210/379] fix incorrect documentation --- app/src/AeronauticalStatisticalModel.cpp | 2 +- app/src/HeightGainTerminalCorrectionModel.cpp | 2 +- app/src/TerrestrialStatisticalModel.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/AeronauticalStatisticalModel.cpp b/app/src/AeronauticalStatisticalModel.cpp index bbf6b2d..8c16006 100644 --- a/app/src/AeronauticalStatisticalModel.cpp +++ b/app/src/AeronauticalStatisticalModel.cpp @@ -39,7 +39,7 @@ ReturnCode CallAeronauticalStatisticalModel( /******************************************************************************* * Parse input stream (file or string stream) to ASM parameter struct. * - * @param[in] stream Path to ASM input parameter file + * @param[in] stream Input stream containing ASM parameters * @param[out] asm_params ASM input parameter struct * @return Return code ******************************************************************************/ diff --git a/app/src/HeightGainTerminalCorrectionModel.cpp b/app/src/HeightGainTerminalCorrectionModel.cpp index a76cd1f..b586215 100644 --- a/app/src/HeightGainTerminalCorrectionModel.cpp +++ b/app/src/HeightGainTerminalCorrectionModel.cpp @@ -46,7 +46,7 @@ ReturnCode CallHeightGainTerminalCorrectionModel( /******************************************************************************* * Parse input stream (file or string stream) to HGTCM parameter struct. * - * @param[in] stream Path to HGTCM input parameter file + * @param[in] stream Input stream containing HGTCM parameters * @param[out] hgtcm_params HGTCM input parameter struct * @return Return code ******************************************************************************/ diff --git a/app/src/TerrestrialStatisticalModel.cpp b/app/src/TerrestrialStatisticalModel.cpp index bc1bdff..45be368 100644 --- a/app/src/TerrestrialStatisticalModel.cpp +++ b/app/src/TerrestrialStatisticalModel.cpp @@ -19,7 +19,7 @@ const std::string TSMInputKeys::p = "p"; /******************************************************************************* * Top-level control function for Terrestrial Statistical Model operation * - * @param[in] tsm_params Terrestrial Statistical Model input parameter struct + * @param[in] tsm_params Terrestrial Statistical Model input parameter struct * @param[out] L_ctt__db Additional loss (clutter loss), in dB * @return Return code ******************************************************************************/ @@ -39,7 +39,7 @@ ReturnCode CallTerrestrialStatisticalModel( /******************************************************************************* * Parse input stream (file or string stream) to TSM parameter struct. * - * @param[in] stream Path to TSM input parameter file + * @param[in] stream Input stream containing TSM parameters * @param[out] tsm_params TSM input parameter struct * @return Return code ******************************************************************************/ From dc6af9f908cbf4b50410b42806a9558d1be4c146 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:44:54 -0500 Subject: [PATCH 211/379] Add missing function definitions --- app/include/Driver.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/include/Driver.h b/app/include/Driver.h index e611110..1cb2550 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -39,6 +39,7 @@ ReturnCode CallAeronauticalStatisticalModel( ); DrvrReturnCode ParseASMInputFile(const std::string &in_file, ASMParams &asm_params); +DrvrReturnCode ParseASMInputStream(std::istream &stream, ASMParams &asm_params); void WriteASMInputs(std::ofstream &fp, const ASMParams ¶ms); // Height Gain Terminal Correction Model @@ -47,6 +48,8 @@ ReturnCode CallHeightGainTerminalCorrectionModel( ); DrvrReturnCode ParseHGTCMInputFile(const std::string &in_file, HGTCMParams &hgtcm_params); +DrvrReturnCode + ParseHGTCMInputStream(std::istream &stream, HGTCMParams &hgtcm_params); void WriteHGTCMInputs(std::ofstream &fp, const HGTCMParams ¶ms); // Terrestrial Statistical Model @@ -55,6 +58,7 @@ ReturnCode CallTerrestrialStatisticalModel( ); DrvrReturnCode ParseTSMInputFile(const std::string &in_file, TSMParams &tsm_params); +DrvrReturnCode ParseTSMInputStream(std::istream &stream, TSMParams &tsm_params); void WriteTSMInputs(std::ofstream &fp, const TSMParams ¶ms); // Reporting From 5cc0edc69cd2ab17cbb2bfa86c53fdf123477e32 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 4 Nov 2024 18:58:53 -0500 Subject: [PATCH 212/379] Consistent order for GTest comparisons --- app/tests/TestDriver.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/tests/TestDriver.cpp b/app/tests/TestDriver.cpp index 3134768..70ab160 100644 --- a/app/tests/TestDriver.cpp +++ b/app/tests/TestDriver.cpp @@ -8,7 +8,7 @@ TEST_F(DriverTest, MissingOptionError1) { std::string cmd = executable + " -i -o out.txt"; SuppressOutputs(cmd); int rtn = RunCommand(cmd); - EXPECT_EQ(rtn, DRVRERR__MISSING_OPTION); + EXPECT_EQ(DRVRERR__MISSING_OPTION, rtn); } TEST_F(DriverTest, MissingOptionError2) { @@ -16,14 +16,14 @@ TEST_F(DriverTest, MissingOptionError2) { std::string cmd = executable + " -i"; SuppressOutputs(cmd); int rtn = RunCommand(cmd); - EXPECT_EQ(rtn, DRVRERR__MISSING_OPTION); + EXPECT_EQ(DRVRERR__MISSING_OPTION, rtn); } TEST_F(DriverTest, InvalidOptionError) { std::string cmd = executable + " -X"; SuppressOutputs(cmd); int rtn = RunCommand(cmd); - EXPECT_EQ(rtn, DRVRERR__INVALID_OPTION); + EXPECT_EQ(DRVRERR__INVALID_OPTION, rtn); } TEST_F(DriverTest, OpeningInputFileError) { @@ -31,7 +31,7 @@ TEST_F(DriverTest, OpeningInputFileError) { params.in_file = "/invalid/path/input.xyz"; params.DBG = false; int rtn = RunDriver(params); - EXPECT_EQ(rtn, DRVRERR__OPENING_INPUT_FILE); + EXPECT_EQ(DRVRERR__OPENING_INPUT_FILE, rtn); } TEST_F(DriverTest, OpeningOutputFileError) { @@ -41,14 +41,14 @@ TEST_F(DriverTest, OpeningOutputFileError) { params.DBG = true; params.out_file = "/invalid/path/output.xyz"; int rtn = RunDriverWithInputFile(inputs, params); - EXPECT_EQ(rtn, DRVRERR__OPENING_OUTPUT_FILE); + EXPECT_EQ(DRVRERR__OPENING_OUTPUT_FILE, rtn); } TEST_F(DriverTest, ValidationInFileError) { std::string cmd = executable + " -o out.txt"; SuppressOutputs(cmd); int rtn = RunCommand(cmd); - EXPECT_EQ(rtn, DRVRERR__VALIDATION_IN_FILE); + EXPECT_EQ(DRVRERR__VALIDATION_IN_FILE, rtn); } TEST_F(DriverTest, ValidationOutFileError) { @@ -57,7 +57,7 @@ TEST_F(DriverTest, ValidationOutFileError) { std::string cmd = executable + " -i in.txt"; SuppressOutputs(cmd); int rtn = RunCommand(cmd); - EXPECT_EQ(rtn, DRVRERR__VALIDATION_OUT_FILE); + EXPECT_EQ(DRVRERR__VALIDATION_OUT_FILE, rtn); } // TODO-TEMPLATE: Add tests for any additional validation errors From 7dd864b5e601db6694fab9a26f103c7314cb558f Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 4 Nov 2024 19:24:39 -0500 Subject: [PATCH 213/379] more informative error message in driver --- app/src/AeronauticalStatisticalModel.cpp | 1 + app/src/HeightGainTerminalCorrectionModel.cpp | 1 + app/src/TerrestrialStatisticalModel.cpp | 1 + 3 files changed, 3 insertions(+) diff --git a/app/src/AeronauticalStatisticalModel.cpp b/app/src/AeronauticalStatisticalModel.cpp index 8c16006..a366529 100644 --- a/app/src/AeronauticalStatisticalModel.cpp +++ b/app/src/AeronauticalStatisticalModel.cpp @@ -63,6 +63,7 @@ DrvrReturnCode if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_PERCENTAGE; } else { + std::cerr << "Unknown parameter: " << key << std::endl; rtn = DRVRERR__PARSE; } diff --git a/app/src/HeightGainTerminalCorrectionModel.cpp b/app/src/HeightGainTerminalCorrectionModel.cpp index b586215..91635af 100644 --- a/app/src/HeightGainTerminalCorrectionModel.cpp +++ b/app/src/HeightGainTerminalCorrectionModel.cpp @@ -83,6 +83,7 @@ DrvrReturnCode = static_cast(clutter_type_int); } } else { + std::cerr << "Unknown parameter: " << key << std::endl; rtn = DRVRERR__PARSE; } diff --git a/app/src/TerrestrialStatisticalModel.cpp b/app/src/TerrestrialStatisticalModel.cpp index 45be368..dc10ce9 100644 --- a/app/src/TerrestrialStatisticalModel.cpp +++ b/app/src/TerrestrialStatisticalModel.cpp @@ -63,6 +63,7 @@ DrvrReturnCode if (rtn == DRVRERR__PARSE) rtn = DRVRERR__PARSE_PERCENTAGE; } else { + std::cerr << "Unknown parameter: " << key << std::endl; rtn = DRVRERR__PARSE; } From 2b3eb08607031c1a3d1492a455e549d42b6fca79 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 4 Nov 2024 20:01:42 -0500 Subject: [PATCH 214/379] cleanup driver printing code --- app/src/Driver.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index 91ab35e..cb7efeb 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -36,12 +36,14 @@ int main(int argc, char **argv) { if (rtn > DRVR__RETURN_SUCCESS) return rtn; - // Print results to file + // Open output file for writing std::ofstream fp(params.out_file); if (!fp) { std::cerr << "Error opening output file. Exiting." << std::endl; return DRVRERR__OPENING_OUTPUT_FILE; } + + // Print generator information to file fp << std::left << std::setw(25) << "Model" << LIBRARY_NAME; fp PRINT "Library Version" << "v" << LIBRARY_VERSION; fp PRINT "Driver Version" << "v" << DRIVER_VERSION; From 57a320ff3f58aa835dcfd45ab792ed299fef210b Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 4 Nov 2024 20:01:54 -0500 Subject: [PATCH 215/379] organize driver printing code --- app/src/Driver.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index d72adbb..4236b67 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -70,12 +70,14 @@ int main(int argc, char **argv) { return rtn; } - // Print results to file + // Open output file for writing std::ofstream fp(params.out_file); if (!fp) { std::cerr << "Error opening output file. Exiting." << std::endl; return DRVRERR__OPENING_OUTPUT_FILE; } + + // Print generator information to file fp << std::left << std::setw(25) << "Model" << LIBRARY_NAME; fp PRINT "Model Variant"; switch (params.model) { @@ -98,8 +100,9 @@ int main(int argc, char **argv) { fp << argv[i] << " "; } fp << std::endl << std::endl; - fp << "Inputs"; + // Print inputs to file + fp << "Inputs"; switch (params.model) { case P2108Model::HGTCM: WriteHGTCMInputs(fp, hgtcm_params); @@ -113,13 +116,11 @@ int main(int argc, char **argv) { // Validation above ensures one of these cases evaluates } - if (rtn != SUCCESS) { - fp PRINT LIBRARY_NAME << " Error" SETW13 rtn; - PrintLabel(fp, GetReturnStatus(rtn)); - } else { - fp << std::endl << std::endl << "Results"; - fp PRINT "Return Code" SETW13 rtn; - PrintLabel(fp, GetReturnStatus(rtn)); + // Print results to file + fp << std::endl << std::endl << "Results"; + fp PRINT "Return Code" SETW13 rtn; + PrintLabel(fp, GetReturnStatus(rtn)); + if (rtn == SUCCESS) { fp PRINT "Clutter loss" SETW13 std::fixed << std::setprecision(1) << loss__db.front() << "(dB)"; } From 496fba68d0f55af1692fe67fc1d7b0e5c988038d Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 5 Nov 2024 15:39:10 -0500 Subject: [PATCH 216/379] .gitignore binaries in wrappers --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index 738bc1d..b0f36aa 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,13 @@ Thumbs.db *.o +#################################### +## Shared Library files in Wrappers +#################################### +**.dll +**.so +**.dylib + ################# ## Visual Studio ################# From df2c0efd9caf92d6aae569ad79c6e0e7c3aba450 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 5 Nov 2024 15:39:35 -0500 Subject: [PATCH 217/379] Organize includes --- src/ReturnCodes.cpp | 2 +- tests/TestAeronauticalStatisticalModel.cpp | 6 ++++++ tests/TestHeightGainTerminalCorrectionModel.cpp | 6 ++++-- tests/TestInverseComplementaryCumulativeDistribution.cpp | 3 ++- tests/TestTerrestrialStatisticalModel.cpp | 3 +++ tests/TestUtils.h | 4 +--- 6 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/ReturnCodes.cpp b/src/ReturnCodes.cpp index b27a32f..f355fd2 100644 --- a/src/ReturnCodes.cpp +++ b/src/ReturnCodes.cpp @@ -14,7 +14,7 @@ #endif #endif -#include // for strcpy_s +#include // for strcpy, strcpy_s #include // for std::string #include // for std::unordered_map diff --git a/tests/TestAeronauticalStatisticalModel.cpp b/tests/TestAeronauticalStatisticalModel.cpp index 49506c8..252ebec 100644 --- a/tests/TestAeronauticalStatisticalModel.cpp +++ b/tests/TestAeronauticalStatisticalModel.cpp @@ -1,5 +1,11 @@ #include "TestUtils.h" +#define _USE_MATH_DEFINES // for constants +#include // for M_PI_2, M_PI_4 +#include // GoogleTest +#include // for std::numeric_limits +#include // for std::vector + // Test fixture for the unit tests class AeronauticalStatisticalModelTest: public ::testing::Test { protected: diff --git a/tests/TestHeightGainTerminalCorrectionModel.cpp b/tests/TestHeightGainTerminalCorrectionModel.cpp index bff10d3..5a2994f 100644 --- a/tests/TestHeightGainTerminalCorrectionModel.cpp +++ b/tests/TestHeightGainTerminalCorrectionModel.cpp @@ -1,7 +1,9 @@ #include "TestUtils.h" -#include // for std::log10 -#include // For std::numeric_limits::max() +#include // for std::log10 +#include // GoogleTest +#include // for std::numeric_limits +#include // for std::vector // Test fixture for the unit tests class HeightGainTerminalCorrectionModelTest: public ::testing::Test { diff --git a/tests/TestInverseComplementaryCumulativeDistribution.cpp b/tests/TestInverseComplementaryCumulativeDistribution.cpp index 4418f2b..fdd1979 100644 --- a/tests/TestInverseComplementaryCumulativeDistribution.cpp +++ b/tests/TestInverseComplementaryCumulativeDistribution.cpp @@ -1,6 +1,7 @@ #include "TestUtils.h" -#include +#include // GoogleTest +#include // for std::out_of_range TEST(InverseCCDFTest, TestInverseCCDF) { EXPECT_DOUBLE_EQ( diff --git a/tests/TestTerrestrialStatisticalModel.cpp b/tests/TestTerrestrialStatisticalModel.cpp index 7e6b32d..48b9048 100644 --- a/tests/TestTerrestrialStatisticalModel.cpp +++ b/tests/TestTerrestrialStatisticalModel.cpp @@ -1,5 +1,8 @@ #include "TestUtils.h" +#include // GoogleTest +#include // for std::vector + // Test fixture for the unit tests class TerrestrialStatisticalModelTest: public ::testing::Test { protected: diff --git a/tests/TestUtils.h b/tests/TestUtils.h index ce3e67a..7c847a3 100644 --- a/tests/TestUtils.h +++ b/tests/TestUtils.h @@ -2,9 +2,7 @@ #include "ITS.ITU.PSeries.P2108/P2108.h" -#include -#include -#include +#include // for std::string using namespace ITS::ITU::PSeries::P2108; From fd28e1acff877f062dc2a19d5218e1c06bf00f2f Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 5 Nov 2024 15:44:29 -0500 Subject: [PATCH 218/379] Add missing include --- tests/TestUtils.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/TestUtils.h b/tests/TestUtils.h index 7c847a3..6d973c7 100644 --- a/tests/TestUtils.h +++ b/tests/TestUtils.h @@ -3,6 +3,7 @@ #include "ITS.ITU.PSeries.P2108/P2108.h" #include // for std::string +#include // for std::vector using namespace ITS::ITU::PSeries::P2108; From cc867b11cc974cf582f75aa1289722819bbb86ab Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 8 Nov 2024 15:43:12 -0500 Subject: [PATCH 219/379] ignore derived files from wrappers --- .gitignore | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.gitignore b/.gitignore index b0f36aa..9c069f8 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,14 @@ Thumbs.db **.so **.dylib +###################################### +## Derived files produced by wrappers +###################################### +**.mltbx +**.whl +**.tar.gz +**.nupkg + ################# ## Visual Studio ################# From 649203b7f1d5f8b0aabd4e9a673531fded82895f Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:04:37 -0500 Subject: [PATCH 220/379] fix template code so tests all pass --- app/src/Driver.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index cb7efeb..357f7b8 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -32,6 +32,14 @@ int main(int argc, char **argv) { // TODO-TEMPLATE: Add driver logic, e.g. validating inputs and calling the model + // TODO-TEMPLATE this code block exists for a unit test to pass. Similar logic + // should be added to functions which parse input files. + std::ifstream file(params.in_file); + if (!file) { + std::cerr << "Failed to open file " << params.in_file << std::endl; + return DRVRERR__OPENING_INPUT_FILE; + } + // Return driver error code if one was returned if (rtn > DRVR__RETURN_SUCCESS) return rtn; @@ -68,7 +76,7 @@ int main(int argc, char **argv) { DrvrReturnCode ParseArguments(int argc, char **argv, DrvrParams ¶ms) { // TODO-TEMPLATE: Populate vector with all valid arguments const std::vector validArgs - = {"-i", "-o", "-h", "--help", "-v", "--version"}; + = {"-i", "-o", "-dbg", "-h", "--help", "-v", "--version"}; for (int i = 1; i < argc; i++) { // Parse arg to lowercase string @@ -93,6 +101,7 @@ DrvrReturnCode ParseArguments(int argc, char **argv, DrvrParams ¶ms) { // TODO-TEMPLATE handle any model input flags here } else if (arg == "-dbg") { params.DBG = true; + continue; } // Check if end of arguments reached or next argument is another flag From 9d98550bc9b7206e158ce216199a9803763b9815 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:06:24 -0500 Subject: [PATCH 221/379] Run actions on template --- .github/workflows/ctest.yml | 1 - .github/workflows/doxygen.yml | 1 - .github/workflows/release.yml | 1 - 3 files changed, 3 deletions(-) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index 6987805..c53cfb9 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -12,7 +12,6 @@ on: # Define the matrix for different operating systems jobs: build-and-test: - if: ${{ github.repository != 'NTIA/proplib-template' }} name: ${{ matrix.os }} / CMake ${{ matrix.cmakeVersion }} runs-on: ${{ matrix.os }} strategy: diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index 9a2f311..0870eaf 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -18,7 +18,6 @@ concurrency: jobs: build: - if: ${{ github.repository != 'NTIA/proplib-template' }} runs-on: ubuntu-latest steps: - name: Checkout repository diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 815d0d9..f9a25c9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,6 @@ permissions: jobs: create_release_artifacts: - if: github.repository != 'NTIA/proplib-template' name: Create release artifacts runs-on: ${{ matrix.os }} strategy: From 2f55d04b111d414e872f81a9bcd251e7d7062b49 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:07:45 -0500 Subject: [PATCH 222/379] remove template condition --- .github/workflows/doxygen.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index 0870eaf..e97a974 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -54,7 +54,7 @@ jobs: path: ./docs/html/ deploy: - if: ${{ (github.event_name == 'release') && (github.repository != 'NTIA/proplib-template') }} + if: ${{ github.event_name == 'release' }} needs: build permissions: contents: read From 07484c3348cda2064e538909b493d38c532d14b6 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:08:38 -0500 Subject: [PATCH 223/379] build docs on push to main or dev --- .github/workflows/doxygen.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index e97a974..a9287dd 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -8,6 +8,8 @@ on: types: ["published"] pull_request: branches: ["main", "dev"] + push: + branches: ["main", "dev"] workflow_dispatch: # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. From d4146b504759d794b1ec7bd688b62cfe754d8458 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:18:55 -0500 Subject: [PATCH 224/379] cleanup includes --- app/include/Driver.h | 1 - app/src/CommaSeparatedIterator.cpp | 7 ++----- app/src/Driver.cpp | 9 +++++++++ app/src/DriverUtils.cpp | 2 +- app/tests/TempTextFile.cpp | 2 +- app/tests/TestDriver.cpp | 2 ++ app/tests/TestDriver.h | 1 - 7 files changed, 15 insertions(+), 9 deletions(-) diff --git a/app/include/Driver.h b/app/include/Driver.h index 1d735d7..8c357bf 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -10,7 +10,6 @@ // TODO-TEMPLATE: Include your library's main interface header #include "ITS.PropLibTemplate/PropLibTemplate.h" -#include // for std::ofstream #include // for std::left, std::setw #include // for std::cout #include // for std::endl, std::ostream diff --git a/app/src/CommaSeparatedIterator.cpp b/app/src/CommaSeparatedIterator.cpp index 9e665bc..022a952 100644 --- a/app/src/CommaSeparatedIterator.cpp +++ b/app/src/CommaSeparatedIterator.cpp @@ -5,11 +5,8 @@ #include "Driver.h" -#include // for transform -#include // for std::tolower -#include // for std::ptrdiff_t, std::size_t -#include // for std::istream -#include // for std::input_iterator_tag +#include // for std::size_t +#include // for std::istream #include // for std::runtime_error #include // for std::getline, std::string diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index 357f7b8..62e1bf4 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -3,6 +3,15 @@ */ #include "Driver.h" +#include // for std::find +#include // for std::ifstream, std::ofstream +#include // for std::setw +#include // for std::left +#include // for std::cerr +#include // for std::endl +#include // for std::string +#include // for std::vector + /******************************************************************************* * Main function of the driver executable * diff --git a/app/src/DriverUtils.cpp b/app/src/DriverUtils.cpp index e4c5766..b4c5e56 100644 --- a/app/src/DriverUtils.cpp +++ b/app/src/DriverUtils.cpp @@ -16,9 +16,9 @@ #include // for std::transform #include // for std::tolower #include // for localtime_{s,r}, std::{time, time_t, tm, strftime} -#include // for std::ofstream #include // for std::setfill, std::setw #include // for std::cerr, std::endl +#include // for std::ostream #include // for std::stod, std::stoi, std::string /****************************************************************************** diff --git a/app/tests/TempTextFile.cpp b/app/tests/TempTextFile.cpp index ab572a5..f814e3d 100644 --- a/app/tests/TempTextFile.cpp +++ b/app/tests/TempTextFile.cpp @@ -19,7 +19,7 @@ #include // for std::remove #include // for std::ofstream -#include // for std::cerr, std::cout, std::ios::trunc +#include // for std::cerr, std::ios::trunc #include // for std::endl #include // for std::runtime_error #include // for std::string diff --git a/app/tests/TestDriver.cpp b/app/tests/TestDriver.cpp index 70ab160..abc9083 100644 --- a/app/tests/TestDriver.cpp +++ b/app/tests/TestDriver.cpp @@ -3,6 +3,8 @@ */ #include "TestDriver.h" +#include // for std::string + TEST_F(DriverTest, MissingOptionError1) { // Test case: missing option between two provided flags std::string cmd = executable + " -i -o out.txt"; diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index a1daf12..ecfd2ac 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -6,7 +6,6 @@ #include "Driver.h" #include "TempTextFile.h" -#include // for std::replace #include // for std::remove, std::perror #include // for std::system #include // GoogleTest From 3624162c216f9992a88a5ce4636acfa874c709f7 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 12 Nov 2024 13:11:50 -0500 Subject: [PATCH 225/379] Update badges, add links to other template repos --- README.md | 69 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 228b32b..39c8bd0 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,47 @@ # NTIA/ITS Propagation Library Template Project # - -[![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] - - - - + +[![NTIA/ITS PropLib][proplib-badge]][proplib-link] + +[proplib-badge]: https://img.shields.io/badge/PropLib-badge?label=%F0%9F%87%BA%F0%9F%87%B8%20NTIA%2FITS&labelColor=162E51&color=D63E04 +[proplib-link]: https://ntia.github.io/propagation-library-wiki +[gh-actions-test-badge]: https://img.shields.io/github/actions/workflow/status/NTIA/proplib-template/ctest.yml?branch=main&logo=cmake&label=Build%2FTests&labelColor=162E51 [gh-actions-test-link]: https://github.com/NTIA/proplib-template/actions/workflows/ctest.yml -[gh-actions-test-badge]: https://github.com/NTIA/proplib-template/actions/workflows/ctest.yml/badge.svg?branch=main -[gh-actions-docs-link]: https://github.com/NTIA/proplib-template/actions/workflows/doxygen.yml -[gh-actions-docs-badge]: https://github.com/NTIA/proplib-template/actions/workflows/doxygen.yml/badge.svg?branch=main -[gh-releases-badge]: https://img.shields.io/github/v/release/NTIA/proplib-template -[gh-issues-badge]: https://img.shields.io/github/issues/NTIA/proplib-template - - +[gh-actions-docs-badge]: https://img.shields.io/github/actions/workflow/status/NTIA/proplib-template/doxygen.yml?branch=main&logo=c%2B%2B&label=Docs&labelColor=162E51 +[gh-pages-docs-link]: https://ntia.github.io/proplib-template +[gh-releases-badge]: https://img.shields.io/github/v/release/NTIA/proplib-template?logo=github&label=Release&labelColor=162E51&color=D63E04 +[gh-releases-link]: https://github.com/NTIA/proplib-template/releases +[gh-issues-badge]: https://img.shields.io/github/issues/NTIA/proplib-template?logo=github&label=Issues&labelColor=162E51 +[gh-issues-link]: https://github.com/NTIA/proplib-template/issues +[doi-badge]: https://img.shields.io/badge/{TODO-ALL-VERSIONS-DOI}-x?logo=doi&logoColor=ffffff&labelColor=162E51&color=D63E04 +[doi-link]: https://zenodo.org/badge/latestdoi/{TODO-REPOSITORY-ID} This code repository is a template repository for software in the NTIA/ITS @@ -25,6 +49,17 @@ Propagation Library (PropLib). This template is intended for developers wishing to develop a cross-platform C++ library as part of PropLib. Instructions on how to use this repository are found in its [GitHub Wiki](https://github.com/NTIA/proplib-template/wiki). +Additional template repositories exist for building C#/.NET, MATLAB, and Python +wrappers for PropLib C++ libraries. See: + +- [NTIA/proplib-template-dotnet](https://github.com/NTIA/proplib-template-dotnet) +- [NTIA/proplib-template-matlab](https://github.com/NTIA/proplib-template-matlab) +- [NTIA/proplib-template-python](https://github.com/NTIA/proplib-template-python) + +## Contact ## + +For questions about using this template repository, contact + [![NTIA/ITS PropLib][proplib-badge]][proplib-link] This code repository is a template repository for software in the NTIA/ITS From 3225da173dda64223b98c31e66062f3c7b388750 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 20 Nov 2024 16:48:47 -0500 Subject: [PATCH 249/379] Update GitHubRepoPublicReleaseApproval.md --- GitHubRepoPublicReleaseApproval.md | 39 ++++++++++++++++++------------ 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/GitHubRepoPublicReleaseApproval.md b/GitHubRepoPublicReleaseApproval.md index ff4fc4a..ad5714a 100644 --- a/GitHubRepoPublicReleaseApproval.md +++ b/GitHubRepoPublicReleaseApproval.md @@ -1,23 +1,32 @@ # GitHub Repository Public Release Approval -**Project Name:** Office of Spectrum Management R&D - Propagation Library +**Project Name:** NTIA/OSM Research and Development **Software Name:** TODO-TEMPLATE -The software identified above, which is contained within the repository this document is stored in, has met the following criteria for public release: +The project identified above, which is contained within the repository this +document is stored in, has met the following criteria for public release: -1. [ ] 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. - * [ ] Unit tests are available and the software has been tested against the unit tests - * [ ] Any test data necessary for the code and its unit tests to function is included in this GitHub repository, or in a parent repository which includes this one as a Git submodule. - * [ ] This repository contains complete README and CONTRIBUTING files -1. [ ] This repository adheres to the PropLib Template, and is up-to-date with the latest tagged release of the template repository. -1. [ ] All template or placeholder code has been removed -1. [ ] The README.md file has passed editorial review from the ITS Publications Office. -1. [ ] 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. -1. [ ] Approved disclaimer and licensing language has been included. +1. [ ] 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. + * [ ] Unit tests are available and the software has been tested against the unit tests. + * [ ] The software can be compiled and/or used on Windows, macOS, and Linux. + * [ ] The repository structure and contents follow from the ITS PropLib template, and + all template or placeholder code has been removed. + * [ ] The repository includes the appropriate `LICENSE.md` file +2. [ ] Any test data necessary for the code and its unit tests to function is included in this +GitHub repository, or in a parent repository which includes this one as a Git submodule. +3. [ ] The README.md file has passed editorial review from the ITS Publications Office. +4. [ ] 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. [ ] 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: +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:** TODO-TEMPLATE - -* **Supervising Division Chief or Release Authority:** TODO-TEMPLATE +* Project Lead: TODO-TEMPLATE +* Supervising Division Chief or Release Authority: TODO-TEMPLATE From e64b0b8958c9045ed39f50f47cd39b133140cb83 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:51:33 -0500 Subject: [PATCH 250/379] Create cff-validator.yml --- .github/workflows/cff-validator.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/cff-validator.yml diff --git a/.github/workflows/cff-validator.yml b/.github/workflows/cff-validator.yml new file mode 100644 index 0000000..f21aebc --- /dev/null +++ b/.github/workflows/cff-validator.yml @@ -0,0 +1,21 @@ +name: Validate CITATION.cff + +on: + push: + paths: CITATION.cff + pull_request: + paths: CITATION.cff + workflow_dispatch: + +jobs: + Validate-CITATION-cff: + runs-on: ubuntu-latest + name: Validate CITATION.cff + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Validate CITATION.cff + uses: dieghernan/cff-validator@v3 \ No newline at end of file From acd284606612594294f7374fb614d904bc2a00d8 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:57:36 -0500 Subject: [PATCH 251/379] Remove name-particle from template citation file --- CITATION.cff | 1 - 1 file changed, 1 deletion(-) diff --git a/CITATION.cff b/CITATION.cff index e398a6a..1d97612 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -6,7 +6,6 @@ type: software authors: - given-names: TODO-TEMPLATE family-names: TODO-TEMPLATE - name-particle: TODO-TEMPLATE (optional) email: TODO-TEMPLATE@ntia.gov affiliation: >- U.S. Department of Commerce, National From af1c2d96986ce31106e41d182ba6f5aeceb245ba Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:12:59 -0500 Subject: [PATCH 252/379] Make template citation valid --- CITATION.cff | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CITATION.cff b/CITATION.cff index 1d97612..30b6fb5 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -11,7 +11,7 @@ authors: U.S. Department of Commerce, National Telecommunications and Information Administration, Institute for Telecommunication Sciences - orcid: 'https://orcid.org/TODO-TEMPLATE' + orcid: 'https://orcid.org/0000-0000-0000-0000' - name: >- U.S. Department of Commerce, National Telecommunications and Information Administration, From 366dc884c54d7adcf6b2390c31dc94ac82de32d2 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:26:44 -0500 Subject: [PATCH 253/379] Compiler and output configuration at top-level --- CMakeLists.txt | 29 +++++++++++++++++++++++++++++ app/CMakeLists.txt | 1 - app/src/CMakeLists.txt | 3 --- app/tests/CMakeLists.txt | 7 ------- src/CMakeLists.txt | 9 --------- tests/CMakeLists.txt | 7 ------- 6 files changed, 29 insertions(+), 27 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 23f5b92..6b08d35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,8 @@ +############################################################ +## CMakeList.txt : Top-level CMake project file, do global +## configuration and include sub-projects here. +############################################################ + # >=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 @@ -43,6 +48,30 @@ option(COPY_TO_WRAPPERS "Copy the compiled shared library into wrapper submodule # GoogleTest v1.12.1 requires at least C++11 set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +add_library(proplib_compiler_flags INTERFACE) +target_compile_features(proplib_compiler_flags INTERFACE cxx_std_11) + +# add compiler warning flags just when building this project via +# the BUILD_INTERFACE genex +set(gcc_like_cxx "$") +set(msvc_cxx "$") +target_compile_options(proplib_compiler_flags INTERFACE + "$<${gcc_like_cxx}:$>" + "$<${msvc_cxx}:$>" +) + +# 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() + +# control where the static and shared libraries are built +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" CACHE STRING "Set the CMAKE Archive Output Directory") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" CACHE STRING "Set the CMAKE Library Output Directory") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" CACHE STRING "Set the CMAKE Runtime Output Directory") ########################################## ## BUILD/RUN diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 4fdacdc..780d972 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -4,7 +4,6 @@ set(DRIVER_NAME "${LIB_NAME}Driver") set(DRIVER_VERSION ${PROJECT_VERSION}.0) set(DRIVER_HEADERS "${PROJECT_SOURCE_DIR}/app/include") -set(DRIVER_EXE_DIR "${PROJECT_SOURCE_DIR}/bin") ########################################### ## BUILD THE COMMAND LINE DRIVER diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index 7eaded8..4ec7247 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -31,7 +31,4 @@ add_definitions(-DDRIVER_NAME="${DRIVER_NAME}") set_target_properties( ${DRIVER_NAME} PROPERTIES OUTPUT_NAME ${DRIVER_NAME}-${CMAKE_SYSTEM_NAME}-${DRIVER_VERSION} - LIBRARY_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} - ARCHIVE_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} - RUNTIME_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} ) diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index 18a02ed..e79c6ec 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -26,13 +26,6 @@ target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME}) # Make driver executable location available to source add_definitions(-DDRIVER_LOCATION="$") -set_target_properties( - ${DRIVER_TEST_NAME} PROPERTIES - LIBRARY_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} - ARCHIVE_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} - RUNTIME_OUTPUT_DIRECTORY ${DRIVER_EXE_DIR} -) - ########################################### ## SET UP AND DISCOVER TESTS ########################################### diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9e7ca6b..fbc6500 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,13 +33,4 @@ endif () set_target_properties( ${LIB_NAME} PROPERTIES OUTPUT_NAME ${LIB_NAME}-${PROJECT_VERSION} - LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" - ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" - RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" ) - -# 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() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 92583ab..facec7e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,13 +11,6 @@ add_executable( "TestUtils.h" ) -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" -) - ########################################### ## SET UP AND DISCOVER TESTS ########################################### From af447ee96e378276820b2a0eb1fda0220e087287 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 21 Nov 2024 17:36:38 -0500 Subject: [PATCH 254/379] Fix file filter on CFF validator --- .github/workflows/cff-validator.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cff-validator.yml b/.github/workflows/cff-validator.yml index f21aebc..a01dee3 100644 --- a/.github/workflows/cff-validator.yml +++ b/.github/workflows/cff-validator.yml @@ -2,9 +2,13 @@ name: Validate CITATION.cff on: push: - paths: CITATION.cff + paths: + - CITATION.cff + - .github/workflows/cff-validator.yml pull_request: - paths: CITATION.cff + paths: + - CITATION.cff + - .github/workflows/cff-validator.yml workflow_dispatch: jobs: @@ -18,4 +22,4 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Validate CITATION.cff - uses: dieghernan/cff-validator@v3 \ No newline at end of file + uses: dieghernan/cff-validator@v3 From d0702974cc630653e418089df2bb10a2bbe3aabb Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:12:06 -0500 Subject: [PATCH 255/379] Remove wrappers-as-submodules structure --- CONTRIBUTING.md | 63 ++++++++++++++++++++++++++++--------------- README.md | 7 +++-- docs/doxy_mainpage.md | 2 +- wrap/CMakeLists.txt | 28 ------------------- 4 files changed, 46 insertions(+), 54 deletions(-) delete mode 100644 wrap/CMakeLists.txt diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5f75e90..5153820 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,18 +51,16 @@ When complete, features branches should merge into `dev`. ### 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). +PropLib C++ repositories make use of Git submodules to reference certain development +dependencies, e.g. GoogleTest. Depending on the CMake preset or options used, submodules +may be required to successfully build and/or test the software. When cloning a repository, +submodules are not additionally cloned by default. Use the following commands to initialize +and clone any submodules in a repository: + +```cmd +git submodule init +git submodule update +``` ### Contributing on GitHub @@ -153,10 +151,6 @@ tests/ .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. Files containing tests must be included 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. "release", "debug", etc. ... @@ -179,7 +173,6 @@ The following CMake options are used for top-level project configuration: | `RUN_DRIVER_TESTS` | `ON` | Test the command-line driver executable | | `DOCS_ONLY` | `OFF` | Skip all steps _except_ generating the documentation site | | `RUN_TESTS` | `ON` | Run unit tests for the main library | -| `COPY_TO_WRAPPERS` | `ON` | Copy the compiled shared library into wrapper submodules | [CMake Presets](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html) are provided to support common build configurations. These are specified in the @@ -273,8 +266,36 @@ the Doxygen site to GitHub Pages. ### MATLAB Wrappers -Most code in the MATLAB wrapper is actually written in C. In these files, the same -documentation style as noted above for C++ should be used. +MATLAB® wrappers are implemented as toolboxes which interface with the shared library +compiled from C++ source code. The project structure is informed by the best practices +provided by MathWorks® in their [`toolboxdesign` repository](https://github.com/mathworks/toolboxdesign). +Here is an example of how a function may be documented in a MATLAB wrapper. Note the +documentation with code, where input and output arguments are provided for autocompletion. + +```matlab +function y = DoubleTheInput(x) +% DoubleTheInput - produces an output which is twice its input. +% +% Syntax: +% y = DoubleTheInput(x) +% +% Input Arguments: +% x (double) - A number which needs doubling +% +% Output Arguments: +% y (double) - The result, 2*x +% +% Description: +% Functions more complex than this one may warrant an additional, +% longer description. +arguments (Input) + x double +end +arguments (Output) + y double +end +... +``` ### Python Wrappers @@ -302,9 +323,9 @@ def double_the_input(x: float) -> float: return 2 * x ``` -### C#/.NET Wrappers +### .NET Wrappers -In C#/.NET, documentation comments are written in +PropLib .NET wrappers are written in C# and documentation comments are written in [XML format](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/documentation-comments) and are used to generate documentation through tools like Visual Studio. Use `` tags to provide brief descriptions of classes, constants, functions, etc. Functions should diff --git a/README.md b/README.md index 7d995b9..58a098f 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Propagation Library (PropLib). This template is intended for developers wishing to develop a cross-platform C++ library as part of PropLib. Instructions on how to use this repository are found in its [GitHub Wiki](https://github.com/NTIA/proplib-template/wiki). -Additional template repositories exist for building C#/.NET, MATLAB, and Python +Additional template repositories exist for building .NET, MATLAB, and Python wrappers for PropLib C++ libraries. See: - [NTIA/proplib-template-dotnet](https://github.com/NTIA/proplib-template-dotnet) @@ -95,9 +95,8 @@ In order to do either, ensure the required submodules are cloned by running: ```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 +git submodule init +git submodule update ``` ## Running Tests ## diff --git a/docs/doxy_mainpage.md b/docs/doxy_mainpage.md index d02612c..9084066 100644 --- a/docs/doxy_mainpage.md +++ b/docs/doxy_mainpage.md @@ -11,7 +11,7 @@ library or take it as a dependency. 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. +including bindings for Python, MATLAB, and .NET. ## Site Navigation diff --git a/wrap/CMakeLists.txt b/wrap/CMakeLists.txt deleted file mode 100644 index 297d528..0000000 --- a/wrap/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -########################################### -## 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 From 702cdfd83370739f594721e585c53e6951de25d2 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:13:58 -0500 Subject: [PATCH 256/379] Link to wrapper repositories in readme --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 58a098f..dbaac3a 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,13 @@ For questions about using this template repository, contact Date: Thu, 21 Nov 2024 18:15:03 -0500 Subject: [PATCH 257/379] More wrapper submodule cleanup --- .gitignore | 15 --------------- CMakeLists.txt | 11 ----------- 2 files changed, 26 deletions(-) diff --git a/.gitignore b/.gitignore index 9c069f8..738bc1d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,21 +6,6 @@ Thumbs.db *.o -#################################### -## Shared Library files in Wrappers -#################################### -**.dll -**.so -**.dylib - -###################################### -## Derived files produced by wrappers -###################################### -**.mltbx -**.whl -**.tar.gz -**.nupkg - ################# ## Visual Studio ################# diff --git a/CMakeLists.txt b/CMakeLists.txt index 23f5b92..583edbe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,13 +19,6 @@ project( 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 ########################################### @@ -35,7 +28,6 @@ option(BUILD_DRIVER "Build the command-line driver executable" ON) option(RUN_DRIVER_TESTS "Test the command-line driver executable" ON) option(DOCS_ONLY "Skip all steps except generating the documentation site" OFF) option(RUN_TESTS "Run unit tests for the main library" ON) -option(COPY_TO_WRAPPERS "Copy the compiled shared library into wrapper submodules" ON) ########################################### ## SETUP @@ -49,9 +41,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) ########################################## 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 OR RUN_DRIVER_TESTS) if (EXISTS "${PROJECT_SOURCE_DIR}/extern/googletest/CMakeLists.txt") enable_testing() From 5a8d982fb4c4f10e09ae016c47d22e5aa69bb944 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:41:41 -0500 Subject: [PATCH 258/379] Use a single interface header file --- CONTRIBUTING.md | 3 +-- app/include/Driver.h | 2 +- include/ITS.PropLibTemplate/ReturnCodes.h | 26 ------------------- .../PropLibTemplate.h | 22 ++++++++++++++-- src/CMakeLists.txt | 4 +-- src/ReturnCodes.cpp | 2 +- 6 files changed, 24 insertions(+), 35 deletions(-) delete mode 100644 include/ITS.PropLibTemplate/ReturnCodes.h rename include/{ITS.PropLibTemplate => }/PropLibTemplate.h (57%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5153820..81696d8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -140,8 +140,7 @@ docs/ 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" + .h # Library interface header file goes here, e.g. "ITM.h" src/ .cpp # Source files go here, e.g. "LongleyRice.cpp" and "FreeSpaceLoss.cpp" CMakeLists.txt # Configures cross-platform build diff --git a/app/include/Driver.h b/app/include/Driver.h index 8c357bf..4f5645c 100644 --- a/app/include/Driver.h +++ b/app/include/Driver.h @@ -8,7 +8,7 @@ #include "Structs.h" // TODO-TEMPLATE: Include your library's main interface header -#include "ITS.PropLibTemplate/PropLibTemplate.h" +#include "PropLibTemplate.h" #include // for std::left, std::setw #include // for std::cout diff --git a/include/ITS.PropLibTemplate/ReturnCodes.h b/include/ITS.PropLibTemplate/ReturnCodes.h deleted file mode 100644 index e09d022..0000000 --- a/include/ITS.PropLibTemplate/ReturnCodes.h +++ /dev/null @@ -1,26 +0,0 @@ -/** @file ReturnCodes.h - * Contains return codes used by this software - */ -#pragma once - -#include -#include - -namespace ITS { -// TODO-TEMPLATE put this enum in the correct namespace - -/******************************************************************************* - * Return Codes defined by this software (0-127) - ******************************************************************************/ -// clang-format off -enum ReturnCode { - SUCCESS = 0, /**< Successful execution */ - - // TODO-TEMPLATE add return codes for this software - // TODO-TEMPLATE: Add corresponding status messages in src/ReturnCodes.cpp -}; -// clang-format on - -std::string GetReturnStatus(int code); - -} // namespace ITS diff --git a/include/ITS.PropLibTemplate/PropLibTemplate.h b/include/PropLibTemplate.h similarity index 57% rename from include/ITS.PropLibTemplate/PropLibTemplate.h rename to include/PropLibTemplate.h index 3335260..6553f22 100644 --- a/include/ITS.PropLibTemplate/PropLibTemplate.h +++ b/include/PropLibTemplate.h @@ -4,8 +4,11 @@ */ #pragma once -// TODO-TEMPLATE add other local includes here -#include "ReturnCodes.h" +#include // for std::string +#include // for std::unordered_map + +// TODO-TEMPLATE: This header should provide EVERYTHING needed to interface +// with the shared library, without needing to include other headers. namespace ITS { // TODO-TEMPLATE: Use your library's namespace @@ -19,6 +22,21 @@ namespace ITS { #endif #endif +//////////////////////////////////////////////////////////////////////////////// +// Enums + +/******************************************************************************* + * Return Codes defined by this software (0-127) + ******************************************************************************/ +// clang-format off +enum ReturnCode { + SUCCESS = 0, /**< Successful execution */ + + // TODO-TEMPLATE add return codes for this software + // TODO-TEMPLATE: Add corresponding status messages in src/ReturnCodes.cpp +}; +// clang-format on + //////////////////////////////////////////////////////////////////////////////// // Constants // TODO-TEMPLATE define any global constants here (use constexpr!) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9e7ca6b..9025b76 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,12 +3,10 @@ ########################################### ## TODO-TEMPLATE: Include source AND header files here. ## TODO-TEMPLATE: See CREATING-REPOSITORIES.md for examples. -set(PROJECT_HEADERS "${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}") add_library( ${LIB_NAME} SHARED "ReturnCodes.cpp" - "${PROJECT_HEADERS}/ReturnCodes.h" - "${PROJECT_HEADERS}/${LIB_NAME}.h" + "${PROJECT_SOURCE_DIR}/include/${LIB_NAME}.h" ) # Add the include directory diff --git a/src/ReturnCodes.cpp b/src/ReturnCodes.cpp index 6dd8efa..a37f665 100644 --- a/src/ReturnCodes.cpp +++ b/src/ReturnCodes.cpp @@ -3,7 +3,7 @@ */ // TODO-TEMPLATE include your primary library header -#include "ITS.PropLibTemplate/PropLibTemplate.h" +#include "PropLibTemplate.h" #ifdef _WIN32 // Ensure strcpy_s is available on Windows From 1d9c60b1a9960e1e96edf56c0793266a4a51ee25 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:44:13 -0500 Subject: [PATCH 259/379] Renamed EXPORTED to PROPLIB_API --- include/PropLibTemplate.h | 18 ++++++++++-------- src/ReturnCodes.cpp | 4 ++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/include/PropLibTemplate.h b/include/PropLibTemplate.h index 6553f22..4a0dbaf 100644 --- a/include/PropLibTemplate.h +++ b/include/PropLibTemplate.h @@ -13,12 +13,14 @@ namespace ITS { // TODO-TEMPLATE: Use your library's namespace -// Define cross-platform EXPORTED +// Define cross-platform PROPLIB_API to export functions #ifndef DOXYGEN_SHOULD_SKIP - #ifdef _WIN32 - #define EXPORTED extern "C" __declspec(dllexport) - #else - #define EXPORTED extern "C" + #ifndef PROPLIB_API + #ifdef _WIN32 + #define PROPLIB_API extern "C" __declspec(dllexport) + #else + #define PROPLIB_API extern "C" + #endif #endif #endif @@ -44,12 +46,12 @@ enum ReturnCode { //////////////////////////////////////////////////////////////////////////////// // Public Functions // TODO-TEMPLATE: Add functions which should be exported in the DLL -EXPORTED char *GetReturnStatusCharArray(const int code); -EXPORTED void FreeReturnStatusCharArray(char *c_msg); +PROPLIB_API char *GetReturnStatusCharArray(const int code); +PROPLIB_API void FreeReturnStatusCharArray(char *c_msg); //////////////////////////////////////////////////////////////////////////////// // Private Functions -// TODO-TEMPLATE: Add other/internal functions here (no need for "EXPORTED") +// TODO-TEMPLATE: Add other/internal functions here (no need for "PROPLIB_API") std::string GetReturnStatus(const int code); } // namespace ITS diff --git a/src/ReturnCodes.cpp b/src/ReturnCodes.cpp index a37f665..2dde4dd 100644 --- a/src/ReturnCodes.cpp +++ b/src/ReturnCodes.cpp @@ -56,7 +56,7 @@ std::string GetReturnStatus(int code) { * @param[in] code Integer return code. * @return A status message corresponding to the input code. ******************************************************************************/ -EXPORTED char *GetReturnStatusCharArray(const int code) { +char *GetReturnStatusCharArray(const int code) { const std::string msg = GetReturnStatus(code); char *c_msg = new char[msg.size() + 1]; #ifdef _WIN32 @@ -72,7 +72,7 @@ EXPORTED char *GetReturnStatusCharArray(const int code) { * * @param[in] c_msg The status message C-style string to delete ******************************************************************************/ -EXPORTED void FreeReturnStatusCharArray(char *c_msg) { +void FreeReturnStatusCharArray(char *c_msg) { delete[] c_msg; } From 57fdc5b6f1585b27a2e82ff8b7cf34c5baa07003 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:52:53 -0500 Subject: [PATCH 260/379] Adjust for single library header --- app/include/Structs.h | 2 +- include/ITS.ITU.PSeries.P2108/Enums.h | 40 ------ include/ITS.ITU.PSeries.P2108/P2108.h | 79 ---------- include/ITS.ITU.PSeries.P2108/ReturnCodes.h | 43 ------ include/P2108.h | 136 ++++++++++++++++++ include/PropLibTemplate.h | 57 -------- src/AeronauticalStatisticalModel.cpp | 2 +- src/HeightGainTerminalCorrectionModel.cpp | 2 +- ...rseComplementaryCumulativeDistribution.cpp | 2 +- src/TerrestrialStatisticalModel.cpp | 2 +- tests/TestUtils.h | 2 +- 11 files changed, 142 insertions(+), 225 deletions(-) delete mode 100644 include/ITS.ITU.PSeries.P2108/Enums.h delete mode 100644 include/ITS.ITU.PSeries.P2108/P2108.h delete mode 100644 include/ITS.ITU.PSeries.P2108/ReturnCodes.h create mode 100644 include/P2108.h delete mode 100644 include/PropLibTemplate.h diff --git a/app/include/Structs.h b/app/include/Structs.h index 448863e..bd6dd35 100644 --- a/app/include/Structs.h +++ b/app/include/Structs.h @@ -3,7 +3,7 @@ */ #pragma once -#include "ITS.ITU.PSeries.P2108/Enums.h" // For ClutterType +#include "P2108.h" // For ClutterType enum #include // for std::string diff --git a/include/ITS.ITU.PSeries.P2108/Enums.h b/include/ITS.ITU.PSeries.P2108/Enums.h deleted file mode 100644 index 769af60..0000000 --- a/include/ITS.ITU.PSeries.P2108/Enums.h +++ /dev/null @@ -1,40 +0,0 @@ -/** @file Enums.h - * Enumerated types used by this software - */ -#pragma once - -namespace ITS { -namespace ITU { -namespace PSeries { -namespace P2108 { - -/** Clutter type enum, based on Table 3 in Section 3.1 */ -enum 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 RepresentativeClutterHeight { - R__WATER_SEA = 10, /**< @f$ R @f$ for the trees/forest clutter type */ - R__OPEN_RURAL = 10, /**< @f$ R @f$ for the open/rural clutter type */ - R__SUBURBAN = 10, /**< @f$ R @f$ for the suburban clutter type */ - R__URBAN = 15, /**< @f$ R @f$ for the urban clutter type */ - R__TREES_FOREST = 15, /**< @f$ R @f$ for the trees/forest clutter type */ - R__DENSE_URBAN = 20, /**< @f$ R @f$ for the dense urban clutter type */ -}; - -} // namespace P2108 -} // namespace PSeries -} // namespace ITU -} // namespace ITS diff --git a/include/ITS.ITU.PSeries.P2108/P2108.h b/include/ITS.ITU.PSeries.P2108/P2108.h deleted file mode 100644 index 01cb195..0000000 --- a/include/ITS.ITU.PSeries.P2108/P2108.h +++ /dev/null @@ -1,79 +0,0 @@ -/** @file P2108.h - * Interface header for this library - */ -#pragma once - -#include "Enums.h" -#include "ReturnCodes.h" - -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 - -//////////////////////////////////////////////////////////////////////////////// -// Constants -/** Approximate value of @f$ \pi @f$ */ -constexpr double PI = 3.14159265358979323846; - -//////////////////////////////////////////////////////////////////////////////// -// Public Functions -EXPORTED ReturnCode AeronauticalStatisticalModel( - const double f__ghz, - const double theta__deg, - const double p, - double &L_ces__db -); -EXPORTED ReturnCode TerrestrialStatisticalModel( - const double f__ghz, const double d__km, const double p, double &L_ctt__db -); -EXPORTED ReturnCode 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 -); -EXPORTED char *GetReturnStatusCharArray(const int code); -EXPORTED void FreeReturnStatusCharArray(char *c_msg); - -//////////////////////////////////////////////////////////////////////////////// -// Private Functions -std::string GetReturnStatus(const int code); -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 -); -ReturnCode Section3p1_InputValidation( - const double f__ghz, - const double h__meter, - const double w_s__meter, - const double R__meter -); -ReturnCode Section3p2_InputValidation( - const double f__ghz, const double d__km, const double p -); -ReturnCode 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 diff --git a/include/ITS.ITU.PSeries.P2108/ReturnCodes.h b/include/ITS.ITU.PSeries.P2108/ReturnCodes.h deleted file mode 100644 index 1bf32c1..0000000 --- a/include/ITS.ITU.PSeries.P2108/ReturnCodes.h +++ /dev/null @@ -1,43 +0,0 @@ -/** @file ReturnCodes.h - * Contains return codes used by this software - */ -#pragma once - -#include // for std::string -#include // for std::unordered_map - -namespace ITS { -namespace ITU { -namespace PSeries { -namespace P2108 { - -/******************************************************************************* - * Return Codes defined by this software (0-127) - ******************************************************************************/ -// clang-format off -enum ReturnCode { - SUCCESS = 0, /**< Successful execution */ - - // Section 3.1 Error Codes - ERROR31__FREQUENCY = 32, /**< Frequency must be between 0.3 and 3 GHz */ - ERROR31__ANTENNA_HEIGHT, /**< Antenna height must be @f$ \geq @f$ 0 meters */ - ERROR31__STREET_WIDTH, /**< Street width must be @f$ > @f$ 0 meters */ - ERROR31__CLUTTER_HEIGHT, /**< Representative clutter height must be @f$ > @f$ 0 meters */ - ERROR31__CLUTTER_TYPE, /**< Invalid value for clutter type */ - - // Section 3.2 Error Codes - ERROR32__FREQUENCY = 48, /**< Frequency must be between 2 and 67 GHz */ - ERROR32__DISTANCE, /**< Path distance must be @f$ \geq @f$ 0.25 km */ - ERROR32__PERCENTAGE, /**< Percentage must be between 0 and 100 */ - - // Section 3.3 Error Codes - ERROR33__FREQUENCY = 64, /**< Frequency must be between 10 and 100 GHz */ - ERROR33__THETA, /**< Elevation angle must be between 0 and 100 GHz */ - ERROR33__PERCENTAGE, /**< Percentage must be between 0 and 100 */ -}; -// clang-format on - -} // namespace P2108 -} // namespace PSeries -} // namespace ITU -} // namespace ITS diff --git a/include/P2108.h b/include/P2108.h new file mode 100644 index 0000000..76ccf16 --- /dev/null +++ b/include/P2108.h @@ -0,0 +1,136 @@ +/** @file P2108.h + * Interface header for this library + */ +#pragma once + +#include // for std::string +#include // for std::unordered_map + +namespace ITS { +namespace ITU { +namespace PSeries { +namespace P2108 { + +// Define cross-platform PROPLIB_API to export functions +#ifndef DOXYGEN_SHOULD_SKIP + #ifndef PROPLIB_API + #ifdef _WIN32 + #define PROPLIB_API extern "C" __declspec(dllexport) + #else + #define PROPLIB_API extern "C" + #endif + #endif +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Enums + +/** Clutter type enum, based on Table 3 in Section 3.1 */ +enum 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 RepresentativeClutterHeight { + R__WATER_SEA = 10, /**< @f$ R @f$ for the trees/forest clutter type */ + R__OPEN_RURAL = 10, /**< @f$ R @f$ for the open/rural clutter type */ + R__SUBURBAN = 10, /**< @f$ R @f$ for the suburban clutter type */ + R__URBAN = 15, /**< @f$ R @f$ for the urban clutter type */ + R__TREES_FOREST = 15, /**< @f$ R @f$ for the trees/forest clutter type */ + R__DENSE_URBAN = 20, /**< @f$ R @f$ for the dense urban clutter type */ +}; + +/******************************************************************************* + * Return Codes defined by this software (0-127) + ******************************************************************************/ +// clang-format off +enum ReturnCode { + SUCCESS = 0, /**< Successful execution */ + + // Section 3.1 Error Codes + ERROR31__FREQUENCY = 32, /**< Frequency must be between 0.3 and 3 GHz */ + ERROR31__ANTENNA_HEIGHT, /**< Antenna height must be @f$ \geq @f$ 0 meters */ + ERROR31__STREET_WIDTH, /**< Street width must be @f$ > @f$ 0 meters */ + ERROR31__CLUTTER_HEIGHT, /**< Representative clutter height must be @f$ > @f$ 0 meters */ + ERROR31__CLUTTER_TYPE, /**< Invalid value for clutter type */ + + // Section 3.2 Error Codes + ERROR32__FREQUENCY = 48, /**< Frequency must be between 2 and 67 GHz */ + ERROR32__DISTANCE, /**< Path distance must be @f$ \geq @f$ 0.25 km */ + ERROR32__PERCENTAGE, /**< Percentage must be between 0 and 100 */ + + // Section 3.3 Error Codes + ERROR33__FREQUENCY = 64, /**< Frequency must be between 10 and 100 GHz */ + ERROR33__THETA, /**< Elevation angle must be between 0 and 100 GHz */ + ERROR33__PERCENTAGE, /**< Percentage must be between 0 and 100 */ +}; +// clang-format on + +//////////////////////////////////////////////////////////////////////////////// +// Constants +/** Approximate value of @f$ \pi @f$ */ +constexpr double PI = 3.14159265358979323846; + +//////////////////////////////////////////////////////////////////////////////// +// Public Functions +PROPLIB_API ReturnCode AeronauticalStatisticalModel( + const double f__ghz, + const double theta__deg, + const double p, + double &L_ces__db +); +PROPLIB_API ReturnCode TerrestrialStatisticalModel( + const double f__ghz, const double d__km, const double p, double &L_ctt__db +); +PROPLIB_API ReturnCode 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 +); +PROPLIB_API char *GetReturnStatusCharArray(const int code); +PROPLIB_API void FreeReturnStatusCharArray(char *c_msg); + +//////////////////////////////////////////////////////////////////////////////// +// Private Functions +std::string GetReturnStatus(const int code); +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 +); +ReturnCode Section3p1_InputValidation( + const double f__ghz, + const double h__meter, + const double w_s__meter, + const double R__meter +); +ReturnCode Section3p2_InputValidation( + const double f__ghz, const double d__km, const double p +); +ReturnCode 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 diff --git a/include/PropLibTemplate.h b/include/PropLibTemplate.h deleted file mode 100644 index 4a0dbaf..0000000 --- a/include/PropLibTemplate.h +++ /dev/null @@ -1,57 +0,0 @@ -/** @file PropLibTemplate.h - * Interface header for this library - * // TODO-TEMPLATE: Rename this file to your ${LIB_NAME} - */ -#pragma once - -#include // for std::string -#include // for std::unordered_map - -// TODO-TEMPLATE: This header should provide EVERYTHING needed to interface -// with the shared library, without needing to include other headers. - -namespace ITS { -// TODO-TEMPLATE: Use your library's namespace - -// Define cross-platform PROPLIB_API to export functions -#ifndef DOXYGEN_SHOULD_SKIP - #ifndef PROPLIB_API - #ifdef _WIN32 - #define PROPLIB_API extern "C" __declspec(dllexport) - #else - #define PROPLIB_API extern "C" - #endif - #endif -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Enums - -/******************************************************************************* - * Return Codes defined by this software (0-127) - ******************************************************************************/ -// clang-format off -enum ReturnCode { - SUCCESS = 0, /**< Successful execution */ - - // TODO-TEMPLATE add return codes for this software - // TODO-TEMPLATE: Add corresponding status messages in src/ReturnCodes.cpp -}; -// clang-format on - -//////////////////////////////////////////////////////////////////////////////// -// Constants -// TODO-TEMPLATE define any global constants here (use constexpr!) - -//////////////////////////////////////////////////////////////////////////////// -// Public Functions -// TODO-TEMPLATE: Add functions which should be exported in the DLL -PROPLIB_API char *GetReturnStatusCharArray(const int code); -PROPLIB_API void FreeReturnStatusCharArray(char *c_msg); - -//////////////////////////////////////////////////////////////////////////////// -// Private Functions -// TODO-TEMPLATE: Add other/internal functions here (no need for "PROPLIB_API") -std::string GetReturnStatus(const int code); - -} // namespace ITS diff --git a/src/AeronauticalStatisticalModel.cpp b/src/AeronauticalStatisticalModel.cpp index 8a93522..930d8a0 100644 --- a/src/AeronauticalStatisticalModel.cpp +++ b/src/AeronauticalStatisticalModel.cpp @@ -1,7 +1,7 @@ /** @file AeronauticalStatisticalModel.cpp * Implements the model from ITU-R P.2108 Section 3.3. */ -#include "ITS.ITU.PSeries.P2108/P2108.h" +#include "P2108.h" #include // for std::pow, std::log, std::tan diff --git a/src/HeightGainTerminalCorrectionModel.cpp b/src/HeightGainTerminalCorrectionModel.cpp index 9e60179..09d6678 100644 --- a/src/HeightGainTerminalCorrectionModel.cpp +++ b/src/HeightGainTerminalCorrectionModel.cpp @@ -1,7 +1,7 @@ /** @file HeightGainTerminalCorrectionModel.cpp * Implements the model from ITU-R P.2108 Section 3.1. */ -#include "ITS.ITU.PSeries.P2108/P2108.h" +#include "P2108.h" #include // for std::atan, std::log10, std::pow, std::sqrt diff --git a/src/InverseComplementaryCumulativeDistribution.cpp b/src/InverseComplementaryCumulativeDistribution.cpp index 127534b..139d465 100644 --- a/src/InverseComplementaryCumulativeDistribution.cpp +++ b/src/InverseComplementaryCumulativeDistribution.cpp @@ -1,7 +1,7 @@ /** @internal @file InverseComplementaryCumulativeDistribution.cpp * @brief Implements a function to calculate the inverse CCDF. */ -#include "ITS.ITU.PSeries.P2108/P2108.h" +#include "P2108.h" #include // for std::log, std::sqrt #include // for std::out_of_range diff --git a/src/TerrestrialStatisticalModel.cpp b/src/TerrestrialStatisticalModel.cpp index e8c9d59..faa232a 100644 --- a/src/TerrestrialStatisticalModel.cpp +++ b/src/TerrestrialStatisticalModel.cpp @@ -1,7 +1,7 @@ /** @file TerrestrialStatisticalModel.cpp * Implements the model from ITU-R P.2108 Section 3.2. */ -#include "ITS.ITU.PSeries.P2108/P2108.h" +#include "P2108.h" #include // for std::fmin, std::log10, std::pow, std::sqrt diff --git a/tests/TestUtils.h b/tests/TestUtils.h index 6d973c7..21b3f4f 100644 --- a/tests/TestUtils.h +++ b/tests/TestUtils.h @@ -1,6 +1,6 @@ #pragma once -#include "ITS.ITU.PSeries.P2108/P2108.h" +#include "P2108.h" #include // for std::string #include // for std::vector From 8d740b2c3fd22a3d9f5f17e863817e5d59ba1e0e Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:56:05 -0500 Subject: [PATCH 261/379] Remove "U.S. reference implementation" --- CITATION.cff | 3 +-- README.md | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index 59a1eb5..549da1c 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -1,7 +1,6 @@ cff-version: 1.2.0 title: >- - Recommendation ITU-R P.2108-1, U.S. Reference Software - Implementation + Recommendation ITU-R P.2108 message: Please cite this software using these metadata. type: software authors: diff --git a/README.md b/README.md index 24c1f60..c2a841f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Recommendation ITU-R P.2108 - U.S. Reference Implementation # +# Recommendation ITU-R P.2108 # [![NTIA/ITS PropLib][proplib-badge]][proplib-link] [![GitHub Release][gh-releases-badge]][gh-releases-link] From 0009197027645f46135fc9e1858b8e208ba27204 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:56:23 -0500 Subject: [PATCH 262/379] Remove "U.S. Reference implementation" --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d8c446..253bbad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ set(LIB_NAMESPACE "ITS.ITU.PSeries") # Namespace for the named library project( "${LIB_NAMESPACE}.${LIB_NAME}" VERSION 1.0 - DESCRIPTION "Recommendation ITU-R P.2108, U.S. Reference Implementation" + DESCRIPTION "Recommendation ITU-R P.2108" HOMEPAGE_URL "https://ntia.github.io/propagation-library-wiki/models/P2108" LANGUAGES "CXX" ) From 954ea6e78d92bf360bf2ae409d9f3d86d63dbfdf Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 22 Nov 2024 13:52:49 -0500 Subject: [PATCH 263/379] Fix CFF validator path filters --- .github/workflows/cff-validator.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cff-validator.yml b/.github/workflows/cff-validator.yml index a01dee3..83d75d0 100644 --- a/.github/workflows/cff-validator.yml +++ b/.github/workflows/cff-validator.yml @@ -3,12 +3,12 @@ name: Validate CITATION.cff on: push: paths: - - CITATION.cff - - .github/workflows/cff-validator.yml + - 'CITATION.cff' + - '.github/workflows/cff-validator.yml' pull_request: paths: - - CITATION.cff - - .github/workflows/cff-validator.yml + - 'CITATION.cff' + - '.github/workflows/cff-validator.yml' workflow_dispatch: jobs: From 03b9c0c407bbf454600e520a8af882a5dc65091d Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 26 Nov 2024 14:32:06 -0500 Subject: [PATCH 264/379] add link to driver readme --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dbaac3a..a45d9c3 100644 --- a/README.md +++ b/README.md @@ -63,11 +63,15 @@ for .NET, MATLAB, and Python in the following repositories: TODO-TEMPLATE: Update links in this section, if applicable TODO-TEMPLATE: Otherwise, add correct "getting started" information here. -To get started using this model, refer to +To get started using this library, refer to [its page on the **NTIA/ITS Propagation Library Wiki**](https://ntia.github.io/propagation-library-wiki/models/TODO-TEMPLATE/). There, you will find installation instructions, usage information, and code examples for all supported languages. +An executable is also provided which can be used to run the functions provided +by this library using plain text input and output files. Installation and usage +details for the command-line driver are provided in [its own README](./app/README.md). + 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/TODO-TEMPLATE), and a guide for contributors From 45cdfcf44d4575042cc5d6f2f68726cc3a57e555 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 26 Nov 2024 14:32:22 -0500 Subject: [PATCH 265/379] Remove old test data directory structure --- tests/TestUtils.cpp | 5 ++++- tests/data/README.md | 4 ---- 2 files changed, 4 insertions(+), 5 deletions(-) delete mode 100644 tests/data/README.md diff --git a/tests/TestUtils.cpp b/tests/TestUtils.cpp index c3e40db..fd75d92 100644 --- a/tests/TestUtils.cpp +++ b/tests/TestUtils.cpp @@ -15,8 +15,11 @@ void appendDirectorySep(std::string &str) { std::string getDataDirectory() { std::string dataDir(__FILE__); dataDir.resize(dataDir.find_last_of("/\\")); + dataDir.resize(dataDir.find_last_of("/\\")); + appendDirectorySep(dataDir); + dataDir += "extern"; appendDirectorySep(dataDir); - dataDir += "data"; + dataDir += "TODO-TEMPLATE"; // Name of data directory as clone in the `extern` directory appendDirectorySep(dataDir); return dataDir; } \ No newline at end of file diff --git a/tests/data/README.md b/tests/data/README.md deleted file mode 100644 index 1ba5dab..0000000 --- a/tests/data/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# TODO-TEMPLATE - -Populate this folder with example input and output files for -use in unit tests. After populating this folder, delete this file. From 04ef5ced4b9ebef050b0044464b5b2bea6217e3f Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:13:03 -0500 Subject: [PATCH 266/379] Use newer add_compile_definitions --- app/tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index e79c6ec..a32cac5 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -24,7 +24,7 @@ target_include_directories( target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME}) # Make driver executable location available to source -add_definitions(-DDRIVER_LOCATION="$") +add_compile_definitions(DRIVER_LOCATION="$") ########################################### ## SET UP AND DISCOVER TESTS From 85e79f45f83329468c1c7a2b3560c1a9fbdbff4e Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:13:26 -0500 Subject: [PATCH 267/379] Support (non-default) static library compilation --- CMakeLists.txt | 14 +++++++++++++- CMakePresets.json | 3 ++- src/CMakeLists.txt | 32 ++++++++++++++++++++++---------- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f1acb6..5c6257c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,7 +46,7 @@ add_library(proplib_compiler_flags INTERFACE) target_compile_features(proplib_compiler_flags INTERFACE cxx_std_11) # add compiler warning flags just when building this project via -# the BUILD_INTERFACE genex +# the BUILD_INTERFACE generator expression set(gcc_like_cxx "$") set(msvc_cxx "$") target_compile_options(proplib_compiler_flags INTERFACE @@ -71,11 +71,23 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" CACHE STRING "Set if (NOT DOCS_ONLY) add_subdirectory(src) # Build the shared library if (RUN_TESTS OR RUN_DRIVER_TESTS) + # Set up GoogleTest if (EXISTS "${PROJECT_SOURCE_DIR}/extern/googletest/CMakeLists.txt") enable_testing() set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + # Ensure GoogleTest is built as a static library + if (DEFINED BUILD_SHARED_LIBS AND BUILD_SHARED_LIBS) + set(BUILD_SHARED_LIBS_${LIB_NAME} ${BUILD_SHARED_LIBS}) + set(BUILD_SHARED_LIBS OFF) + message(STATUS "STATUS: BUILD_SHARED_LIBS is " ${BUILD_SHARED_LIBS} " to build gtest library.") + endif () add_subdirectory("${PROJECT_SOURCE_DIR}/extern/googletest" "extern/googletest") include(GoogleTest) + # Restore initial value of BUILD_SHARED_LIBS + if (DEFINED BUILD_SHARED_LIBS_${LIB_NAME}) + set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_${LIB_NAME}}) + message(STATUS "STATUS: BUILD_SHARED_LIBS is " ${BUILD_SHARED_LIBS} " to build ${LIB_NAME} library.") + endif () else () message(SEND_ERROR "Unable to build tests. GoogleTest submodule is missing. " diff --git a/CMakePresets.json b/CMakePresets.json index 0fdfc3a..f974223 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -14,7 +14,8 @@ "cacheVariables": { "CMAKE_BUILD_RPATH_USE_ORIGIN": "ON", "DOCS_ONLY": "OFF", - "RUN_TESTS": "ON" + "RUN_TESTS": "ON", + "BUILD_SHARED_LIBS": "ON" } }, { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c040b46..268066b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,22 +1,33 @@ ########################################### ## BUILD THE LIBRARY ########################################### -## TODO-TEMPLATE: Include source AND header files here. -## TODO-TEMPLATE: See CREATING-REPOSITORIES.md for examples. -add_library( - ${LIB_NAME} SHARED +message(STATUS "STATUS: Start building the library: " ${LIB_NAME}) + +set(LIB_HEADERS "${PROJECT_SOURCE_DIR}/include") +set(LIB_FILES "ReturnCodes.cpp" - "${PROJECT_SOURCE_DIR}/include/${LIB_NAME}.h" + "${LIB_HEADERS}/${LIB_NAME}.h" + ## TODO-TEMPLATE: Include source AND header files here. ) +# By default, create shared library +if (NOT DEFINED BUILD_SHARED_LIBS) + message(STATUS "STATUS: BUILD_SHARED_LIBS is not defined to build the library: " ${LIB_NAME} ".") + add_library(${LIB_NAME} SHARED ${LIB_FILES}) +else () + message(STATUS "STATUS: BUILD_SHARED_LIBS is " ${BUILD_SHARED_LIBS} " to build the library: " ${LIB_NAME} ".") + add_library(${LIB_NAME} ${LIB_FILES}) +endif () + + # Add the include directory -target_include_directories(${LIB_NAME} PUBLIC "${PROJECT_SOURCE_DIR}/include") +target_include_directories(${LIB_NAME} PUBLIC "${LIB_HEADERS}") -# Add definition to get the library name inside the library -add_definitions(-DLIBRARY_NAME="${LIB_NAME}") +# link library to proplib_compiler_flags +target_link_libraries(${LIB_NAME} PUBLIC proplib_compiler_flags) -# Set minimum C++ version to C++11 -target_compile_features(${LIB_NAME} PUBLIC cxx_std_11) +# Add definition to get the library name inside the library +add_compile_definitions(LIBRARY_NAME="${LIB_NAME}") # Platform-specific configurations if (WIN32) @@ -31,4 +42,5 @@ endif () set_target_properties( ${LIB_NAME} PROPERTIES OUTPUT_NAME ${LIB_NAME}-${PROJECT_VERSION} + VERSION ${PROJECT_VERSION} ) From 15b3d95e0f033d8c2f2f4e5710d6a6a090d80143 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:13:35 -0500 Subject: [PATCH 268/379] Use newer add_compile_definitions --- app/src/CMakeLists.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index 4ec7247..592ffa5 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -22,10 +22,12 @@ target_include_directories(${DRIVER_NAME} PUBLIC "${DRIVER_HEADERS}") target_link_libraries(${DRIVER_NAME} ${LIB_NAME}) # Add definitions to enable version identification inside the driver -add_definitions(-DLIBRARY_VERSION="${PROJECT_VERSION}") -add_definitions(-DDRIVER_VERSION="${DRIVER_VERSION}") -add_definitions(-DLIBRARY_NAME="${LIB_NAME}") -add_definitions(-DDRIVER_NAME="${DRIVER_NAME}") +add_compile_definitions( + LIBRARY_VERSION="${PROJECT_VERSION}" + DRIVER_VERSION="${DRIVER_VERSION}" + LIBRARY_NAME="${LIB_NAME}" + DRIVER_NAME="${DRIVER_NAME}" +) # Set some target metadata set_target_properties( From cf6906cf2d7d022882db1382bda5badfae8683b2 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:19:20 -0500 Subject: [PATCH 269/379] remove leftover template comment --- src/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0858d43..30993a0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,7 +11,6 @@ set(LIB_FILES "TerrestrialStatisticalModel.cpp" "ReturnCodes.cpp" "${LIB_HEADERS}/${LIB_NAME}.h" - ## TODO-TEMPLATE: Include source AND header files here. ) # By default, create shared library From 1e609df7a02f9037038f44abbf268e73c7946cbc Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:19:34 -0500 Subject: [PATCH 270/379] Fix test data directory name (does not exist yet) --- tests/TestUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/TestUtils.cpp b/tests/TestUtils.cpp index e7b6a9b..75b6390 100644 --- a/tests/TestUtils.cpp +++ b/tests/TestUtils.cpp @@ -20,7 +20,7 @@ std::string getDataDirectory() { appendDirectorySep(dataDir); dataDir += "extern"; appendDirectorySep(dataDir); - dataDir += "TODO-TEMPLATE"; // Name of data directory as clone in the `extern` directory + dataDir += "p2108-test-data"; appendDirectorySep(dataDir); return dataDir; } From 4ea139517ece2a26aebeefd923e733476f5563e5 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:19:44 -0500 Subject: [PATCH 271/379] fix typo --- tests/TestUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/TestUtils.cpp b/tests/TestUtils.cpp index fd75d92..b7bfa92 100644 --- a/tests/TestUtils.cpp +++ b/tests/TestUtils.cpp @@ -19,7 +19,7 @@ std::string getDataDirectory() { appendDirectorySep(dataDir); dataDir += "extern"; appendDirectorySep(dataDir); - dataDir += "TODO-TEMPLATE"; // Name of data directory as clone in the `extern` directory + dataDir += "TODO-TEMPLATE"; // Name of data directory as cloned in the `extern` directory appendDirectorySep(dataDir); return dataDir; } \ No newline at end of file From a621d1eba6b46fa7f5274f431aa86ecbb1395a89 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:36:37 -0500 Subject: [PATCH 272/379] Add macOS multi-architecture logic --- CMakeLists.txt | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c6257c..32dec66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,28 @@ # >=3.14 required for GoogleTest v1.12.x cmake_minimum_required(VERSION 3.14 FATAL_ERROR) +# If on macOS, handle arm64/x86_64 architectures (must be done before project()) +if (APPLE) + # Get the current platform's native architecture + execute_process( + COMMAND uname -m + RESULT_VARIABLE result + OUTPUT_VARIABLE MACOS_NATIVE_ARCHITECTURE + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + message(STATUS "Current macOS architecture is " ${MACOS_NATIVE_ARCHITECTURE}) + + # If running on Apple silicon, try a universal build. Otherwise, a native build. + if ((CMAKE_GENERATOR STREQUAL "Xcode") AND (MACOS_NATIVE_ARCHITECTURE STREQUAL "arm64")) + set(CMAKE_OSX_ARCHITECTURE "arm64;x86_64" CACHE STRING "") + set(PROPLIB_ARCHITECTURE "macOS_universal") + message(STATUS "Configured for universal macOS build") + else () + set(PROPLIB_ARCHITECTURE MACOS_NATIVE_ARCHITECTURE) + message(STATUS "Configured for native macOS build") + endif () +endif () + ########################################### ## PROJECT METADATA ########################################### @@ -58,7 +80,7 @@ target_compile_options(proplib_compiler_flags INTERFACE if (POLICY CMP0141) cmake_policy(SET CMP0141 NEW) set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") -endif() +endif () # control where the static and shared libraries are built set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" CACHE STRING "Set the CMAKE Archive Output Directory") From 1b6d50f50b061412e9be9b2027b3f8e2f5e1d47f Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:46:13 -0500 Subject: [PATCH 273/379] Add windows x86 runner --- .github/workflows/ctest.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index 9894cc5..450a31d 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -23,16 +23,15 @@ jobs: - ubuntu-latest - macos-latest - windows-latest - - windows-2019 # Used for Windows 32-bit builds - architecture: [arm64, x64] + architecture: [arm64, x64, x86] cmakeVersion: ["3.21", latest] # CMake >= 3.21 is required to use "--preset " and discover generators - exclude: # Only use x64 strategies for Linux and Windows + exclude: + - os: macos-latest + architecture: x86 - os: windows-latest architecture: arm64 - - os: windows-2019 - architecture: arm64 - os: ubuntu-latest - architecture: arm64 + architecture: [arm64, x86] steps: - name: Checkout repository uses: actions/checkout@v4 From 3c27db4dbe149573b1ee7de4b7f11fda2741e812 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:46:57 -0500 Subject: [PATCH 274/379] fix matrix architecture exclude --- .github/workflows/ctest.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index 450a31d..0ada805 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -31,7 +31,9 @@ jobs: - os: windows-latest architecture: arm64 - os: ubuntu-latest - architecture: [arm64, x86] + architecture: arm64 + - os: ubuntu-latest + architecture: x86 steps: - name: Checkout repository uses: actions/checkout@v4 From 6ef11240de4c417bd6392a516e9ed091b6d2e9aa Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:48:47 -0500 Subject: [PATCH 275/379] try OS type name in action matrix names --- .github/workflows/ctest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index 0ada805..c579c51 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -15,7 +15,7 @@ concurrency: # Define the matrix for different operating systems jobs: build-and-test: - name: ${{ matrix.os }} / ${{ matrix.architecture }} / CMake ${{ matrix.cmakeVersion }} + name: ${{ runner.os }} / ${{ matrix.architecture }} / CMake ${{ matrix.cmakeVersion }} runs-on: ${{ matrix.os }} strategy: matrix: From 193b04603fe7e8cfa30350619bc57ee01c7ce3e9 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:49:13 -0500 Subject: [PATCH 276/379] Revert "try OS type name in action matrix names" This reverts commit 6ef11240de4c417bd6392a516e9ed091b6d2e9aa. --- .github/workflows/ctest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index c579c51..0ada805 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -15,7 +15,7 @@ concurrency: # Define the matrix for different operating systems jobs: build-and-test: - name: ${{ runner.os }} / ${{ matrix.architecture }} / CMake ${{ matrix.cmakeVersion }} + name: ${{ matrix.os }} / ${{ matrix.architecture }} / CMake ${{ matrix.cmakeVersion }} runs-on: ${{ matrix.os }} strategy: matrix: From 39cd0a1525015e2547efa6b8c7fd6e157e5e61f6 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:54:23 -0500 Subject: [PATCH 277/379] Add compiler flags MSVC: always use /Gz G++ equivalents: none required for Gz --- CMakeLists.txt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 32dec66..0b7f878 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,8 +72,18 @@ target_compile_features(proplib_compiler_flags INTERFACE cxx_std_11) set(gcc_like_cxx "$") set(msvc_cxx "$") target_compile_options(proplib_compiler_flags INTERFACE - "$<${gcc_like_cxx}:$>" - "$<${msvc_cxx}:$>" + # For GCC-like compilers in any configuration + "$<${gcc_like_cxx}:$>" + # For GCC-like compilers in Release configurations + "$<${gcc_like_cxx}:$<$:-O3;-DNDEBUG>>" + # For GCC-like compilers in Debug configurations + "$<${gcc_like_cxx}:$<$:-g;-O0>>" + # For MSVC compiler in any configuration + "$<${msvc_cxx}:$>" + # For MSVC compiler in Release configurations + "$<${msvc_cxx}:$<$:/O2;/DNDEBUG>>" + # For MSVC compiler in Debug configurations + "$<${msvc_cxx}:$<$:/Od;/Zi>>" ) # Enable Hot Reload for MSVC compilers if supported. From bcb09a2baec5ecf50e4db9b0e60eebc3d86a173b Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:54:30 -0500 Subject: [PATCH 278/379] Fix incorrect field used --- CITATION.cff | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CITATION.cff b/CITATION.cff index 30b6fb5..c5036b1 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -18,8 +18,9 @@ authors: Institute for Telecommunication Sciences address: 325 Broadway city: Boulder - country: CO + country: US post-code: '80305' + region: Colorado alias: NTIA/ITS email: code@ntia.gov website: 'https://its.ntia.gov' From f1b65b9c4ff07728dcefd1a421ba30ac13c72301 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 2 Dec 2024 17:57:03 -0500 Subject: [PATCH 279/379] Update release approval form for new data structure --- GitHubRepoPublicReleaseApproval.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GitHubRepoPublicReleaseApproval.md b/GitHubRepoPublicReleaseApproval.md index ad5714a..85235a5 100644 --- a/GitHubRepoPublicReleaseApproval.md +++ b/GitHubRepoPublicReleaseApproval.md @@ -17,7 +17,7 @@ mark next to each attests that the criterion has been met. all template or placeholder code has been removed. * [ ] The repository includes the appropriate `LICENSE.md` file 2. [ ] Any test data necessary for the code and its unit tests to function is included in this -GitHub repository, or in a parent repository which includes this one as a Git submodule. +GitHub repository, either directly or as a linked Git submodule. 3. [ ] The README.md file has passed editorial review from the ITS Publications Office. 4. [ ] 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. From 2e0a55497b0ebe434f9e671020b94b93be8d3807 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 2 Dec 2024 17:58:44 -0500 Subject: [PATCH 280/379] Link test data template in README --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a45d9c3..a22a4b4 100644 --- a/README.md +++ b/README.md @@ -39,11 +39,14 @@ to develop a cross-platform C++ library as part of PropLib. Instructions on how to use this repository are found in its [GitHub Wiki](https://github.com/NTIA/proplib-template/wiki). Additional template repositories exist for building .NET, MATLAB, and Python -wrappers for PropLib C++ libraries. See: +wrappers for PropLib C++ libraries. Finally, another template is available as +an example of a submodule repository to provide common test data to all versions +of the software. See: - [NTIA/proplib-template-dotnet](https://github.com/NTIA/proplib-template-dotnet) - [NTIA/proplib-template-matlab](https://github.com/NTIA/proplib-template-matlab) - [NTIA/proplib-template-python](https://github.com/NTIA/proplib-template-python) +- [NTIA/proplib-template-test-data](https://github.com/NTIA/proplib-template-test-data) ## Contact ## From 8bb2fc2fe8c6b2ba53ba2b3753d39eac03f3f2dd Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 2 Dec 2024 18:02:31 -0500 Subject: [PATCH 281/379] Add DOI badge to README template --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index a22a4b4..9a298e5 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ - The fifth badge displays open GitHub Issues - Update the repository name in [gh-issues-badge] - Update the repository name in [gh-issues-link] +- The sixth badge displays and links the Zenodo DOI + - Get your repository ID from https://api.github.com/repos/NTIA/{repo} + - Or, if private, follow: https://stackoverflow.com/a/47223479 + - Populate the repository ID in [doi-link] and [doi-badge] --> [![NTIA/ITS PropLib][proplib-badge]][proplib-link] [proplib-badge]: https://img.shields.io/badge/PropLib-badge?label=%F0%9F%87%BA%F0%9F%87%B8%20NTIA%2FITS&labelColor=162E51&color=D63E04 [proplib-link]: https://ntia.github.io/propagation-library-wiki @@ -31,6 +36,8 @@ [gh-releases-link]: https://github.com/NTIA/proplib-template/releases [gh-issues-badge]: https://img.shields.io/github/issues/NTIA/proplib-template?logo=github&label=Issues&labelColor=162E51 [gh-issues-link]: https://github.com/NTIA/proplib-template/issues +[doi-badge]: https://zenodo.org/badge/TODO-TEMPLATE.svg +[doi-link]: https://zenodo.org/badge/latestdoi/TODO-TEMPLATE This code repository is a template repository for software in the NTIA/ITS From 8596ae5cac67bc06ced634dc4c186031dd0c519b Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 2 Dec 2024 18:17:24 -0500 Subject: [PATCH 282/379] Add template zenodo file --- .zenodo.json | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 .zenodo.json diff --git a/.zenodo.json b/.zenodo.json new file mode 100644 index 0000000..b468ee5 --- /dev/null +++ b/.zenodo.json @@ -0,0 +1,51 @@ +{ + "upload_type": "software", + "publication_date": "TODO-TEMPLATE", + "title": "TODO-TEMPLATE", + "creators": [ + { + "name": "TODO-TEMPLATE", + "affiliation": "U.S. Department of Commerce, National Telecommunications and Information Administration, Institute for Telecommunication Sciences", + "orcid": "TODO-TEMPLATE" + } + ], + "description": "TODO-TEMPLATE. Make this the same as the abstract.", + "access_right": "open", + "keywords": [ + "TODO-TEMPLATE", + "TODO-TEMPLATE" + ], + "related_identifiers": [ + { + "identifier": "https://github.com/NTIA/TODO-TEMPLATE-dotnet", + "relation": "isSupplementedBy", + "resource_type": "software" + }, + { + "identifier": "https://github.com/NTIA/TODO-TEMPLATE-matlab", + "relation": "isSupplementedBy", + "resource_type": "software" + }, + { + "identifier": "https://github.com/NTIA/TODO-TEMPLATE-python", + "relation": "isSupplementedBy", + "resource_type": "software" + }, + { + "identifier": "https://github.com/NTIA/TODO-TEMPLATE-test-data", + "relation": "isSupplementedBy", + "resource_type": "software" + }, + { + "identifier": "https://ntia.github.io/TODO-TEMPLATE/", + "relation": "isDocumentedBy", + "resource_type": "softwaredocumentation" + }, + { + "identifier": "https://ntia.github.io/propagation-library-wiki/models/TODO-TEMPLATE/", + "relation": "isDocumentedBy", + "resource_type": "softwaredocumentation" + } + ], + "version": "TODO-TEMPLATE" +} \ No newline at end of file From 27aa7f2d8d3cd8f2651307ef1c73664b1c999071 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 2 Dec 2024 18:30:50 -0500 Subject: [PATCH 283/379] Fix resource_type in zenodo file --- .zenodo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.zenodo.json b/.zenodo.json index b468ee5..59d66ac 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -34,7 +34,7 @@ { "identifier": "https://github.com/NTIA/TODO-TEMPLATE-test-data", "relation": "isSupplementedBy", - "resource_type": "software" + "resource_type": "dataset" }, { "identifier": "https://ntia.github.io/TODO-TEMPLATE/", From 8379ecee397a0aaeb4be65ef5b39da268b844c86 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 2 Dec 2024 18:59:25 -0500 Subject: [PATCH 284/379] Consistent markdown formatting --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2d2e605..e2bab56 100644 --- a/README.md +++ b/README.md @@ -96,11 +96,11 @@ 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) -* [P2108 Wiki Page](https://ntia.github.io/propagation-library-wiki/models/P2108) -* [`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) +- [ITS Propagation Library Wiki](https://ntia.github.io/propagation-library-wiki) +- [P2108 Wiki Page](https://ntia.github.io/propagation-library-wiki/models/P2108) +- [`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 ## From 682d960669b86372404a9c8c8ba13a8e4dda60f7 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 2 Dec 2024 18:59:36 -0500 Subject: [PATCH 285/379] Add public release approval form --- GitHubRepoPublicReleaseApproval.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/GitHubRepoPublicReleaseApproval.md b/GitHubRepoPublicReleaseApproval.md index 85235a5..2a4bafe 100644 --- a/GitHubRepoPublicReleaseApproval.md +++ b/GitHubRepoPublicReleaseApproval.md @@ -2,31 +2,31 @@ **Project Name:** NTIA/OSM Research and Development -**Software Name:** TODO-TEMPLATE +**Software Name:** Recommendation ITU-R P.2108 The project identified above, which is contained within the repository this document is stored in, has met the following criteria for public release: -1. [ ] The project, including the test criteria, meets the requirements defined +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. - * [ ] Unit tests are available and the software has been tested against the unit tests. - * [ ] The software can be compiled and/or used on Windows, macOS, and Linux. - * [ ] The repository structure and contents follow from the ITS PropLib template, and + * [x] Unit tests are available and the software has been tested against the unit tests. + * [x] The software can be compiled and/or used on Windows, macOS, and Linux. + * [x] The repository structure and contents follow from the ITS PropLib template, and all template or placeholder code has been removed. - * [ ] The repository includes the appropriate `LICENSE.md` file -2. [ ] Any test data necessary for the code and its unit tests to function is included in this + * [x] The repository includes the appropriate `LICENSE.md` file +2. [x] Any test data necessary for the code and its unit tests to function is included in this GitHub repository, either directly or as a linked Git submodule. -3. [ ] The README.md file has passed editorial review from the ITS Publications Office. -4. [ ] The project complies with the ITS Code Style Guide or an appropriate style +3. [x] The README.md file has passed editorial review from the ITS Publications Office. +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. [ ] Approved disclaimer and licensing language has been included. +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: TODO-TEMPLATE -* Supervising Division Chief or Release Authority: TODO-TEMPLATE +* Project Lead: Brian Lain +* Supervising Division Chief or Release Authority: Chris Anderson From 9077b352eff3c0089eab4fbaf6a1a7615c6ba9f3 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 2 Dec 2024 18:59:43 -0500 Subject: [PATCH 286/379] Remove test data --- .../AeronauticalStatisticalModelTestData.csv | 14 ----------- ...ghtGainTerminalCorrectionModelTestData.csv | 24 ------------------- .../TerrestrialStatisticalModelTestData.csv | 13 ---------- 3 files changed, 51 deletions(-) delete mode 100644 tests/data/AeronauticalStatisticalModelTestData.csv delete mode 100644 tests/data/HeightGainTerminalCorrectionModelTestData.csv delete mode 100644 tests/data/TerrestrialStatisticalModelTestData.csv diff --git a/tests/data/AeronauticalStatisticalModelTestData.csv b/tests/data/AeronauticalStatisticalModelTestData.csv deleted file mode 100644 index 9ed7120..0000000 --- a/tests/data/AeronauticalStatisticalModelTestData.csv +++ /dev/null @@ -1,14 +0,0 @@ -f__ghz,theta_deg,p,rtn,L_ces__db -30,2,5,0,7.7 -30,2,1,0,1.9 -30,2,99,0,87.3 -10,10.5,45,0,12.4 -15,90,50,0,0 -20,0,50,0,45.6 -11.1,15.5,80.5,0,14.7 -9.9,45,45,64,0 -100.1,45,45,64,0 -18,-0.1,50,65,0 -18,90.1,50,65,0 -22,25,0,66,0 -22,25,100,66,0 diff --git a/tests/data/HeightGainTerminalCorrectionModelTestData.csv b/tests/data/HeightGainTerminalCorrectionModelTestData.csv deleted file mode 100644 index aa47f89..0000000 --- a/tests/data/HeightGainTerminalCorrectionModelTestData.csv +++ /dev/null @@ -1,24 +0,0 @@ -f__ghz,h__meter,w_s__meter,R__meter,clutter_type,rtn,A_h__db -1.5,2,27,10,1,0,16 -1.5,2,27,6,1,0,10.9 -1.5,2,27,10,2,0,16 -1.5,2,27,6,2,0,10.9 -1.5,2,27,10,3,0,20.5 -1.5,2,27,6,3,0,14.6 -1.5,2,27,15,4,0,24.5 -1.5,2,27,6,4,0,14.6 -1.5,2,27,15,5,0,24.5 -1.5,2,27,6,5,0,14.6 -1.5,2,27,20,6,0,27.1 -1.5,2,27,6,6,0,14.6 -3,3,15,15,6,0,29 -0.9,2.3,30,10,2,0,13.7 -1.5,2.5,25,10,3,0,20.2 -0.03,2.1,24.5,9.8,3,0,5.7 -1.7,30,24.5,9.8,3,0,0 -1.7,30,24.5,30,3,0,0 -0.02,2,27,10,3,32,0 -4,2,27,10,3,32,0 -1,0,10,9,2,33,0 -2,1,0,9,6,34,0 -2,1,27,0,6,35,0 \ No newline at end of file diff --git a/tests/data/TerrestrialStatisticalModelTestData.csv b/tests/data/TerrestrialStatisticalModelTestData.csv deleted file mode 100644 index 5ccadc8..0000000 --- a/tests/data/TerrestrialStatisticalModelTestData.csv +++ /dev/null @@ -1,13 +0,0 @@ -f__ghz,d__km,p,rtn,L_ctt__db -0.5,0.25,50,0,17.4 -0.5,0.25,1,0,3.9 -0.5,0.25,99,0,30.9 -26.6,15.8,45,0,32.5 -67,5.4,30.5,0,30.9 -3.5,1,0.1,0,16.8 -3.5,1,99.9,0,42.8 -0.24,2,50,48,0 -67.1,5,50,48,0 -10,0.24,50,49,0 -6,3,0,50,0 -6,3,100,50,0 From 16b3b132522dfa093c63d0d44efc41c51c525a64 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 2 Dec 2024 19:00:42 -0500 Subject: [PATCH 287/379] Add test data submodule --- .gitmodules | 3 +++ extern/p2108-test-data | 1 + 2 files changed, 4 insertions(+) create mode 160000 extern/p2108-test-data diff --git a/.gitmodules b/.gitmodules index 2e325a1..25f950c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,3 +5,6 @@ [submodule "extern/doxygen-awesome-css"] path = extern/doxygen-awesome-css url = https://github.com/jothepro/doxygen-awesome-css +[submodule "extern/p2108-test-data"] + path = extern/p2108-test-data + url = https://github.com/NTIA/p2108-test-data diff --git a/extern/p2108-test-data b/extern/p2108-test-data new file mode 160000 index 0000000..8a8497d --- /dev/null +++ b/extern/p2108-test-data @@ -0,0 +1 @@ +Subproject commit 8a8497da1ce996d00ba6f291079117c5098b8ded From 191fe169c3701fb3972d38d580840a39b6a27fb2 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 3 Dec 2024 14:11:42 -0500 Subject: [PATCH 288/379] include library version in return status messages --- src/CMakeLists.txt | 5 ++++- src/ReturnCodes.cpp | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 268066b..313393e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,7 +27,10 @@ target_include_directories(${LIB_NAME} PUBLIC "${LIB_HEADERS}") target_link_libraries(${LIB_NAME} PUBLIC proplib_compiler_flags) # Add definition to get the library name inside the library -add_compile_definitions(LIBRARY_NAME="${LIB_NAME}") +add_compile_definitions( + LIBRARY_NAME="${LIB_NAME}" + LIBRARY_VERSION="${PROJECT_VERSION}" +) # Platform-specific configurations if (WIN32) diff --git a/src/ReturnCodes.cpp b/src/ReturnCodes.cpp index 2dde4dd..bee6333 100644 --- a/src/ReturnCodes.cpp +++ b/src/ReturnCodes.cpp @@ -35,6 +35,8 @@ std::string GetReturnStatus(int code) { }; // Construct status message std::string msg = LIBRARY_NAME; + msg += " v"; + msg += LIBRARY_VERSION; if (code == SUCCESS) { msg += " Status: "; } else { From c3502e1779956dce0c1fe14ce67d26fd962a58b7 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 3 Dec 2024 14:12:21 -0500 Subject: [PATCH 289/379] update comment --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 313393e..f0e63fa 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -26,7 +26,7 @@ target_include_directories(${LIB_NAME} PUBLIC "${LIB_HEADERS}") # link library to proplib_compiler_flags target_link_libraries(${LIB_NAME} PUBLIC proplib_compiler_flags) -# Add definition to get the library name inside the library +# Add definition to get the library name and version inside the library add_compile_definitions( LIBRARY_NAME="${LIB_NAME}" LIBRARY_VERSION="${PROJECT_VERSION}" From 945ba08f7e184b1c722ac67c92f758b16c3aa5e9 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 3 Dec 2024 14:22:49 -0500 Subject: [PATCH 290/379] Rename test data submodule --- .gitmodules | 2 +- extern/{p2108-test-data => test-data} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename extern/{p2108-test-data => test-data} (100%) diff --git a/.gitmodules b/.gitmodules index 25f950c..4da471a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,5 +6,5 @@ path = extern/doxygen-awesome-css url = https://github.com/jothepro/doxygen-awesome-css [submodule "extern/p2108-test-data"] - path = extern/p2108-test-data + path = extern/test-data url = https://github.com/NTIA/p2108-test-data diff --git a/extern/p2108-test-data b/extern/test-data similarity index 100% rename from extern/p2108-test-data rename to extern/test-data From df52deafdf7bc038cdb0068807e34f77441e80e5 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 3 Dec 2024 14:25:05 -0500 Subject: [PATCH 291/379] Add documentation for test utils --- tests/TestUtils.cpp | 27 +++++++++++++++++++++------ tests/TestUtils.h | 3 +++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/tests/TestUtils.cpp b/tests/TestUtils.cpp index b7bfa92..0bb84a0 100644 --- a/tests/TestUtils.cpp +++ b/tests/TestUtils.cpp @@ -1,10 +1,19 @@ +/** @file TestUtils.cpp + * Primary implementations for fixtures or common functions used by unit tests. + */ #include "TestUtils.h" #include // for std::string // TODO-TEMPLATE: populate this file with common utilities for tests -void appendDirectorySep(std::string &str) { +/******************************************************************************* + * Append a directory separator ('/' or '\') to a string, based on the + * current operating system. + * + * @param[in, out] str String to which the character will be appended. + *****************************************************************************/ +void AppendDirectorySep(std::string &str) { #ifdef _WIN32 str += "\\"; #else @@ -12,14 +21,20 @@ void appendDirectorySep(std::string &str) { #endif } -std::string getDataDirectory() { +/****************************************************************************** + * Get the full path of the directory containing test data files. + * + * @return The path of the test data directory. + *****************************************************************************/ +std::string GetDataDirectory() { std::string dataDir(__FILE__); dataDir.resize(dataDir.find_last_of("/\\")); dataDir.resize(dataDir.find_last_of("/\\")); - appendDirectorySep(dataDir); + AppendDirectorySep(dataDir); dataDir += "extern"; - appendDirectorySep(dataDir); - dataDir += "TODO-TEMPLATE"; // Name of data directory as cloned in the `extern` directory - appendDirectorySep(dataDir); + AppendDirectorySep(dataDir); + dataDir + += "test-data"; // Name of data directory as cloned in the `extern` directory + AppendDirectorySep(dataDir); return dataDir; } \ No newline at end of file diff --git a/tests/TestUtils.h b/tests/TestUtils.h index b551a11..a49dff8 100644 --- a/tests/TestUtils.h +++ b/tests/TestUtils.h @@ -1,3 +1,6 @@ +/** @file TestUtils.h + * Primary header for fixtures or common functions used by unit tests. + */ #pragma once #include // for GoogleTest From dabd7286a574f7fed282b966c62a552b159ce914 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:59:31 -0500 Subject: [PATCH 292/379] Add missing return value, add error message --- app/src/Driver.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index 45ea55c..aec0318 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -51,6 +51,7 @@ int main(int argc, char **argv) { // Return driver error code if one was returned if (rtn > DRVR__RETURN_SUCCESS) + std::cerr << GetDrvrReturnStatusMsg(rtn) << std::endl; return rtn; // Open output file for writing @@ -72,6 +73,7 @@ int main(int argc, char **argv) { fp << std::endl << std::endl; // TODO-TEMPLATE populate the rest of the output file fp.close(); + return SUCCESS; } /******************************************************************************* From 5ec53a7a64d5fb69bf0350d7fd86591d1ec7bba3 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:59:43 -0500 Subject: [PATCH 293/379] Add version to driver error messages --- app/src/ReturnCodes.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/ReturnCodes.cpp b/app/src/ReturnCodes.cpp index 6f715d8..9f2792a 100644 --- a/app/src/ReturnCodes.cpp +++ b/app/src/ReturnCodes.cpp @@ -32,6 +32,8 @@ std::string GetDrvrReturnStatusMsg(int code) { // Construct status message std::string msg = DRIVER_NAME; + msg += " v"; + msg += DRIVER_VERSION; if (code == DRVR__SUCCESS) { msg += " Status: "; } else { From d75bb4992b6b9db8ec134e55abdba53f252d2329 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:00:28 -0500 Subject: [PATCH 294/379] Remove redundant cxx_std_11 Already handled above with CMAKE_CXX_STANDARD. These two should not be mixed. The other method is preferred in combination with CMAKE_CXX_EXTENSIONS OFF --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b7f878..70be23f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,7 +65,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) add_library(proplib_compiler_flags INTERFACE) -target_compile_features(proplib_compiler_flags INTERFACE cxx_std_11) # add compiler warning flags just when building this project via # the BUILD_INTERFACE generator expression From 188dd5315f361587d25703a4314d2e85d8000c03 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:02:22 -0500 Subject: [PATCH 295/379] Add missing braces --- app/src/Driver.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index aec0318..e9162e8 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -50,9 +50,10 @@ int main(int argc, char **argv) { } // Return driver error code if one was returned - if (rtn > DRVR__RETURN_SUCCESS) + if (rtn > DRVR__RETURN_SUCCESS) { std::cerr << GetDrvrReturnStatusMsg(rtn) << std::endl; return rtn; + } // Open output file for writing std::ofstream fp(params.out_file); From d8d916eed0eba0ea0db89225c219933573419967 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:08:35 -0500 Subject: [PATCH 296/379] Add functions to test header --- tests/TestUtils.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/TestUtils.h b/tests/TestUtils.h index 390111f..6b9fac0 100644 --- a/tests/TestUtils.h +++ b/tests/TestUtils.h @@ -13,6 +13,9 @@ using namespace ITS::ITU::PSeries::P2108; // Absolute tolerance for checking model outputs against test data constexpr double ABSTOL__DB = 0.1; +void AppendDirectorySep(std::string &str); +std::string GetDataDirectory(); + struct AeronauticalStatisticalModelTestData { double f__ghz; double theta__deg; From 92f607bcb449cc6f1c8cceeb8ff2a7140bbaf236 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:13:05 -0500 Subject: [PATCH 297/379] Remove unused macro and include --- tests/TestAeronauticalStatisticalModel.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/TestAeronauticalStatisticalModel.cpp b/tests/TestAeronauticalStatisticalModel.cpp index 252ebec..a9065a7 100644 --- a/tests/TestAeronauticalStatisticalModel.cpp +++ b/tests/TestAeronauticalStatisticalModel.cpp @@ -1,7 +1,5 @@ #include "TestUtils.h" -#define _USE_MATH_DEFINES // for constants -#include // for M_PI_2, M_PI_4 #include // GoogleTest #include // for std::numeric_limits #include // for std::vector From 527c5ab0d1a3925702ab200ab62c9f2a641856a8 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:20:57 -0500 Subject: [PATCH 298/379] Consistent use of std::size_t --- app/src/DriverUtils.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/DriverUtils.cpp b/app/src/DriverUtils.cpp index b4c5e56..7071037 100644 --- a/app/src/DriverUtils.cpp +++ b/app/src/DriverUtils.cpp @@ -15,6 +15,7 @@ #include // for std::transform #include // for std::tolower +#include // for std::size_t #include // for localtime_{s,r}, std::{time, time_t, tm, strftime} #include // for std::setfill, std::setw #include // for std::cerr, std::endl @@ -97,7 +98,7 @@ DrvrReturnCode ParseDouble(const std::string &str, double &value) { ******************************************************************************/ DrvrReturnCode ParseInteger(const std::string &str, int &value) { try { - size_t pos; + std::size_t pos; value = std::stoi(str, &pos, 10); // Verify the entire string was parsed From e0a4ccc5707db86c7a4cf9f4ce48423be08676d6 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:21:07 -0500 Subject: [PATCH 299/379] Consistent use of std::size_t --- app/src/DriverUtils.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/DriverUtils.cpp b/app/src/DriverUtils.cpp index b4c5e56..7071037 100644 --- a/app/src/DriverUtils.cpp +++ b/app/src/DriverUtils.cpp @@ -15,6 +15,7 @@ #include // for std::transform #include // for std::tolower +#include // for std::size_t #include // for localtime_{s,r}, std::{time, time_t, tm, strftime} #include // for std::setfill, std::setw #include // for std::cerr, std::endl @@ -97,7 +98,7 @@ DrvrReturnCode ParseDouble(const std::string &str, double &value) { ******************************************************************************/ DrvrReturnCode ParseInteger(const std::string &str, int &value) { try { - size_t pos; + std::size_t pos; value = std::stoi(str, &pos, 10); // Verify the entire string was parsed From 5b2a79a49330b2d828d95750897e6285aa0eb1f6 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 4 Dec 2024 15:11:36 -0500 Subject: [PATCH 300/379] Update NTIA logo --- docs/CMakeLists.txt | 2 +- docs/doxy_header.html | 4 ++-- docs/images/ntia-logo-300px.png | Bin 9085 -> 0 bytes docs/images/ntia-logo-400px.png | Bin 0 -> 37216 bytes 4 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 docs/images/ntia-logo-300px.png create mode 100644 docs/images/ntia-logo-400px.png diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index e5a0786..c300778 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -42,7 +42,7 @@ 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_PROJECT_LOGO "${DOCS_DIR}/images/ntia-logo-400px.png") set(DOXYGEN_REPEAT_BRIEF "YES") set(DOXYGEN_SHOW_INCLUDE_FILES "NO") set(DOXYGEN_USE_MATHJAX "YES") diff --git a/docs/doxy_header.html b/docs/doxy_header.html index abbf188..ebea68f 100644 --- a/docs/doxy_header.html +++ b/docs/doxy_header.html @@ -44,7 +44,7 @@ - + @@ -52,7 +52,7 @@ - + diff --git a/docs/images/ntia-logo-300px.png b/docs/images/ntia-logo-300px.png deleted file mode 100644 index 4e228594ba78ce7ee60900d6bdb1c6b5dafa38c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9085 zcmZ{KXEYpM(6<_*|AQ4ojTUzG-h1zLi5eEG_ufShQCASX+r_Fuq6g8->NR@rM2YZv z-Vg6N?}z8ZoVhde`^~*)?!6!8oQc!cR3gNq!b3wtBUDkA*F{4^2mX)XVm^B`i$j#2 z6}`2xt_B)f01Fyg*he(9`)60!9vYfAHyYZ3B^sLO7c?{qx36tF;?G1DZFPNxXTa0b z)ArW!@ZcgWy}A9zi60e0-RFyse&_oxM2Q zJ3kpcKV3vtw+#(+&P`RH?Z=+(IKxsa27kgfS8~pF16>?GA>sM8RVkZ`?q`Q3KbjNg zrhlDoTfkDXHokXwp0Lw*O;%P*aje zi^ZlC*(f2hi7 z-SKC=yeu;ey5K9zSy2;I{w6~;#7-l0WVhf-F2;};MVvg}R}ez~vu0pl4#x*mmyQLw z6n;P9xAJuw2lL3hL=w$(W_Iz!W`}Hc^l2BTci6+fU2|^#dyNo@(_Ye>a=hlUxnOGj z<0L*GoaUX@e=6lS=o{?2)7~}~yl*`bu>u;uO)ARLCJ{uekcPUoM{c()i6Oc<4>kZe zD@H(LgHd14&W}c$m0Gh*>(j7&RjAd`OQP9)N!FV0Y4sa3{2302#Wd;G zlDy7q7tF{=4TOK$()NI$@l@Mn;_pAEkpLP8h z1uygpKBPep(^IpVKUqM5?FwE9Dld(Bu6K9`uOzAY{YcZzk%(Z6ZLTdeYh`G-W8_Xi zg}mABVfdR4JgCSup4<8W#JQT8f!E#GZmE3?h~80XgpgzlD7|^bdYnN>g*v@h zZQKjImLZzaKDIar6J44GYYUj7bhCz{H&(#cn)&0NCg{dlET0w^e)(;sUh?igi6wDq zXzV5gW1FVcgRfeu{8$Zly|$Zog&mMg2kQ4OP&^-{hd5%=T44qY^L@?KP$82o4bcIv zsT`n%_B~;SmUk5mMpoTh*~qSyWa-oN_N80R#$WGm?Y=F_nw5HOPFL2gCy(o27c8GJ znf{mb$GmfEdu3I^K1Ik)(*^tO8IUsYaW56c+t2|s+u=%`X0C+mUG$q~ck;l`_j4&6 z2y+z9>z2|?I2GPFsr!Q{WcgFUz+uzXtJy|B!+)h7@M2x{VAV5#QPv8=bGBSF8Mnn} zzBQ=9b0r%Pvc2iM>uu01T6AD9SC*CIEOHoEG`Fv=%dFEge0}WJ*V3B@3!?7$<6fhj zfbjGq==S{Y6GfEXx(R>df|j%I8|!YMt#ub5=){C!m!0%VsHCr+YJ41nO{>m%I=@vj zgYT%_W$ib<^(RNl`WEG#3+kf2`ipUfFBGmV*!?b~=2)MOFvk2D{wu=KXjio7LHyA4 zyO;g;np`5r7q{8!wPII~lcP4>E4$gePe*RpP6zR@d)9V7&_jd>Y<&Q}xf^IKt4~WW zK-`l7#dPY#7qR&uz})@Xqn5H*M_Xm14I7j+!#d#ABxTc3a``PZsiVs$pIbAI=f^E$ zoZUW7vz__`hX^@zC2Zyvm8klv+;B}Su81BX8ut0Bx?3>NW+EqDYz|N2XD0{f!6fbL z41;85{umF}2uoj*Lp{9i@@xC7>Qelk$BBS^WrBB&gsQ2Tb#}gaChSonF)c!=DqBmX zP?#hmRrWE}_JRplKOHh)n+lx6Z}U*VOqH+|iLQ$spkX!nrIn~K`JEkfC5l@1PV@8- z4spb;I##tZ0jZ7=(?qxp&@W9?Qxj3pmbp@DC2>qleLvu+yC8#)t}kEORP3zpq$7{* zPL2vB7}tNTQ_1XR^A*O%MCj>7a!*s$!Mlba!zl*r_ITfheDfoV^^SY-a)gyEm&mV? zsH2`R{KS|{j$8m|ykhsZ`Eb*O<0mqBre5<9(I*-qyc>LTJ6MhnO9atemUX6mxvl$u zpkSW>5x9E2dg><5yS95LqaX7XYtt+S^FEON(s9!XT&`HI!flHvKv0O#U!qj&d!XcZ z&x@)V1u^re$wAugQmq7?JSitt`z8%zbH_U_n~e5_TWut}tedbi4P*aHPB*TvCkb`q z3s?V8+0Er0D}qzi zEK6m$A9T9eLs8S!^_0urIy@~tY~&7O*rXIjWIP}_dIzz;nal{|Rk71d|F(mhh;{(0 zPR6H^`rEaa)LYxu zi%I>^L~9iBCFYP>CAowMYz_m$Vs-u_sX_h+ro%qNZ}HL)dtz2WMbeQmQ4oI}Pn=#= zv%`z^vov9A0r3;E)x~PO-m$1ZjC(I6oWNe#b&*;ExUrPZm5yE6WP|ON0>P6$p{{ln z+j51T*4`1HK$VfmUw0lg6f@H7wIZKiT9O)3)>;Gf*Nf^?^ z_+dAsnGCvK?Kod&&&(4|7$f6+M-DAKQor z&<>s()CU4SN}cl>{-ypCQEIE=Sn{Z#b)oD$xO6|`A?{ZordIk_D@Ft!m?@)X!UrwA zpOuU!5Fr`7?FJayO&C`fPg>qKlo<~;D*we$ErK=wIlJDHev{(c?1flo0}n{i5ri&^ zb6|qr4URW8LMUV92bQ`o+W*-88^AoJ_}qnuiWu(a!{vreO#^CTHw2s7gV2bxMG~&_ zM}Fjszu-{kKo1@8z%KzE1DUI0bBE3FgRG%GYY!f0J`6^+=pgH4a||j&$n4A{kbZpr8T@ zO-B+;CVyS)EEN0HZzdjVANE3(QGJfI(NLgzGC-b*j?*1JO~QE~2-(#ST=#@$+g3-a!}ieDzEw(3QiPtoJ2sIXdEbFHk@%`ldMY_DPqg7?I* zWvv%efa&21#)!J0aFuYneE~J z7+(OJw&TA|3~3lO&oL0tZ{dl%RWBNCiyjZaZGcw_Q|{8dvl~_-moa3;^I@}CBIX{T zs-_ROx*Mz5hUcSh!e5wG07~b4~Z!eeC-_n`{9xAXZjWEnmVi z3%Hm2TbCp2)YUXi7aY>W-}hbxBpxtnDWMTCPS9dl^oZw}!_fDh&;bmNcSoUBy`C^B zkduFBUcFnY`KI18yDZZT752&KC3Ro=BDoj|xPn#{Q}3ZTVK*9k@in(kI1eONPM;Xt zY$!ga%*bp`pBi%-Q}^8CO%VVrsv8+?qD6#1(v*5hmlB`3IZ-7gkNvs+{!^XN@ALf5 z8t425tw)TIY_0{?4xWicH^&%v;Vs(tUq;4&93{^o6rHiyKyTr(}CjFJ_HSPcaS?uSQ&tHng|t_gL}* zfHdffOdz@r%F3t>*;BMb>iw+K_x0hz>V85x9WuN8f;7abr9MdWHO$$-{j9aczQV<0 zd6%MWC_C*0mbSKuNit7T4EDWFHOQZCFTq=MHrM#)jkJcD@Ju@d1bM1InxkhOTYDHe$ZBLa%HG{Cg_Ggwa z4=dDNaSz3MJ=dCS622g1Rn?k+`p^X%zmj#36^-1w4{|jt?{5=2;0vlk3U62d;%N!#&BpXjiA=)<}&WHR;K zx3&|KckX4h*vq`iHzrUVNAOxlD-Cdrr;sumn!G+^YQ`30gZ8Cs?!O@hT9`?%ZbGLh zFfxd1WI{a>rZsx`d#KykM3>095ffnY2RX)vg7?1K3Hel@=Wa|m+o}Tl2kZw2w8FkibMQ1oWAv36PAa}mAz4Z#!~t{Vtm zls;L?>0)%fgbiDH@DW<(8k7~NY9K#GDpjoU^i0j&^buN4W(JN+=+io&gY7cjbua!x zG8s4!9wzRXv<6ME|AQbaf}SE~%v_nk%gcqVU5v8BiaqapKVVJRE!T5JDny>ph$U0x z?1#PoL7(A*m#{ClYDNvn{R=@KaJr5i1>)So=LGQiclb-D-Ka%YIzPH^0Z}Vka zFo0E3THXClH@KtxH*%wzddMLq&&;l(P>I2Jn&M}2)dLX*q70o_08ronsBmssJlVAj zJ!}izGQ&yV3}g7iN%<$&GNX|V>W|DcH$N~CTpd*(j>>xGpzyT8>(7&Mcb z(amOB81OV_?O2NVAb$^=ia&@w^#)!t9`v%;HcrgjQoY#|BYq+QK8-zp3<@1;!v#(L zCLsc#%lTd`+ZVgz6Z8bLeR9i;({L;6Y$$TDN|4?Z9gt^NSbWw~se9#Vs0Ox$m{1!U zJVz&vE2^ur$bmV*a(^`p&$JXF0YYGPKEgH{Z&3~QM`654X!9ev6=lZ$iY3n|KkM%$ zAEeWmXhHt(_r3T1Egu8Br*fbt7usybk@=G}RlW669v>w$WU^L?VXNwqD{Y&7{F)uTi`{}gz>Zv4w){!LfTuUGw-9Z z^z0;`&2eT9lvq#EsFyzvmfg)SjfwR`+)R#wPOtiCX0Dqgq&*)Yf2%e@cPq(*9xHX0 z!dSOa7&AMHqkGB={U*ewqHo*xi20yy>w}V@)I;@|C)xdaC&0*`5Zt!0W&5fnD|+ry z?As(;C+4L_sE-)VcAK?X#H?zHk1(pQGu6`73rb(a#}qy-CJ^gB_#-R*O%Xu zB%UEVt$C!gV@^tH=E2#**vsn!raT6to}4)GN-bK;UjhFJ zsmz7-<^TP?sv&?Fk(QQOn^7wC2AwODR zyJc_b3$16Yj`yG|k!c%m304cvd2iReHvJD)1FuC>6Wtw)zkLh#BhI@dJp-CC(@ecC zN@q6v7IWZ>bx6)*g??pKyV@ujT1tIRe!?(Rk{{f@PWw=HE2NE|5u~Jv5ke>P5pxld zT!Y&uJlkM?={kl@o+Qi7%-pVtVWFaIOgA;dBx3=bkrfy&nk!QLmQAh?2+W;gEycc< zE$u(2F`0=}Av0Nt%uzGe!oy!J__Iv0arE}78RJ@XYSedPN#Z7|qFdT54!FD-E(S|u zlkbsf*;N`e`2Md9T_1`0%?p8UqYz)D{fcNRI9^jLkkLfC9X^Jg07PadGT@cM>-Q5G zd_d*bz!#hBBLDtd+sE?qx$0rB0O08;*DUmQ9W>?r{9Wxd9{O{?eOz);6wiMh%~B(O zyF(0NodVF(JT=r|TlL*lcjR}Kh%y!iu-RTIdhJ~(8Wd}HA3XgdT{a@j?AdxSLQwy# zqs!|sN|JR8O+P1Ht|Nv5x50i!O0*rDgmU{Y03KxQCMV;0zsDTHqpZ=xHM}s}LZhZB zsEV|^vmfN90{k%H6r2OL_W14rnJ#}A*6N7+*PYe^yzm#2drBwlCT#)56DckhRxK1v zh?u9A;4SL6-_AfQGX%t$vU2_Ps;7SUoIMAREC=F$+V`Y=jC{RLtp-xLDkEmTr>_RT zv0hBxo*B@wfl`N0naR zpz-6+HsIs6V7jaKR);d64-}HAv#MNM@qY~a-dAPG1Cn|tPj8%A{j`~hISQx7!DwOh zQ!b3o+hFE>nyG^atK6${c+Al&tNaF$a=g@)ZvNk;FzdTgM7ks4lgQ)GT&3!PhV5wY zSEi^gBA5?8bPGJS;Col|ivaXQycR=hcOGWOs19a_rz&XO4ICyhzZqh@+lWf zwl-ezB&b0Q|)% z@(v3rx_FmT+y!d<-eP^smLUsNhJd@8LK>1pBli#76-g>cBIFYzp={#lhyO-OCdIOZ zz+I|lhZeI|Ylp{1)dNUo@r35x(;`?j3HTkMY><*%B}bz#aL*+4R5GEAo!pD zEd}fne|?S`Gw*h$Z4nXYPkyJ|u&S?6f(ru*wuv7lf@99RT=QT*tAxlXRAUJJNKMiS zGv460!NhJqU*DtF3j$+(6WMX&$lZyF*?<9DavuH4BPS&sz{pa2?-#3~HJcXxt5+mK zmEV#(A{f7Z#kxi}ucA^WQTm#PURLu{5QUmdsH%CsTH_(xP1&Q(0H=Ip z&h=N4@FO?uYMPRMbDGr1Qq4c=dZJHGA)MWnIqeBC1m%*`eu%3ICt5wWT9+IwwZcA`oGGef>BXavb)S_EDbvd!e<%dQs(B6G;4Jv@WVFWc7ylg8 z&<6E_eK@jfkXy6H>pGDLMI65S_i^z^v}*N!5r5(oo3&9l6Q+z&Lbnu~sHj}6h6dty zr(TA)n3{cCnt?q-2>R+Hj&o0w1kL*-a}!8Iy`Q(~%UncP&r&fc7e~vJ_dXt(iocFs z6^{QC_)ykbV7^_LY}m(0=$ZPQ@3!ij*&!=o=!{B>g` z2UDG3yZB167B#w*O{P)U@vsFP>o2xfAxI4Pdg;rAsiA3@(e3PGva42G7WX?{_4G#0 z#%kDC_m93!B(fYLm` z?C3*X!}_i%GD9?SlI3qV2oi(i*wV+$ zFm@JeA|9q-+2Svws+qX#q65+GT<+k8`nUJywiPQB#O!Fs{8K>mU+K-*McF5Mn)FF? z5+!8>eZA3-@nq`z&W&VC?*7nZ1!!GO_EmpRwrt2|hwQb#C^%v~l zW9a+B4MchiZ)Q`+vbm&x`4*<Sk z7X z%!ERLlU)2s7a})8TkE#Jg2WNkLl-I}zr<+uVm?#FJB-;Wwuwx*MvyBx&P&F85EM8T zq4!cG1$n~WFVW#Ek+88#VlqJu|cP&sRWBeV9lX$nx?}sX~FcH0e7>_2wst?X1UT<16QX2v(s4wcXjb}j<=;g zS#Jb1RInwwa436;mrNV#0wIzyOEAwRFZBLIgX2u=8c$e1>_2+Hh<&}oQE4=lWFg!? z*MZOBLD^SaY?pRcec!)nFxjWcT7ylx8lBYfRi4DDvbjk0C);A{>KzLLw;JVY@rGCW zvnHYYKqZmW{OLi0$d6dUmX1KE4?GCI&G|nNOL0%m#>6+Z)1Y|PO)HTi#w))j9*bXz zX&qeV8;19YQ=ey)C+(>ICOY=e4mLrCXa^s!K}VzM{g0o*AIr*2)ePG^D9PVay8Mct z5Y0~?DnCab<5--BWaCGtFHP1z?fb|xG%weD8f^*jM(dQ1_ZtaZNhNOwpNPn9^{$4M ztvks1TguqyUH&YLuF``sWKD-y8>Yh+ClBrNw+lx78w!J%H7`rN=$j|U&l(4qD;hnQ zZY&j`l^X3u>$#q#j-7as7m0@uq+i@@Y5&najD@3~*-OZ95dt@;I5eVH~2A#8d-m}#8v49i5XyGT=Y4zY}uZ*l#^N$~?wHm1fNxjZbI6!2y)H?Rq7?t>wI#+wHnrb8$RM_2*2EDV3$| zWxq$OJ(V({>D2xJJehad4henj?Y{CcGi92zfcLj*U#2sKll3xsYPlSw$9v(93ew;{ z7lhXUMVsr%XyO(qxm#Ecd0D~w)?f@iQ-c}-H^DTx{<5@q&|T8Zu>kF$jyB#x1!YD- z*5)D&jWkE!shSH9lN!F;V!GQWbRxZEL2I?qk>@!GW-kS>myMMdMAX^?@~qH!xq10P+=3t; zK7C$pQ9eOYUJ(v%Zc%RT?t=fw|38AWs}0oF|9>YS|5a8#6WIRW4qi}ah^Lp8v)lj9 z+1-ovdFD|54@b|{*2~w*1A-=FYNbJCbi(%81ySdDGFaq`XmeCu6n^3P1>T>H$q z&fY(rC`EZmiI0R!U;Q%^4=1cQb+$Q>{O zH7itfM@0Y+Es`xLEJT6^fdeVn3vrw+2!V2xfLse@-V5UjttS+5SAuN$B{N4bPN?gy zWa_y?90btfV}LXW1QZMP#=!SO03fV{U=uIjuCR_2K*?zLM6Jzcy_bM&>HPI)7d+}O z(%GMS3_y?^pBjc{CaoHxit4w%|Z^5&Ag*-XN6l*cAkXOuTyIM)@fXS|C-A z2qZy+gzR?W7KbJea_ZGIj7{bX9zybi%aNBkKyyUnf*|;U7bYVsgcjK=L=sCv@;&{l zEEFZzSii!Ae2<|U=#PLQXDu`rB~Upn9q+$&H|24Rda(?79OLKv;lHsJ(yZoS|8Y0O zuT=pRaj-Bz1q;FOdHi;@~<0E2IXb6p;3ex5=o2BHi|XCtz|x`a=04ohJgu=!^gN zFPTN?A_+RDtW;KmkyvTbY}F3T@}p|2qF zfS?7MVzK|uGa)IQm>*XqCQv4bnuQKZ2(uPN6Vf&rFh=|TKVJ&E-sv?iX-c9s7vGjt zJZzLsg&q-y&=Dzy2MD*~X^w z3XRf2SA?|drQQB!m{%(e69oT+?F0j<0nq|&5p~3vzo-oT58@L_9TE8Lzu#1jfTq_@ zK89R=A~FzB>8Mdk9|S1Vm5l&jyerc9o8GQoC~rIp1o&1&N<-x9MgZ`2$Q2$6{aSWz zW>%%;292Lbm5iEk8|XMwIP&n_`&631_`fw<^SF?DeYcGa3)0acnR@lNr+Xs`UJJlS zeME~OcBHtLF%13h6EUJ|s%<@`Y-)J1UN`I!wO)r~s|Q+=?Tb;k-29SUF--vYsg-~wzzZ992ebAj&-LZ84-=nsZyj4wg(_j!E7 zkP1)@Lk$=>mgB_BFmKwRB4}eCaBcUwT*`qX$4Wy#B5~1idTVD3q23~?rLbVguJ;Df zk=9Z90xgm0NUrS~p#z|EF}i@@@A#&10Srm`+iq};C17Cu(J1a0_`0Ze$-&imjXE=4 zv-1ZzLqY~^?rimTfdGVk8!Yd2_&(r#B%C&cm@Q z2IL11)Y}8jyBiG5F#!HaGOa9GRpu=V3N~d`I*?=JhA$nrUav~ht~sR! zC0~-UYyga_QnIEHN;?u{{c&QUU(g8lBl<{OY5CDmytOYjnSjc1=_ueO#0Yq{wYRT? zDitupRwP_1GG2xm%g;JWOHT?)3u4MC5%tWk7#ULm@W?aC9HUYa%4lOZeMr#=01lqW zT19gIU`;;AhhD$lwSr7Nv`D5zN!DvV$EcdgmKEE=?y{^unJDra?uv?Y!oavF`4|%+ z270aVQZ#;1P{^n}BIys7(9kEef_~`GrrlymC|{U$8lZC6e}!$@y1hY1{tLPi^@#|( zEq9L|UmInzKYBW0Y-iX_d=xFhAhagoBu~$tV4h1JkcvemWfocpYe$Q_2b96vh@nMl z*frPnBHp{g{Or8r6_P9on~PSLPi=HAtxf5dPWEj5x?Pfn!_SqTB-%eFjY`E$`V*p% z1navdxlJH109gKgo)ms?e$0gEnl*9X?$N%9yR_80u(mY}`k8@%=2EPv(GspOZ?MS7 zC>9TUEb$(G*Em=VHr>4x#y$mbqQdemScor)EBN@t%8^t5r_thy*{{;Zlz}gDwu0-j zGHH5zvYcI!G<6@@{5;X?=hSncf$PWlm?)+1DBHN(oFIT()Cn5QF3o-tB0b!&*T8#o za~8MO`otwU@;Tq%@I}URBn45;{9N3$6wxAiN680) z1f1TdeA-p+@+r-=cZP&Is4a#VxGMSy>59+|Q_)8KrJA5!QSk|}9UCNi=u2#6>R=&V zAkn~anK$8u*CT8ba?Uxw?s&1@!$MOu_I!8y-2niO)y0{>Bzyz|+Zro*E`R{GIu%eR zBpXJ8RyAe-u>+ncJ_bWUUY9n~75Z;Qm~qgAR{E|!YzhlnP{yC|Du z3;<{|s0t4?8w_qyS0^Xp+br_O!-w|ngq1tV7jEp$h&CqqqR0ap(Fq$2oGpJ6i)glV$)g9|q| zgA#C22q*Xgu=a_7GT?ue{@!OH@I)PTdhA+0N4ZttxE=eRLO_cl?hj=Vt`FBREXm?x zO;!fnk=l*BVz>^gj6wYbhOW+vOMm@5~L& zb^+aV0e!pVwRmad(Jq>!BX*I}q=}n(&9r?2g-15t0sCEOO^O165*hKJ;0`n2y<9XWRBoz54X10Dks0SIHyuRbI0`a-Ebp(KQ?Tm-DxF@z1j zOSsiET2iX*@h=F~wR!2;TeM4USXDMGi@O&kT84I##n{{(nbUIf`fDqxg#lfj$U#0q z0a4!Q!X#a!sDj%HQ{sE6&(E1$>JH^a^<%pmcL`qnH)zR>^R`yZcjCveq~2X#Aq-UM;h{zvx(wln5;sx0!rxasjHa+P5)bLQ$T$LC0YFXl#z)SnqAB?Din~hCl3=oX} zaSyY^I|SZz&knbdE{usTx_)*M^e&aPL275?Tv`YGpcB{}F?nl}!ukQA0t$tbB6HGC z(D4JbgO(P(J6`r=Q}mxzF2xyXuaGYh-*IW@CBxZ1gV{ltB17TV1IsA%;2kCp-eHL~ zW8}%XDCPrteiw7M>go>EW?Kl;8p<<3W*)Y5mI1O+S!8k=fuGwXN;cP`snRl8QMZ5G zcXv*&@1vp;bTC#5K8$rSlUuST4FosFaL{+|06YeucVp|yqa)*b)_1tH{ z*F+9&JB7&%C{N=2o2saNq}4>ggD`9!#_VQ`G|*5G84Hiqr~1ABGO?`eVp~d>d<}(N zQPt#M>vtDtd1?)VY@mp4q^IGQIKE>T0GI_6ghX)rguwqLm~qzA(wgOF^Tn8h*z#Yg z*C-IdbXdzN76(u6Ckb*w-~g$L+FD&qs9m94zKh8Qf6^$Xh@YyS?5Pd}pKBx@Q5@Zc@W(kVglKb^A zTnU%v{>M+J9F(akWQ*Y<&u~+T=y-Si$ar}~NkqKm#=`Kodixd&7hvy@ipkU1IZ~45 z4Y6iW769ligBE#}G(xuhqiSrx+NIV^lG~xWW^nh=`k)jII!!jlWcOHDBx%v(FG~sC z>T-8YIC2O6#%3lL<9cruA{#CXyLzk8rzn{W0H{q0FQ!T4*UXdJ6%Z{tblJocUINSN z;7wNOZ0uJIrMn$4yJ~Jey`%ltuFBo#Xe@?O(&af@)+i2%S!VDEVG#GuQ$^;scFnB$ zs><{v!YqBm(oGg0ck0{)BW(2)VqnR7@~pdbU+nkmPb3WT_oo##EhIt$3L6WD8ugofC?ryZXz{ix`I$hC zqt3Y;XuJGwukHBF^)2kdrnqFOecIgO`P_Z4^6m)&S;g({E1swSB26Zbu7Q&6jLbw1 z*5sEX02?)j=6NpECn<(jacN^1svw<7O%}k}Sp~Z7q5Rk22=Y_Y$`{?k3O6zVAj#C% zy9Y~EfMwn4^Kk=)x;8&Z@$oC~Emn}zpRCzfC?82@|zZB;8on@_#r?&NAJ5J>=` zpicUfmdP%hce^J(^U`;^zZAs2Rua%zvr@T5zsh3LG;+3UW1tT}Fh>bd`@0!s02GYH zUMRm>DFW6oakr0Ul$P&n-QCvTC}7~QmfkKndl7O+bo#h`1cL6!H}*4-^`(r&IQ z?kIv}m?PwOnd4 zyKXv_n@)Yo@^aiiJHsM=;z<$|dkbRpauKEjfSX@loKmq`a4N#K`(M z|7OacqC+4d&tYQQlF&;jSk@B%>VxeUdGr3@9sJDN5)Sf*fyIF(ILWTIJg7>L*2P=O zVxbHkO0&Sk;xfbsRY6wY{3ANke=i=br-uxHzEtr!e@l*gd!I~hY%4vP-hP6x@1zeQ z#Z?l}8=u{<``tDvD{8{|--ugsQ&OLo^UBFUNrukK^6vd36evsvF2UOug62kea0C|P zJE4@UD#^|-iM(o?+BL#+9>qy~;VNQ1L#E$RC5QQ_ITEifT(Uy1e8F_!fgu@p1kzU=_yG3=H5VV~! zv{=vB-tWy!WfhWc(30703ZC&gXSLZ(_AGldRrb=C`8>yz{2@h%0@I^R^f zP^$Bi2P>`0p1hJ4cN|AmRa;yd^OKlRIh2)cB=Q5O&RH`0`2CI|#LzJh^n&)9-Px{u zRR<44R&|aWsKn}y>(7JI`#)It+qF1qw3hTGNE9G?)NML5+vPuQSDk3_o2vfE#oZkJ zvk6Z;p-9=UUr+A$^P%waayde5vA%IjWwLg!I89g$3i#SG|J#I&Dek|_d0o> zchzZR5X$D^i`A(aBV{)19n0gR0nRzV1eGcfAqJ`i(vEpnOe1cnDT>~QeM$mZ|l?R$yY2#sJaAZi{Q4NxzY<~SD^=#x_WSBMW)gft{+`|4| zEyYW?$+#2K-%*Jz<;u@{!CFccI!pnP7ZS8PM3Qx^hVtKj%o^;OYNNP_9^u{Q@ws#! z(%C{YDFT#?`EOOWTN>jQubIqRgz&9!EWcn6W#dfd(uD_yJey<2ZJTdChK>LFYt@{% zIV(f8T@~y0i^M@&(Xd0SPylJsU<$JNM2-kgZ@3&b1EP=0S z`zY*g;RE+Di@bc#l>C`O*(rxIL0JnpS7J(-3iJ#Mu5}8;-=>h$EPN_gHs-8%E8Z7< z8>>w}mz){;aRwU+W^F8r#1>gHd`47{|@rWVlHS@8Cr_ywZKx*X&|G<&=J zw~lkORy%Qz3YN4yJYcmHHdPALD2pbwHAen@86lLGu%f6kY()Q0iA>={l28D6Bv2;T zk@MAnnv1JL0k`8ik{AMx z#$*@0SU^xb0@Mk=7tE}Rm4N(-W(l3P*ZJ?g%ppFE!nanB8|H)^SkVofQZ z!g1GP7M9uNQ^*$5b`-7~91I&v@FoZVwrik`X8b1;pyyL3DsC+gT^VqdXT0^d6_U4F z?I>WZR_T(>gPAD3JcfdeOf}A?Ro2C6~+!nB%kVz3si^WWAAWq6Whb( zbG`ZV=6LLa&yvH#oR1#bpnn^0KCu^r2FP!$*EYVpvPq&Z^W}eNByTQIr9(^61)1*t)w?iINYtVU#lbcdy!_-pEV9O6;dz0IZ4>X1W<=7{KA-vL>N#bGw)7=~zrTK!7P& zc5AwFL=MnXDnjsjyB)R#Ls6$Q#FkGf2BsbZX`L1esRZ!N%I#r`wdY(2Cha>aUF&KM z@;nUL)b?O@0^9w^X6TgEl$A>$P^Ws7jQq!XRqFij_W^AYc4CnHU@nlD@|qskd3u%ovQ+q$0VUSrs>JO#E>xS#~ZRx~N#( zB4P1!g;5zEW&w~plW@GA2xhoya=8~IRr#Fh&OO}9&YW$xuS8Ac=0@k`ZOUB<*sgkK zE(s`YAtn=9?6K+|c^y<9C4vYsIlRxYcN5M?kJT75(9e7hj5rO*iUTR6eA|~feCC+wTe=4!rl2FGVKmsVrqrRdy}0?z;FT{ zJDQK84-#Hh$4YlgCTjEFcJX*~%l`-y+s`MirH)20h-eYy>yzYN;K#yy2jsT8z|at_ zugg)q0YOCu&wN2#W~+~o?>xJysV%o_M+s&A${lY0U)iRCZqF($YlODT-kF@7s%`6W zNqBqH{VQX!gy@fP#XkdIXE%)w$NAMdAH9%s%$G2LmVxmdHDksLj!Pv~I(8`fIw{jf zv{Z3L!!LKoIfZ^*E$s;D+tw={X&8}gj@{_3`36ktC1G3qKIzdCZa=E4Grmo~t%T@Z z(p{X_-wwZq+47&SuEq~i3sz_6OoS?htfz*N&)}2Q)w*d=T-iBQ2^}th_!~+}el2zU z`^~I#&euSn;77IiFr3Xv8N61FdwF)#836sgoi+c`8mbM-v!#dfQpSP!C}!X^SN>2w z`tW5uqQK*(zue|{H-q!;KXAYDV<6;Sm!CJP3=@N|nZLaEvj9xzhwTo08@q_I5Pz$f z$Hkb=nyoN;*tolDKh2Lzss(WRyKB+16&F9fIJYT-iWUVVN$Tn0pe2K1KYabi2 z@?{r&cx)`G1$#)B4lc6_=FC4{BuFO-tD`({?lIC75={QxF9apxUp0V7fZ=4ga`RE! zl(Cz}bXIt=z5i!Fx|+&8vUe2KphZSc3rm zXI!Y1AdxOeRN1#wrswP=TAj#ihl{ep`?6y}Gr-?t2`AXE1AVa&1mHhpgCQGt{^}U( zf6sZvvEq6DEQ+;M`dBXpd**dstu#0U({?|!^`^#MQ%&aqtmPBvPoNM4TA0dLmhN2( zr!%-WUzQsVy+|*r;dG-K1$h&7vSgjhF43|P@TiaEZE}^J^0GNNv&S$IAC5neUI>} z#M$QHlsl#&W58HrQ(t3Z>WyQv<^asbuG;$;Ym!%6AJ}XBDy#R$L}MCo1f3I^nBvSp zfQ|hJ@X6hB8FJ1XN$G;IeSl|^;$rl9N41p2suqyZCmL*^rJTu^>)${5jmC01-}TeO zmWW5@xIy&oB(AuWhX1xvuwiU1OWv-go9Ffs0m<}i)jxh+ZfyZJv4D5g+sWpvm^jSK zX?-nZG62Yvf8srp&Ii~Y`$&`oyG8+BIz_Zz6M|H!E|l%|J35u_`UZOW++I=`y8LXp zl2>@-qb3j{CCTZ0jKFixtpCdb1PN|&W;VDqF{HbUUY~dTw*4rdFmsdP$r=2ShC)gu z-Zk0XG(HvGRN}~GYM_S=yA3=dI?ut5q#thxLiX#5ryuY9XAhb6ugqf?CkK53nlr&Q z5QSq*E zZPu)7o;Mf1a@uG9_}^m__D^N%eT(~>*Gvn4FN&GOM1@mgy2WiQaAcNMa?UQ;7q0F0 z+9tntTAG!V+1<~hA2YE~&*cqjl7|#PN5*|6p`=Ybj)VZSCV~02qVnedkv3s*Flp1` z<4p2mo70mZ^9n=et7}ASLW(dfjPKdx3_qsMb2GbuEVf+ql$L?g5>kME68P0M(9e`7 zAQd95yu^d>ag}O>M2v9(Dlw~D_3|7OT5W!GeW2q9U}s`*dT@Kb~kofnmo`ASMb8k$~8QbX~rVP*IgKSt}-tgCpb z%}5`^Wm?8VOMPB4?^}a$q4#N*#ZkD^(k^g4WH?v|;3UB%~qy@Mk2&#!e>!7#8!(;g~j%K`W4`A+SlP)g-- z+Y1JKT$Gy{2>aB`@?f-pKVvCQvNStSU#>t+Z@ruJ{Z6eT;Xl>SV@xWIJp~NU=?zpy zVZX77f8ftcguCno+txU0jn&KA`F#%?nk3-*JNS`INTh(%B@Ffv$J%zlJ|@uJ8~}!` zojdE=TJuwE4%pV(zMf0{?8S1@?qiQu)+dKcpIovoB|JBYqlWwe-2wDFzXaCU?UDdM z2QNM_ayo}2fOUQ`wFgg*mkD8M>qV{Y9bxYe&jg!YB9w@U4&uChXk@)FG3Hdvk8iF> z-qRPgl;f<6O*sTKmWHo=5^1NtZO~x7-y(w5%$2=uWmYWI#~n2Vtl`ykX^d)k*=0_S zyvM+@8U3`8zm>c_^SW)qEH|^(XXnlwLJWUCb)JNikO7%KxI&$zJ=FaVv#QlJ9Snfm z(owJoDejZ23&J?3sugQDSQWUeVEYe3n<%t{6{+}r&gus+}gj%v`I%nAtVCQSVd72Ncm;Aw@x&b-uU@tT$%ERa?3YdVE z&0#lRlfFYX>X+=&_^k~$S?E}Sp400c_&2v!Si zGEq25BWBOk*BeV=euXYC>Evy1+w`EV4en^1??of2KU;3Ij^d3pXj+y=%(m1fN0sA* zvfYR~0;Bw%*X#OWiF}$Ao0#s7s|Iw91=2K{ZblJDj_2}C)<^G2{Qch@`v>@_@|XVF zA6ED+R=yX6V{#-rRF68gahHu59Q8 zgeADSBBUjrRl_(Cv4AI)a*}H1#m8L8lxKBSd;Qnj4UkE(efT9KGwV-1eZxz#3IVNh z&&pCP3;NrAxskoY51YExmG*|<*H7<2PEK`%-I+wGTz##H!f66PO)aFBy90)ZtSTDn z7D&dt^94|E?Skxss%G;+IH%IzW-QSueZ>RzC{8PkW7x8NnnyHA9{;&jPB*7jQ1bKJ zh+ws?uw+iZX!uk-Sw_74X=fW2tKI)&#OP4BTy z@;>!4Pqbsc0D+k8)3U~pxE3fts3q`TAp1ir2A@%$LV3?N>K_W?beEgRgLXuN%fPxc z5g6<+xvO7;8^VvYobOz0=U^7Wp?0?xK(PNbCb5377#$72zoX*}j8$Hu&{cQXnQWr^ z;em)e9dC5V<;D}ATgmFQZCS;{*aC61c_9FmWX28O`kN`QtkTNj-QI1Ye}^VW7B?^d zgdR+)>Hg*x6)0^a&p%b7*>_Qn*zGInE%h1bbNBab3Nq{x5QRreASQI__C}*N3f6t1 z6_7(eA569lhL+UgMGdS8KH(hSmt1M_%@UOYH{nlwKZ$vk2o5p6n9>k(=| zIQc#IQst#pIM;&$#qZdDD4@PMd;C5vxjQd{xYYc)mm1i%LE7M_Kq0e~V$|&2jc1>q zC+3R~@so^RGf%5qApj>O+9%z?(vtQX1AB{B zsXepGqG55{eEcG#PbFWhtODiCN3gnm4{gk^5Iv?E7HW?4h=9z2s}HwW`EO@AsnvAg z$VhDCzT@O2KNyOf9Rlj_$q!7`R!q&Ve&J0y8PVB(C!K1G==o(zd|C5AIy=v7t?VGE zqQ5vCqCl)BPG>cxrL^A4EIGd{DelJG<9$eto#l;hCsIV5u+B2a(3P&UDoY>m2{0hM z>%14sLBSwO7Te4SpNkS z&cUNPt*{toyLxkN(X4!?!har^6H(J^#SMw&^IpP%`mun-mSy*LNDN2gplCld6ps6} z7P7~$z9$RmK0ohRZh)$R5G3oWuY0<&H84(>aQED2KD5QvtFU{88o#J&wRBD|%cE45 zTVN{{7(a4JAPnXq;~GFCCCuq=KhO(DmSm-N4oW>HbVefn*!F@V23#08gyE`X(Q(x@ zb&diizGbDC+oo3dya^mC^Ty);&_wN2C74_EQ{-%26US@1&V+t^YHC%MD;_FLMIGS9 zAqJolSL^{(+Osy>Na9YClW$l?djUYuzxGTa{bJOc1hD8&0HDNE}abT(qI9JXS{84iVEgu+6c+>F!ie z6z1m@2hImAi9J>_MZ(YB-PttYu&@swEU9;3=EJ=GJN%XD|2;zV(4mIN=o5HAGgoH> zo9k@}B~dtyz|ajAM5`Fd(X>(rf>`sHoU@o}_@)k_|5W{#Z3^&n%r0JB6)(|wy0{xr z-Eh3pOdM`w6zFnw)6x3)Kxig#HV99dRoH!VQj#UW9oW8yZ}~1wD-2Gm1gprMbE!^W zzPY!I{gvsB`%b~)UR+dYp@R; zryBMo@YD%v7)HAN(e}uEl>6bF$4~cOD*)IY?)Ejm(?}_|9W7(|$o`?eW^aMt%~e${ z`+PiM2o{!|%N%-IE5jsnd3T*}nehOO{Jh~MmxNs}koWWft-LV_EMqc_ROnVXNl*;HmL z469N&HMu}r+c7dP5=WcW3_%bBC!&XHDlt*WJ_}{9t=9I@#ah&6-|RYH+q}g^l}Nqq zJG@I})HF~DEA}3lh)T)^+*ZWEl-WK#Ym}`AZ^TUyS6bRQT_J-TCQpOM_&U@@x{8^KIx2Q3yHqwR3+m|*GtwmI?OUvCY zpdNa`s4?*eLU2u~y4e%+s0k*U&$wct&!(TD`obU*aQFuw8)+{OHEw6F!9-62@C_N; z|FK^QoMY4b=O>92x6_WeK(||E+gQ63GoQJ67dEfLe4HYf3^t+Uu{fVsG5&Htx?jMH zrJ4zDpKgBmd9{<&&ng(th9U61Uvn|?amFPY2EcrTEFY6pzXCY|dIH$N#L*RKa{xA_5_){IF~zIb zmJg?-EJWNRt@o-nm1V@;I*O+c3TRGEecxXztP_A&>gXE0kDinCi6=tVH5)V|XI5&z zd^QpXAtaFI85e7sCI@d5#?|m0&bJK@v(qlgZWC8pn|^96safWJ0|5EIWGe_Vr-HfH z*Ill{H9&e+4bfDx#~~}N=<8}6N^%HQDz@WWsEzMEY)Cu>VgrH(4Uw_OXlGNk<`1Uc z#~pBlgr(FF7YwF;7grA~rP`LeBrL@WVLzC$_;#x0WZ#UNQ`*pK-QFCPs3oVOBFx;( z)79U&2!GmWg)4BR-M&zzWE$95Dnle}31nwgkCO7xq6KY|voQo4Y;uJA)QkbhG}k=S z$7t{NZNPxr5jhGx8k}FhWt7KxY^%aW+@5y!e|>g#Xkubq68C%F)4y{5PvuJc_wOo3 zI+I(iijNmSF`TC~VDclY3hY{z|9fhExOGia{W{KSyRQ8w3%`QBD;`W_4rM^kJ-j2# z0CJ3=58;|Lu%Hptvo4t0mfYxOr+vX1pywYf^1ZELcz@Vw*7Tu!{x(5=-mc!-z2`_l zPI;)R)kp(6M^D{Ar#y}4(R-00QA?=i+eY)HT1oWlZ3Zwi=a3L3!(%C}{BWGl!4(E9 z@^a5*3^-U-XUv|x(Z|8^q1!bWfte>e-CF;XXXKBlp zW1&cd;oxwJbWBv3gdv~v{)#7N1IxmTX-Musv+LZD#m>fN*^c>o`Caf>;d4E+#DEPW z$^QuFHP%E@*klX88Wim!EpX~kPWOxCT#I%R=T2}Iev5H ziE^Q&youv zY7?VY@Y-=gjAs`qG4d^?1<0;DTCbo5Yy9GGR8h@3E-hALV_?j5VP7-v;5AsmA9Q7qIh5&!_O-x;3*e)Zee` z!i6Jfjb?$Yws|+Kz?1~a*SjJJ5grNF`Xeq+m2eM?c_%yC{M>U{#-E0rsTe!~r$2O5 z6*~!XaAmDQ98@e|s@9RUa59!na4{WQwkq8?UO(Y;7Q>}k)J;Q-UU}4q9}c3JbK!>t zx0kTH`cYg0pjj2q^!jlPnAdX2r4V!@cggR@pE+}#SrY1PywA7H0hw(b0XI&oL{r7F z3M7E#gwytqGDUEUx5+%-lNjWB{9uqq*x%jMXZbWG-@ra#Ohqgnw4waWFMg5(k5f3X^Z_+9G ztxtH8_q5rClAwYgjUXNnw*YwxCd*}Aym6CjYs;O}C=y*~@=~M<#VLCv9C9komh$y7 z0UZxIUFD#U!0CtY>bbp@_4W4VokZ4;}bw_zF~g?=caU>MGZoO$5ku%UXF6R6Yi1tJ_ArAXmHP@P$z08Zdn_B8WAa&qeV)qFwm^Mc(WPH*-;^1{9z3TAcR}(ck?7_)JR|ZPH{(N zF8XED+L@0FKdi=YdYMr=vGf_YA;QC#M!9&4|K}V*n)oz5=cwo6@scq_!Xuwzg5!{R z7wSZO2?^Ka7LgnC`LKIz^zMzIGvF$1HVAn&hlI zN+1H#^#2AB(5R01{T@2$?kv|!$-ft$hut|US=x^xptIdptm6S~L@D{xrze+rubaGT zbl|d>u#it1Oe6>R<-UYV)M3KI--qz&>;2>S@7>h|4$nOecsJ((k88j9+p;d)N${1_ z2)o}f3L$5IZib7esgYwj2K$l=5?SN+9@m@2&WpnxX2O)DeWxvARi)*>h8A3`UyjNd zY}ax|=suqWl|h(Yx8fWbvisk&i`sst=>O?_KWhA)EESnxBBf5rlXL0AfsnBanpag- zH)-L;iA)4UMn?lImeJnc-?|?iXe-KT7-I*M!gT%p-bVDW*2}V; zF5gw~=kuf8nupVapTbTe?)B3FvPDYd@Vz`;O z&tSCmkCoQlqX5&__zmMFG_H%Gi9JhlmyFXs`Id4a$?J^jK%YqC)DpmPtf(A@o93S`tOzu)IjE~UJ{zhy} z(K&Q_42wBch?*aqCUlK`#l7qL&(ekOg3kxzg*vhWm;&*$Rr_R~CYnN*M(!2jv6_9WZxb8~*~M+9L4R*Q-(^;I-zlSy^}Yhq_dIGTp}r z6cBETx?o|^uCR2t@)!l2(92xV_^HuEQ!R&pxR6{N^eUZdCa|VevHk+VC?NPnL5{d( zoK@!I%D}hQK7<542-42){$TrI%17TM3?^W0=7}Q+>1wjX<}9UTCvF@?yhJ4cj!&(e zkBi?-jO*j$R3=Sz$=$h9*6Z~0=^Tw!xu4%VY>ajN<4IY)`U5RufYcoe+?)seY6ZH7 z?>5cCkUj;V#!e=wDy+dh9xN-#buZ2)|53A(L|!cZw>NVGtM)VKNKWB4^K{YYqchly z4%~d!(9v+H|Bh6QZ=Jm!5wKSL^^E%lns2i#)GKM29+&?lhtBhr?v? zZ-3y$sQO5dU(~=kENaHCuP9)9%NBxl>YgTMQC_Ku6(MMuS+HMZQ6r9_LOIv(2P2gT z=HpBq<{y=qcVrM{mw;}VV0U+JNuJNQ}Jw4NR@ z4UZNDdlI8x1(^FwQZY#V$5)(&-8GZu;I^khv&Gble<~6Trmx6BMUWP<{MMEnH1fo9 z3d#oE8#{?(bn5DaOps#;r*MyFcO8%$0Jno4KotD!zE5myM-tkjYjCpHf#d5d_ zqq>)U!7CQ%I5=E}-B&@PlA0|IiYaxudhcJvhkob2xjo2FJrxs6NjM0oZI!()BDR7jm0MMyc%wRc3cAa>AZ~hfxl6+En_s z`jN}a@iicB^|T$4j+3~c<8}J&`3COC%ILZ?{rnjYKW*A@^acs-a0Zj+}-Cx>-b8Q?L zvEx8ZZzbrtzrQ+DNm*szMOyzKP1oRFSNC+2G!5I>wj0|wSy+=(d&~Akk;Cy3J0z8^Pt2NGAcT2p58>~FA9vU zd#taal!Nr-+kX~^75P_P`^cBP23e|+_>d~U>~2qpuCCVTj8pT<_`)U)nH`i5I%*YR zEVGEK8<~?NDp(^slIKMZFqVAlWdYjcxG|Jv{uSN06@f zLzJ#`uSgXl4@1Y_mnR$qZ23lEBUeFC#XOF7vhDmIaPH`j6I4mr;QCj%t2xWWKvkXc zXu)sXk;~v1pHKWQTIY0~r}8!%-O^`z5O%|xzRPxu{;|gn46HUL-_TuzFc5&Y;CxyhiG_QTXetnMQ_??q8v$Xk$mdU?HLm|@Z zd-~UEPagzwJLT(qo(kv0BQOpXeE{Ygurt?v}O#S1nIf-1H-vPKE{AJE|L9 zXsq|RSyd&2{fAw*_RQP8!Yn(UICc{O}1S|t{MZc7zlAyl+;&||u+;86Mn(!v{*_A3~ z3n+p8M z;EO#Y=Ii3CZ(!DB_3kXV)_a{Nt%^s&@A~_vUL%D?H$d?VL>-lYFO-F#%}>~Yyj0eN zu1B%AkApamhe6Y{Nem`z2pkPUH9d_AncJUG7`m><92;NBrFLH`k7zGcUWV2PTX`_B zioQTh^G3Q2*5iHPNG4`Z(jY}B`YvgCr!qrab4X&bwKC`r=58&IY~^}>O@gvD`3lv3 z@0byP`PHS3M^MS{F9Q&`naD=8hdjmu@#?4gh3!97C0<7Exn>85DtC*Bv8;g_w6 zJsa*ZB}*R9{rJ2$;r@STRw4rea2AItLKlbI+D4-Zd2YNvd#;ToSok)PnhcUdl&YKR)g?!aW_%u7&kBpXyRC37%B2C=W`$H{4XxRUyz#Bi=Zaxt*aqP%2Tx6)? zsypb5PYbk{Ji!~U-p59DM&gJ>2VC20K4rPR1zJbGV6H)(hQ~vSJ6fFcQsOu)+{-my zabBL_g;qfj`c15M6;if3YQ!v2dykNk?zUo{JDJmHC%neX)A>I(lLT4lsJ7ETlw~#L z=t=Z{tl|{{jKJsK4MErfyLuX zt)1Mu&>q!!@iY3^pXt^0G}vZV#zc{mQ&~(gDe@cPil7ZmGjl_9j?Y~t`58o%D~D6# z@fl_@;}D$h6X~vYx%|#MRktvT8nlmxJ%|oxt!novrrR`&@>TOvL zh3xFbu8Jcat3m{1OS^3>ulFlaM>!zL6MTc`k}r7U?&;`8z&A6$p|Dp33e_GeQy~z8 z$b`!LDVbOQdh(AT4Xy!|GTQKBG=j%l1$fbz*Lo`l{d}P@xfxm8X7E>%I6{tJExE5TM9AQ&MlEo4 zh+WF{y0G-Q0uqih_cyZ8TsfDC(R2+6oP`kCIDGxp?DyC+`uWp3vk}Y5D3UFM`J=v` z@ZvFeQzxkes#&X4M1QqU_dL<7;!clbh^!sL3Yfx7Olv)+AP3b(tZ8);6HI5p%~f)5 zS^l9(BWlx~_+uBs!SzryKtZAuL<>%^YoZq$+KW%s+Nkk9!iYeH5)-`73n8J&LF zUZrLD5U=mr3Sa%SzwGSaqp4y4$o!!tRk`WiVAJ;aD~4&%Ak&IFjo>F7Bn*sUF~0mQyqD9j4AqLkA^oj)9~VW!O9yq}K;xoq)8Ab- z|F2U=&+Cl7=ILIas>V8%Hamo3ad>aK=BhPYp)f~w>&)bJ%9rMDG;F1;bU0WU2AubE zjAz~d68~SL^y4WLn67|jAmlMG##^n%f__=BDpk^&?F`^oqCR(ic1B9Q$$KVnh0LYd zc>@w~JfHM8l@CoOPf*x;c~bg%S~z^TEid17q(1%dV&2hulSo1lO<1__quu451gQ%l zDZK6b2Hy$8JTXP^@XUX1S*ap1-yYY&*H`JINPVNKCR}oS_vuSa2FL%zD=fWun2GiB z=R$N|Wt1Rm>T>7pm41vF{58H&Aa3rGeh=m5T;8t3lGYA?DOMou=M(U#qY*p#bVx&*~=`j(X~RJNJtzX zncz@|wOLE`Qe8A;)OftCy+*=sM?lcYi9}w&2NRycWaLl$6^vSCO87???{==TB5RZI zs;i*2{|7VuY=iTAnA*Mji+Ev@Rk|yXwPJnD#P$$IvBmK+4#z^P!rH{Xh)amWND1BNATH_*lDen$YqOb9J24^q&0u zjS{b(Z(jJvusM&Fm6h^-#{xl5PZ~KM+lX}OOiBo019??(!#TQ4HaY6xIsT^P2=}4J z+OC22K{rhOjki;V6EJJED*=eR2p3s+sj#N-QO>FVk9fNcSNgt zaw7D8Bvi;2#Mv%=Z$hRCDRHwvV#dWqKXb&rm&I6kO(_#b*^cc)h&$~@gboQ0y*}cx z-yb7q**B1sJl&(p^hjk8MW+=7$LVeo^s66)K{sL3T3ryJ`4#)^fzzb9pjrFgFMU$H zp4X+Q#f_)TI(b;*T_|m3V#9rXgw1>+A()uBI*xQZ#`Sf%G!}jLX%WJ*kkCs5c_-(H z-68?coE_Q|Z_GQdO8ef6oSPU+fMk8o?dLv|S`RV`%0za{`!D_69zle#kVbw#7u_Ip zeYHWjWAIfMVVJLFALTs=-Mh7 zi`sQvo{wXA^(-3jbMkoc;A-YJ~>zf-ua1ws_ea@F?aJsfZgo%zXz}hF?&{ zu+B<+w;Zj)W(xvCV>0(yg3WmU@+5-)H3^1|n(Qv(f({h5{-+Ac5h1HLj*@MlmdGtQ0t$+SF4_6NP``gXywCH%!Q>dNQh@(0^6l+J`tPZWA7#^z z9rDw{mQ;xxKar*fxpej*a!UNYh9l3Nn79?)&7z;$)7@|Eac|Dwyx#MCMh>>f0c8O2SnQnciz1yQzXV@$oo(dWZy6|TjIw*5>X||TjeCxvLLmS zkI{zn4aKV~Rys9%{7Z{YbbLRPjX~Yr&6V=WGrwFmS7Lb*<wL2czggoK4S+`1lBE^hMR-mJ8F z_nLa(+f6+o2F^nWwAD+5d__JmoErQ{q|cJOC#@4V@gTTX(W#J~8e~c`=Cd<)7J`=j zfzd|e(Kz$h@lTui)ny0hHyQJ{syGRi=uU1+Cw7(+*_$YHZHF^Faq@~ID38Npa0wIF z4z7MNB4HSwcg`VZESG4>YdVz5>R}<^Xon32P(DFRU3K2~QYgbAWAPympXdV>gHr;_ zP!Bj_Nb?*wxaoE0Y`kc&7XIc+3tt`gK`yKq@g9%nT1J+hwhP{cRd0Lc9l`2&B@Xke zjfvhqzw^5}K3*E7ylzEb0ZAuetUm;5vQ;)Gh2<*+LR%C26y7x}_cKfP8Irq*5-420 zqr!S0ulG)j5DPB~b&JpIGvyOG$NEOulmr5CRT>Re=imRrJtM07qzb;Eqg92;cosu3 z9|#F~l|V(0{fwNc`q{aEs+Sd8dYtIOMd|&_>eV?z_`VfsAiN~@gpqhi3!KPu(+je z?bS5&jVkc2(!x$yhsq1l-KGP<%}BkclZ2F+&z;Ve`d{yCrC+m;jt$kEgr^-bx~eu6 z?>wD49J=FrowLXL&K3%Vl&BV=K<{_;TnUHTQHESLU^8fgxU`!dHaAp8F8JQ-h2a2B ze(awF+3!&kB8G*A+qWq_${3tvYgt)1%6XAL!+M!Xi8pStdQY$oNh+2{?+g{Jjt_|D z6FFQl90VP~%YTWuux4Mt41~SD-5yQeoSN%3UU4oQR)OVsT~k)a+jS>-eB>^u!OVyW zFwcet%&g>G7SXbfw86m5vY2Nb4Que+Hm&T$jm8jDOx)^T)bwwl5hSZctfn#9GK2VN zq)h&45o)V6@!NGj_y`LNzrCaz8ymO2uNGG{J#HQfyIrpMHwsWYI}d(p5P@~PIwxhh zd``BdC1ajD;_6@8E|9H`nlRUz*cH&&cD-AMR;iVNm&oMwYJYZT?nfAKVTnw>C@@_b zyEvi2A0o!=nncO#2^RfimY}EIuP)Ri-r9>GL>a01SSOPW&5AmQUTu=1n)o#^mzU9O z2OZ9))SJ!1h8zs1DDaGq;dWr1i;03#FeQw$Psw!m{-?Fhlg@va-f6e*twLWICUKXq zr8b)i>#lpWY!P?318r3FxvrKQE+%JI+BW(qx?9Y0Bsi6L1iVjfzv%hMf2#l)WWv7PFfdF@0f zA*rdi9x21Z!z(djeFt~3@%-1cu9+1YK0d$AP@Vgpn0TEZ7kujcXunUMe<497CSsz`Ks}r`nfRwDa-m^jVut|w zd?l!9zh*#Fd6h67k!H~Pr3W&9{4nzJf>sk_8ObvjRiin|?OHb1Txc+tJ4je#Fzx5) zu&~2VBUGV;9ED|~)AOK9$F}@z5b}J^<*0u<=p>*1uRc68o|9#_X zVL^qs?#(l|r861Z6?&%yjVXnLian&7N=Hs}BJo2vGn4e=HOBw3hw2we0ExlAJbmq! z%JsmQQyjrdk>!kz^Nz*>bVEgRk~YEmsUuH|E_2XvQliRbZUGhVTOWTn*S05-P`uDp z?+>7tJGZ&+#tlv2RXul%bpk-!-QNQ1(_s)xB_=NsqWn7747rcid6yzEfk+=otmR^# z=wjF|3v+^Ll{j&Kgovhfq2B#}AQ6WYP>nr&AW6ElA5mN_1|iV5aKSKF;QHq#Vu`B- zMb<-AN}T&#%klxX4fziolp))Vdvni+f`P-f_bceXd-VIhr^cyw{g%q}Gx#EACXU1K z`Fe?lPS|JHf=Wnug!zthaw!t^@ro<;0)Z3z=ixFD`VE8#X&~*d^;3?~_<`}Lpno^- zXRw1_wp^`2`w`)DO}{Rn^|ic7bZ#g}OUJDFshtVOz1&@j0Q(~jMmUYx69T~e#I!8B z2oVDg1D_84aZJ4>AB`rgC+U2Pw-NDQ!W+4?b6AI*AslIHP2Gbj-cf}e4y8_hh)!40 zEEN&`4habvNNC*|h9M*Tm#WF=r;#RSCpZJ~-AO9C3+s=-YI}nE0>z_Hlq+H3-ffnN8os|-mRH?EAcuUvt zpG^o`VXx4DVt5wCao4)?yx@E7ihY&T_;>}}j`j7cBn+6|rFTmvV)o>DtZ7q11ibl@ z&BvlT#|pA@-_~w%R9aXfHm)p|qd_1`H&oIBLu9FQy1@A*`$k2H7b&>-=$3ln>np*k1q~`w&J3=wMeXiqrpRnVu$!dv|t4vF0NZm##}u&>jnh{#mewq z5XOY%F9>Vdv51s*ftP@)vqy;vHhQcCP#_VD0T6jd(W9jP9LB38MOT)_+avSa4kF)TF>4 zlkQbBevF6*$Bw5SdgxT0c4%CC;%7Fij~_Dt4}gsw`6@vgKmEG)(s*4*@W|Lx5dJb` zC!6DUXyS_I(6hWCu4)@@W>3xXBk@*FQl)9qze`unPci8Yl8lTDDry>9geddaoPT8G z%t8ig8pA>?kPo_1^IrjJuqG#|cuF(Bj(YiV-QV4>>Cuu(ppcUO@+*G^!lK3kSwAQs zdSApSC9eGOQUpVgFbqWeFObX-jD{?x3$qt(?0R?p4qbb{ote1Wv~AcXt6Lty!?I(y z9(E>F5SVJdWUW8y5CcO1H_q|#@mMhIt!)OqKqN}vyT+N6c~cR}tu6|Sgfj=ZNZAsx z3+Kzvuck7bWUUj;%|)G#NnB6O8}B)vPL-eM=cQ19Z}1;S>=<~MpH9`)mz76Ck*IxtDh`crzwQZ>NZJupH3k z{5S}ueYu1iVv0>iT$F`M!EL86)L7qFrL0;SwOaQQ_!?a+wR8EnWv1~3a7E0t$WT6F zWY?@coJ_IRGPyA>%bktd&dzv2@siG!p*;d!xxa?~P&N&P9Jk%B))kdns0-D#*Ch4u zsR}9|ZA)^`7wx&L#~QiV+2qOgkQhuLlCSr)P; z6)(X~>7|wY)E->uve{6rJYI{6MA#XMa#rk^kg!e-_RgWnyHaS-$$xh}Eu{j?GNuac ze*=&R2}0G6(05KoSEL2=1@Y2H>!+!5uEe|Tx!ctN$Eu(CrN5q#bE@xO-^M#G&&%5} z!K^Ssf*4-EM8Wl^yseI#EUbbF;BWZU*U7P*mtT82now4t9^Bo8r?|LjviSxZOAISWZhX^-N{*+|x?+;^w`WN)1r5v9V%EIejLRfKrppee)D*O!!|0uU0)<#mh zB^a@XYU%90iKhKRUqxhd1H0Mi%Q3rU2}^9!lry+u^~umP!7>-#);fto(Ij& zCl(`(_ps9orX=n*mtvCDEGqpfm*DC7ywQwVgjMXd!Sy~`!;aMP@`jC)Xj>5#x0WZj za#zP+x>hGji-(6;{M9lJr|bNz*#_CVPVPMYARL+UGwvFetd8FA?+IE^Z5NYLQSmM2 zu0Jk%gVfhm*^y#NRomwJB&Dzx~&&Y&LAnj16J?^TB%Zk|EFs;m%sS2sJ2l*n@sJpQ_eS zJI1V##ZCoJSv*bEvNCVBPd&4VVIK_@z0X!XhL}zxa|9I3*7f$0;A^v?8&0B%)fLpu z4}Iz5l;*8ZRnnlIDLayOhg8#Mz$#v-H)rn14WwazEa_bCX~&PqQBiA`^+OjLpQ~c- z)A6d&$UycVYsaO@sy%8X#G;s2j{cYYfTIRQArKFCb}O|d;ZoGB7AobY2fdfFG1{3Q zmZLlCex?NCUF_PUIsBA}oo;EETl7Vh5{qavC{5pW*B|fn)O_?x(|P5zY>+r~`Tv_~ zxkUq}SB;IkxwRZ;%=q9BEzEt$O=hqn%Q6~f0*#z7`&lQ?PVD@*9i2V~n zbQuS2EM03S`9Xb1vm-b^4v-St`@?K+nad+py0o7AU`1 z={(^!n?(kqF!O?a*E@x-hxCQI>#!JJw~Q=fs;cZR8=SRXGD!{zXpoyC8OfW1T-KeP z#LVnOMdY{Cfykg`h5dgmK#NY*c{(6mj}hS09jUDRWfCfqqM*Ap`aXSjQ0`8vB8~9I zB$|Hz_Oa?nHelJqGMcO^A>2(nre60^MDWUPezZKUzumq;x$w3^3?DCM_33dxLZDoG zn7{6TcE4WD(3-rp-6j2&A?@#j31mLrj&@;y&PhWN+k^p3CxD@EpStl|JeFWNz{f_3 zNNzjBk1O4<%@b!*(ogo+-FUaCF#nE zZRfK(lX$0FgPXH<7jNqP@(c`l&3a*P1dgSZRA>ZNhama0!w+>t<*gFmSXrmW1RfCX z5!+4e-KwXPCzG$6>!GJ)e5^CZfrT4gC0K5aI;GZ4(eccHcsS&0W3>b5dKD=Gsc#4j zk4{LO&m12lhjE-~T~YU7X&x$rgE`%KeG&=pSKBsP$xN&~MCo)ieFO7-*3yqbEO6Sr z*=ao3+w&lsWRgY-L5wpS4q95P^)tO|{auClD>?>for7|8fY6UWXCLwsPn-HzpEe=E zu0pk!Cv7S)KT0p&A=Xw=63n)ytzvl^!bKg(>AMB|p$uUEFnoD`_&u4H9I>+hT%fjO zW;`x;UO(q5?670W1&O<`_Q89tR<0h!8H0^IzR`mvt<&m0Wr{Bsipb94IQQP+`49s^ zQ^6Bv`w_8mVWMwH`pDN5F2L(4sEYlOyZ*Kh6q2j38KyR$SnTIz2mSW*sC#(X7zLKU zI}2JXA}a#13;RYSzjKmXmwMY0F1K?<;G%dO*wa4mbYOv75cH#)8Lzy24Nvr+rJ=|cm~yoSm+ zP&+Q5Rt&f7>)U~y ztB<>MtTg!*iTlc~-dYnmQcLG$)R(ry<@8a4B^hXoi$=h~s;Q*V<7`5O!n6|eMPt$6 zAhac_jweem6ZX0;f8B8d@-Q~)>VdKH$_Q%3*Et!3?V#8q`12C zMh2zA*_*K>-v6q%ff#`4|FivF^40g9p0Vo_1$J;{( zD!@v#zNI#up_yH2W0z-V6tLFN*H7zjjNDB5Tl>J}@f7NN##@w=l9Guxq1%&DWXWmc z=ZN~qs|;|e?mA3~qkAY2F}&OXz#x2pDlhc?VhGatuYxMDn&V40%1PP1_ZV4%a3(kH zY@ZiZa?C7#rG^<9sc(A~b92v+H1~a|TTh*_MNdeFGPgOmlLmk5TxMXHmHRzx#9;Z< zO6ARtKI#b`Qd3Apxr}Fp)A*==cV;}m~Vd3tLEgH z8Mq(6>C{VHK34HCz86v*6#)fXPmM1m^V+ykjtkJK+nx;{3?c_;Fk#(bu?DtYe{*p4 zuTFbfR=}A2baNTOize%$h~F;YW^Tgi<85{*T=FOMFJM~AY6LFIJth-vqLrb9&Zms ze7+N|kp3Xox98NouI^ySTCMZWj^<={RvNW^>Y#5yRidcI4gFHm?5KpOz=S^Ij-gPR zjF$^d)+ZO99AGAcpY7Y&s8wcPub{ReD`bh;RL$N)SA~vJw6|KEK9TdgY7FZeTBiH_ z)d+5J1wpXf6qEBQ8!DS_d`J6h>;0$O?CSaRV`jF&E(3@BcTtEHhzacI&wf$}$E1;i zZ-4V^eltLBPgRNoBz|*BuiLr(c6Ta5#j2T6Q9_3w*Jh<*>Q)29uS}1d$$nP1<##_f z+FzCQqM~IvMQKDRMlzyd*s$3LoB@=gj3!mD1$|AjU8PrlV`hx-chZ3oQVJn_bbw8H~ zj-_(aV3$|ZHkoWBhx&6cbr@p3oA$4}6!8jqX#ZN&%i9L~Vm{YHWxvvr@zF>0Y_f{Y zQK5+iqF-V*{3M@!#n6PM@blk*Bq5dIPiM>4^{skq4T)=EC*8?iTrQ4M=GsYf`NqF` zPe5aRkW-3SM*s3JX0WxrIcbexGYaQV+LQ~LP%QJo0a4O7xCX4 z9XmycGe#MUCr#$dlTTsuNsDmrtg@2|1En@_D&=F|c4d&>bm;`uLAX`q+Ct)GN!31Bh`>C{oC7a0dJpb4WtK9g7z~UzE(LTAu;kZ z#CqH3^bNu%&74A_bc-ZwPETT*I*(6NB%YCzZxC*DExFz-mBOol;yHG`QleN%~}UT%0Os3jdm`d-%FW9!^b% zaI$ddWmUfHZ>cZ#qk;(jvi5(J0(0~G6G_gkpN9aY%?<&3DGUmB#w2t2tX59vWBb~* z=`O0LyfxS*kNJ1AuqIK^B`qgcKRKLQa;^3}R*sgTQ@uvZ`{>8iJgdTRxW=(whp#k| z9q$0xT^$&JP!!MqnOG}Qu5}XWa2m?+rqU}3#2F9&p(pV1>zR&i=ob5_gw@k+#Otn` zJzSP-K-M_?7MZZ_e@iyS<4OkXEBQ6py*|#}Es^3tnwt?o51Daj1=yZ>}h8ww{Nd;-scUVgrFKwk_FAFTvk0>vI9FH$+RE zcyON{R*2p|7e?*){G8C1ba~ZoaRz_u@c{WhRrq`2jZ;!^+re1fz>`#UAb)0X_UnnK zwJe+JWPMdN&Vb`$_9wZufVNM>RKyP_;KgzvJRJv(&CK9i4Q4#8QMSI zj`h7QE!6#of1?=hx2VV5my_NPN0_`o)TTDgvbR)gdFlWV+h>^?ZF*6Bg3<$A5%Y=Ri^Dh6Qmrpt01%7 z{(OhBs+1;N>*chq*ty67WZ=q!qWp`Lz&w+&|HyQi0q*rqP9~R4SR8UY3wrsl+Gf`9BU7(S2HD08DWBI9W9JB zY-wW7NolO?o3!x%1P$FeJ?&*-?fn!%Qm^#Ip|BZhm~YdoGnNOBi05jP?ku(@SS>ny zObQ~ak6(cIQsG-w5tmp%HI8Lq=oSEKQN3NxG_APb!<1&xaFA(N-yA!{XXC9eP-$q# zOl6QWoq_oLPT9DEhn5QX*}9XSx;0rj;q6{Vz|J)|nBmP)3?${?>~MU<5Ecv@?G2_3 z`ZSfwCO!0v+^xUg*6EdxU%1fl-g|-TSKQHgWpC$l$X-RtWv4nsBJHxV;m!vMM)wjJ zO!UTzXT9wmcU7pmz%#>KbQyN4Q!w|gen;o+(CVmq3Y{y^bX%kn4*KrLC(_|lvc`Itk$H`0xJKW; z`mnBLe$7SCC9%>>K42G&(K3&1x=W5IM}rucVOswf@molQkRXG;9oCn-87ruyf&=QV zF>C9_%C($SjK2GE@gyWnZZfl~;w3pLPuyo{?UnkTn6X3tu$Mo*trH3*aWMe>#EMOg z%%6mVO$7&Jz^5jW3Q8D1_+UAQ;gmuwfmNP+<_c?`=Z(Twl?JytBi(kdk8Yt`%2N?( z2qrL|K-e!`ZUB~3#oU=Y9B>?+Rm=!S_On(3z3kUo!Q}~fi6T|)Enyy6)d*J&F0(e> zDQK+-DN6BpBe=cr<^OXHN}&D&_*DEEIg?~oCXIkWycyF`f(NQt`JRgH6#0sopH)JX z+byXFRyB_I8!@9>IqQX5*@)olzULN0kR>Ltz5E6h8QqAoumiFr!M{f8wa(Yym+gL@ zE=ZSkJQv`Z$;&=WL7%E<)V&a0+S<&Y5kPQ$4v@+5FiGDcN&SHR&wXFkmqXQ6H(AZq zv9>;ZRSlOt+`V`a{j0Dn_v~&ER*E6Te=aR#EK;zBz=UwP4YQ_qcnYQ(MnhYkNQ3vk zkki);F7g%W%bz<3?!TV?;ohqhrB1{i)Hg^#Tg?)c;dvhXfSB2#;be2pTly~z$H}9W zH3QVkSO1PpZ(u-0#BlI+H4&zF;7p;0-ySWJzXCmfaM$3tX>QT3xVS6zce7rGb~S6e z|NRekuPTI>GdQ)5n_LJ90rtqA_s_dNS9W{m6BziJBs^46Kt0n+g<3a5VMG!f84E{2 z!Yesf54I^T3eBWpRS1rT_Etm&mUdH;hD{v&vL+{0RzPb-W8 z>nWrJ~Mk-|b~` z8IFq+nP}$yy5E9DJxeF0V+|FYeHH&mhkoUeJQ~Es^D`DOQw@1D@1Y3eSiHz_jS*P% z<+(RNnb+2h5|OtfRX4mt^)zmj>gjfR*y~;dY&m@I6&;RuSr4awl2b{> zi5lcr7p5*)k3U&RH<-b}Y>8>AunBxxAhtE6YWYqc_68VAc+g4O)qYGE3Q%{yCYHCix zWLg4~2#vqcE(|VBX7D7BmFIKePwpR^iuM1Y$M31p*X{nc{`u18aFU+x(8Yy*{pQh*e)%nFouptPt+)VDv?l5fQso1f{Yc ze@Wo;5GOXskCKoxeT&S8E-g^-s7+tur#e!oe%!y4Ps1BDJ=*u=6VZk*KCLyt_wR9> zjaPKSBKsK#D;!~Gzdli^tt6&6^<@)py{UHh-AK(=fuqI9OplX8hkfdFm{)hhm-o>O zIg?f9=7so<`_HlSo(8Kx@INZ-YBm)Gy@e?r>oK4$S-NyV9T0^<;d~OQiPeesu|B`v zpPRd0>Q$Y+#xQPr5;7o|OMe7Uym@oTdrc7tqmyq65AEEwV0A<|eMZ{TDY4*}4Inw% zsmWWfoPdps#Dzs}v(Iow?5{@zxdV<3N|Diwqv)&himGd)!}_W>&#`KkQ;5OaVernK zN@?fefR&rm*4(Sg_bq77qgKfO3P*6iUmY0gO+p}9Ob}ve0o7i>d2e2G?{`_g_2;to zkN8iyS{=|UEZSFqLfyMYC)enr3nl>I+1b~~M9H^!&PAItqsa#h5a&owRAVcVT_;&w z^gIkmprv?eD0uqP+`haWcL2adfV+)w9(2+j#9S!S^JZtc{F8=aa?h6@+_QT?k@CED zJ6PeblltSQ;VA41U?Q%v;C2JhGRbTjU4HgSLJo&-kcZPU3Hd$zK?PLFYK^zYVOHI% zYlp78V5zxhHw_YideT=D5ddbC`m_qVEwn(hM&CYdKV?^qf5=xjf~V70Ssz&vs}>il z@##ZCKlx^Mn62XRe3fO6%TX^(-b~OvF6N*TvEEIhQvL$81S^+?GYRO{Mr7i!FlMQ` z!zK@lwhdSat3iNix1En7pIB)y?e8MT*-n?-k*;MMxg7sfF#R(t<@dG^mr1M}Cu0a- z9DFDWc;fa>U*p_rq+bPDa8*!HY{vKql=2J$d0K4spWh+^^s}-@9UJC+nCSr~rsSNB z_tuW9i=S?Y6+kayw~5%Pn3yAMC_Z6;P|fYkdOP^uwPMWq8yYo}xS#8cq*FtIuakdx z|7eswW}TUL)QPRWyBZYbge>A+`990@}RgB!JsR$&#D6?TEYbO$FDMKZa!KCcwjnSQYHFlfI zqe?#8WxVd{#a0I`R?M87QxNq>r~7cX z|0*B@_YM>qH)LnMcUao5Rxe7c(=+u1XxU(qnC_KuEBSZtLFremp_$l^x1%=~T}VACM< z1M`v7s6xtaRiS;lA*p^j!Cuu<`f_Qkj^*ZX2k0K)XCqyWJ2R&X&`y>by8tzF=@z$4 z8zSDB1WxKPX@43ESL{q1(|{!Nv*PTs`tn3rwroXskylXL9*KsV8GYSMw=>P!ncVLl z^@;E;;J%P@Ch?1pg`yoK)iSVDSYo~VW36py$bv6w;N~8)Q@?eac%Qr?DZaRD%znd| zka|2Ou{>_Ny-q#oybtx)Al<^xz1Ga1$iNcO1Lmrw4E5xBZ=vtB&Xd($6&%V^Vmh?T zY))<~Kg2@*J+EkDPh@4C%^BkZ)=W{fQBN|#f(gJW_4sDR3tZ{fZ#Y&B_1j97%4!5D zB2#qMLCn;%2>E>nv$(TL$ONMoU0O-Ak!bG7O9?<`ovkN~&@?~%xGIhNzD-~gj;5_s z)z6Yd#2~1T7N_8!(b^f02O@wXOc0*FmT#T(|qk z|L(i-*)umr!+tDu-Z-KIoZHrv6R{4tNNHRw83#+&U9!Wju`moQwiX)}+E?z5s(Frk z1dNTcI6Ujh0T^96`uL!E?7%;0tDGOU7@NAvL#1n4VH0N@*pTUw z6hkKANeMO-#PlJ{|B(77h4`n@)XE>Ae3@z_+ z>yzTnn$#f#+%9%gNqTYNUVx_7Y-fz(6i`Gf1N_fFFcB;cQMoUlM5i=)jarjw%AiVM zh;iP{rk=MP1M49@a)l_c{Gz5V_&+Yw-_ca98&0uPkpVqT%>8rQO-n6L*4gG(ZzZAH z6DyM}ZIB`w3^Poh47achw~Y9bKnxThrF*z4Z5tlvkNZcBgT4V@c`j+_E^JUnqzki_)41vH6D9>qL zSYGen${j6j*ThBruU*eE?v&0*u?ZTg?HC>kt6u25_xz#jcX5`2#mT%Uo8|6=12k9+ z7@y>q8v#kiXgjmsbE7zm(19&N}~2a8ot)BAe;WhS;L{ykwq zcU@)=f0O<_1luRJD7bp|?;)vM<~MqiriP9<*)=Q8wi-1pGVp*bGF0>2w4r@RSge?j z6no%h-=0($@voIWUEV30UWA>vZkBnC|F5Fe0p|sY3s~c}w>}>GOiDuP zgqG9uI~VE8T>zT~pnR_{+O>M(=i;%jhRnm9Uarfd3Gt6oBQs(q`QA>Ogk^7&!iYVo zdR2uZFyEVkKjerYJP=xyVjfO#+MhCq$e6i*gy_?clQK=6r5dj;9T`jqit}sfiwK2& zh5ZagV)Pd#0~mQ?fTOTSWxkEd^w@6_$?v;5K{ugk#lkrBqP(LVp^+oXUj_?!?>qx@a*wV$B2%?ZdC9`WK4!GSzG3( zE$$kgj;2hRDm6q;BHu*9xddq)qLB(5s3z8k!GWsqPfB^sn2eR}<-y0%4VTJVrDYH0 zL0a44o1#d8O4Y_iQBgt6WCso~oQ^J?8@)M;bBzoomxDju2n#S~Vc#P|TKhyMdAr|` zV({H$FP&!_E$9U(r-7k)n*KoAIiCZCm8WmmRKIRGwjZsUmH@(JCKY~Jy~Nf<$D=kj zH#c*#F;YrI{GqdBSR9aYvtQboI17dI?kXXhCB>qa4m0`+^KxM)h)|^FPakgYxgOAc zR;k@)oSRQY9lgytg%{S}b^!%ac;W$XGR#1L^vgSI)9P8>vd2q|FuS$QuP<~N*!`Z; z;!jl&S(L-*gT>W0YfBR)@8|rWA`UcU^opbZI1L=s{)j57p1)-{FjglW9vA1qXAE=v z=!Pq!qgif74-Qp+&*1ctZm$Z*SMlB=zX}GnohsrKZOs>swwoS*J)2ZM6oibb@T44J?AI}bZKsTuZ^d$mqf13pV;VZ?CrlN-RC1w@OTTO1Sk5a&;K0o% zqmH6OSNw=a{*CS`EiF&x*SkFs+89eHQf@5n9CIiJ3`l;N8y00Y6GbCsCXI}QBbX)e zzmM7I>^wnQL$o4Z(M~?GSj9cqNGl{ChZ%Y zj3#3Fkp<+UJ8%ax`xc&OFKsr4PJJ?unCk%r-++AJq-Z64idnT?ns>kcD-B&{aVMuI3J~?^ zrRI#zPD+AjMsYV&|LT@4ZBL8K7le%)LfcF9Do%c=uxlMRa@MYP>Bo_hNlGMZ%9|N6 zQo|CH4TS>QU>O@17)9g~;-fnPDla;A{`ZQYmc<3{YKO(nYh)c)Jpw=g7Di0~X?K4l za~nW`^cZ9U`TNUJ*@hc%&!f3I+O@1<=~9llQ*63v=}~+F2Gd)!@vorbrPx8E(#p~} z;84`vU~m87*6w}gqJ4IEw_2x>eW!)*qK(Ii{xxbL@|*`@bDIbtGy)Ytq{~I5tQwau zp;(OmukdU}2y93gI3X)%G#cpt$gYhYxY6B?IIeD95A-TYNgc>na_Sh@K*=Fp(J8%R z!V$zjWGEp_c9_28g5A#Bt(m#sT$GY`zMFPE>ir5}IT~u%f06;(I+BtWfk3p?d{l;- zNiJ>UWyJq{KRROcRho5(ZhXPe{}KhrGDO~mAP&)KekFoceHp6QWUuvg$B*mL6b4Ot zFPdr8#+pXWrty%Pwn}kHT0{U<3jara&;l;*bqOsE&{fjcPm~b18gL-^fY|mR78N1l zl@UA#M-v+b?}nUhvgiSnzQg*ovsHaN3Gq0%1NG#<{Y#jHe?O36ZD)+@>I|*Fk1x%8 z=$4p$gh?m9~oo1)t z3%SKKW#r~?<1{<|`3=R_7oqvA#`u|1mDSyg<64<=`-$_pM|Ld~yKqIsvc-#uZea zCY3T!9(!B=)NINZat@vSUrARU2-Vky&0?7{8e@oTGrGo}QrY(z`!MnT_eatd z_W{loH9j^}`KO5ZAY!fl<56sC0>Dg8NfmV8Sw*om%Kex{H62-Y05Z80X>frfRPZCf zDK)3+m4ua3ta~S}9+tI>;EMcC5=vGB3G<8KcY8_^VK?nb-Q6(*4Yt+MVVtDBoJu=Qa81Va2jfm34CLXMd-6brb<~V4LKK>M} z)j7%%2dIvyjYawcEWzhhbpO0Ep{I&H>zDZM)|{FR@?r6dfkzJ3zX9gtqkj{SyXDt1 zvb8l+Kll0m&e?P5Tyb+}@`am{8>f%OIiyu~nx&frPs}-sSylzOdC0Hg^T(vlqqp=HD}z=EbDZ zPIAF-#|N{J!_n9rWP6%9JjKHFQeA#sv-8@&^OSPJg%ArenB^IPc_t#4yjw!F``29y?Y0NjXUbxQ{G)K=FyE8%jzY{0NUB|z) zxIPaR2HAhnF{d1@{bs~s842Et-xVd65d_pi7nBiGWj(I%Gu;K4w!cR20U6+*grgUQ z3E^d_yA;&*V%HZAV&_SyJ%`a%{w6}@rk*#|o6sDftnL(C5!X@5S)WL>FLf&R4*nT} z$~&T-pA_ugv!uAOPg{Yqc*7Fzm_;6Bnnt`{eYsWGSajRvoXWVzhz`_CsS|-LBqa(& z%w0Xi`%#5G&uCa-88U@O`=eUN^+~<(GpCy=#{h&qaNuXw)uG9M#Ia%v+nws#*RD&I zOJ&)6*;aTVQ&aCUO|P|mpjg6nuyk?iW=w7*sCigK@fsd@)Fis3tH1u~(Ok3h@O)gK z{n1P*S0gedyc9_x}6_L z-ws~a)xHs~nw)C0Ux{EFwP9osFAI;-8abD317CdwKM>=?RJi?mPAuO(J`V7Rx@rfi zu2pqhxRzt5>%U(RBJ(pa7vz+?EtwORVcCpyUqsJ5nPZ331Z;_lOAW(7pr?{;UvL7|7oZKi%s`5`y`d}C z!f5Ezds{;#8f78a5-uUYQ0=}OEIvOUMR6K>1;EUPkm$DKU)?sRG%@kppN+oo((Yfi z_%pTg@tDA`Xu&bmB@F@lT*=q56O~X;fAOQJBHrV%T!qVu5LNK{s?6?objoVtUr;gZ z@pA4y!}r4}9D)`ZYhe*s@t|~fM)qMfdWAqGRg*`G=ar}_uCFj1IR`u=_i}B)M=j6d zX7A1k_ufs(MZhV1kp-<9ED-sxOosH^%48W^)(iGZ;|*CKR4*||1Qb~fdx$5R?;`&n zVqV|brYFCQMG7uf2l=?CMK}WeK-g}gMT4)a#{MTkIrAng*dGMbQ-BZ}lwo{?kZ~pE zRLp6kuX{z`NuQr|e?3|A@~UV4_aL-L30YF-LpJiHtu^E-hLKeDpq`^)RCZpNq3{No z+f}3gLeWb;V9mT`W*5=g^dLVmurC}h*N4}>?vLxWcYw6?Jd~AgCKxc!X6+r=nVcHP zy<~W6zm0`Bi12clvlDsqk@GT>fvn>Z$i+Qk(vqv~tNI4f$-|xAQ=|=H7(fI&b-HtX@1uBhV3>IL?W&jvfqjFYU)^mr4e zCS0bKGR64y6k-OM__CB#NL?>uXMi@9vdblnwV~r@uSlbb#lBZP6ay@1nfWp%=GNK_VPekorxQmI zTClp^gh2m0P&&a1I>YLUd5gHvE^1Z?628a)u!qUH<8sZ>?Y&u0fNZ2tkelrRvk;fn z7xue|>MALNBL|(ZFME0u25v=lHtD|SkCLZVWUvV+h*Hhqr*0VgP}Dkrl12EL^Uaj8 z5t7S^J&9G}6_=i%Cj@U0@cM2J;j#3k5sH(Dsa48uf}tj@FUi-|BwS}`3vruuspK8# z2}6faVLZ%q0_kllgQoX%Q zl%q49pbX;#6-El%MK`oi3XP&i1*io-86^HvTcFm~NlgEM<-zmz+!+vNq-!f%b7lsV zGW1J$*E z0=abOCBbefWK^;`SpDBmSP8a#*;5URQm2h8C6YiZt|gg!xqh2BTgig#FYc@I&BC`v z@WoxET%i!-b4)3V!$RuFVPQ#;tOBZwMzR++iKa>R=Q+T-OVTh;O(OGfd1^g$RcIKe zWp{9HZ&FW^O5SQHD-5(EVKza;(Tc}BRxN`tC|P&t6ubp-cp=GB5OyB%7uK$zwlliF zCXg-{;`yfZucC3gS{~wVJ+kdqos^x}yjxFNia~EO9#q(Ah*bb&L+z_IT6aF{$ zH-wCc;%(Tm9eZMJ_ik#fQtf!HrZhDwaPWqDfggCT>|-!WkP%8i(cwR9Z1^g5&{53D?v7hSxnM3;Op-VKa;&J{zJRi_Ywka^Rg? zs6ayZWA;#8Sf3uhV$y)+n)-W?Qi!~d#7xJ_GHA{i3fE>ekMPaJbSo#CnF6zG^^uvV z1>CO{w?HNDXbXjU*v80~%DvQJ)Q)|P6$vaUuig{Ah-0u!DT#vaT}wTsq#ruJ1j|Okvow?s47-bakkRd7_lqP8XzniYYaO8KL+jIOy3an7NK_ zTr64<(L8UxIE7rWK?(0%#HLb^nB*$*igQc6#)O^nNgdM}QcJGt^)|JXe5N%^(?&L7 zXEyY0tW9<<`T3mklpdcrI5(`D(U+~c?Gqlk&E`gRYMW%NA8#_PK$)vA2+RPTq-`Z?u0fgvKMS({^2mAJNd oTj%)pak}_^8BGBcg#3`Z4HHshAFlBhbLIhum6<)c*3>ulzq;glM*si- literal 0 HcmV?d00001 From 6b7c2c6764f8bb328a73d8fbe0e7c64630711fb0 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 5 Dec 2024 17:40:58 -0500 Subject: [PATCH 301/379] Avoid googletest include order errors --- app/tests/TestDriver.h | 16 ++++++++++------ tests/TestUtils.h | 5 ++++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index c4fe08d..b01796c 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -3,15 +3,19 @@ */ #pragma once +// clang-format off +// GoogleTest must be included first +#include // GoogleTest +// clang-format on + #include "Driver.h" #include "TempTextFile.h" -#include // for std::remove, std::perror -#include // for std::system -#include // GoogleTest -#include // for std::cout -#include // for std::endl, std::flush -#include // for std::string +#include // for std::remove, std::perror +#include // for std::system +#include // for std::cout +#include // for std::endl, std::flush +#include // for std::string #ifndef _WIN32 #include // for WEXITSTATUS diff --git a/tests/TestUtils.h b/tests/TestUtils.h index a49dff8..adf443d 100644 --- a/tests/TestUtils.h +++ b/tests/TestUtils.h @@ -3,6 +3,9 @@ */ #pragma once -#include // for GoogleTest +// clang-format off +// GoogleTest must be included first +#include // GoogleTest +// clang-format on // TODO-TEMPLATE: define any common test fixtures here \ No newline at end of file From 90e0fb50e005f43790cc4697b84147c957a65344 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 5 Dec 2024 17:41:07 -0500 Subject: [PATCH 302/379] Use target-compile-features --- CMakeLists.txt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 70be23f..5873428 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,13 +59,12 @@ option(RUN_TESTS "Run unit tests for the main library" ON) ########################################### ## SETUP ########################################### -# GoogleTest v1.12.1 requires at least C++11 -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - +# Create an interface to set common compiler flags add_library(proplib_compiler_flags INTERFACE) +# Require at least C++11 +target_compile_features(proplib_compiler_flags INTERFACE cxx_std_11) + # add compiler warning flags just when building this project via # the BUILD_INTERFACE generator expression set(gcc_like_cxx "$") From a57f6d60f7e56b5c670536e142d4bfb8a5842de5 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 5 Dec 2024 17:41:13 -0500 Subject: [PATCH 303/379] Update comment --- tests/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index facec7e..1dcc0c0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -3,8 +3,8 @@ ############################################ set(TEST_NAME "Test${LIB_NAME}") -## TODO-TEMPLATE: Include source AND header files here. -## TODO-TEMPLATE: See CREATING-REPOSITORIES.md for examples. +## TODO-TEMPLATE: Include all source AND header files for tests here. +## Do not include the source and header files of the library under test. add_executable( ${TEST_NAME} "TestUtils.cpp" From a1b9492fa92fb931ea098317a4e2a816a2129a34 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 14:18:16 -0500 Subject: [PATCH 304/379] Update p2108-test-data to v1 --- extern/test-data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/test-data b/extern/test-data index 8a8497d..e46db67 160000 --- a/extern/test-data +++ b/extern/test-data @@ -1 +1 @@ -Subproject commit 8a8497da1ce996d00ba6f291079117c5098b8ded +Subproject commit e46db673fe14732cb3b25d0b37b35316de7b9d73 From 3d3188d985e3db8a4dcde9b292e9b7a1a8afd5fa Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 17:42:12 -0500 Subject: [PATCH 305/379] Add always-passing test to verify discovery --- tests/TestUtils.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/TestUtils.cpp b/tests/TestUtils.cpp index 0bb84a0..4a62ae6 100644 --- a/tests/TestUtils.cpp +++ b/tests/TestUtils.cpp @@ -37,4 +37,10 @@ std::string GetDataDirectory() { += "test-data"; // Name of data directory as cloned in the `extern` directory AppendDirectorySep(dataDir); return dataDir; -} \ No newline at end of file +} + +// TODO-TEMPLATE: Remove this test and write your own in other files. +// This is included to verify test discovery is functional in the template. +TEST(TemplateTest, TestTemplate) { + EXPECT_EQ(1, 1); +} From be5c654ee774d8bef4edd1305879367bddae80bf Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 17:44:44 -0500 Subject: [PATCH 306/379] Reintroduce 32-bit configuration, improve CMake status messages --- CMakeLists.txt | 109 +++++++++++++++++++++++++++++---------- CMakePresets.json | 98 ++++++++++++++++++----------------- CONTRIBUTING.md | 26 +++++----- README.md | 19 ++++--- app/CMakeLists.txt | 5 ++ app/src/CMakeLists.txt | 8 ++- app/tests/CMakeLists.txt | 3 +- src/CMakeLists.txt | 14 +++-- tests/CMakeLists.txt | 6 ++- 9 files changed, 184 insertions(+), 104 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5873428..b1cc984 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,19 +19,36 @@ if (APPLE) OUTPUT_VARIABLE MACOS_NATIVE_ARCHITECTURE OUTPUT_STRIP_TRAILING_WHITESPACE ) - message(STATUS "Current macOS architecture is " ${MACOS_NATIVE_ARCHITECTURE}) # If running on Apple silicon, try a universal build. Otherwise, a native build. if ((CMAKE_GENERATOR STREQUAL "Xcode") AND (MACOS_NATIVE_ARCHITECTURE STREQUAL "arm64")) set(CMAKE_OSX_ARCHITECTURE "arm64;x86_64" CACHE STRING "") - set(PROPLIB_ARCHITECTURE "macOS_universal") - message(STATUS "Configured for universal macOS build") + set(ARCH_SUFFIX "universal") else () - set(PROPLIB_ARCHITECTURE MACOS_NATIVE_ARCHITECTURE) - message(STATUS "Configured for native macOS build") + set(ARCH_SUFFIX MACOS_NATIVE_ARCHITECTURE) endif () +elseif (WIN32) + if (CMAKE_GENERATOR_PLATFORM STREQUAL "Win32") + set(ARCH_SUFFIX "x86") + else () + set(ARCH_SUFFIX "x64") + endif () +elseif (UNIX) # Only non-Apple Linux evaluates as True here + set(ARCH_SUFFIX "x86_64") endif () +# Convenience function for printing visible status messages. +# Accepts arbitrary number of string inputs, each printed as a +# new line in the status message. +function(proplib_message) + string(REPLACE ";" "\n-- " all_args "${ARGN}") + message(STATUS + "==========[PROPLIB STATUS MESSAGE]==========\n" + "-- ${all_args}\n-- " + "============================================\n" + ) +endfunction() + ########################################### ## PROJECT METADATA ########################################### @@ -55,34 +72,61 @@ option(BUILD_DRIVER "Build the command-line driver executable" ON) option(RUN_DRIVER_TESTS "Test the command-line driver executable" ON) option(DOCS_ONLY "Skip all steps except generating the documentation site" OFF) option(RUN_TESTS "Run unit tests for the main library" ON) +option(BUILD_32BIT "Build project for x86/32-bit instead of x64/64-bit" OFF) ########################################### ## SETUP ########################################### # Create an interface to set common compiler flags -add_library(proplib_compiler_flags INTERFACE) +# add_library(proplib_compiler_flags INTERFACE) # Require at least C++11 -target_compile_features(proplib_compiler_flags INTERFACE cxx_std_11) +# target_compile_features(proplib_compiler_flags INTERFACE cxx_std_11) # add compiler warning flags just when building this project via # the BUILD_INTERFACE generator expression set(gcc_like_cxx "$") set(msvc_cxx "$") -target_compile_options(proplib_compiler_flags INTERFACE - # For GCC-like compilers in any configuration - "$<${gcc_like_cxx}:$>" - # For GCC-like compilers in Release configurations - "$<${gcc_like_cxx}:$<$:-O3;-DNDEBUG>>" - # For GCC-like compilers in Debug configurations - "$<${gcc_like_cxx}:$<$:-g;-O0>>" - # For MSVC compiler in any configuration - "$<${msvc_cxx}:$>" - # For MSVC compiler in Release configurations - "$<${msvc_cxx}:$<$:/O2;/DNDEBUG>>" - # For MSVC compiler in Debug configurations - "$<${msvc_cxx}:$<$:/Od;/Zi>>" -) + +function(configure_proplib_target proplib_target) + target_compile_features(${proplib_target} INTERFACE cxx_std_11) # TODO interface here? and below? + target_compile_options(${proplib_target} PRIVATE + # For GCC-like compilers in any configuration + "$<${gcc_like_cxx}:$>" + # For GCC-like compilers in Release configurations + "$<${gcc_like_cxx}:$<$:-O3;-DNDEBUG>>" + # For GCC-like compilers in Debug configurations + "$<${gcc_like_cxx}:$<$:-g;-O0>>" + # For MSVC compiler in any configuration + "$<${msvc_cxx}:$>" + # For MSVC compiler in Release configurations + "$<${msvc_cxx}:$<$:/O2;/DNDEBUG>>" + # For MSVC compiler in Debug configurations + "$<${msvc_cxx}:$<$:/Od;/Zi>>" + ) + if (CMAKE_GENERATOR_PLATFORM STREQUAL "Win32") + set_target_properties(${proplib_target} PROPERTIES COMPILE_OPTIONS "-m32" LINK_FLAGS "-m32") + endif () +endfunction() +# target_compile_options(proplib_compiler_flags INTERFACE +# # For GCC-like compilers in any configuration +# "$<${gcc_like_cxx}:$>" +# # For GCC-like compilers in Release configurations +# "$<${gcc_like_cxx}:$<$:-O3;-DNDEBUG>>" +# # For GCC-like compilers in Debug configurations +# "$<${gcc_like_cxx}:$<$:-g;-O0>>" +# # For MSVC compiler in any configuration +# "$<${msvc_cxx}:$>" +# # For MSVC compiler in Release configurations +# "$<${msvc_cxx}:$<$:/O2;/DNDEBUG>>" +# # For MSVC compiler in Debug configurations +# "$<${msvc_cxx}:$<$:/Od;/Zi>>" +# ) + +# Force 32-bit compile when configured +# if (CMAKE_GENERATOR_PLATFORM STREQUAL "Win32") +# set_target_properties(proplib_compiler_flags PROPERTIES COMPILE_OPTIONS "-m32" LINK_FLAGS "-m32") +# endif () # Enable Hot Reload for MSVC compilers if supported. if (POLICY CMP0141) @@ -95,28 +139,39 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" CACHE STRING "Set set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" CACHE STRING "Set the CMAKE Library Output Directory") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" CACHE STRING "Set the CMAKE Runtime Output Directory") +proplib_message( + "Initial Configuration Complete" + "Target architecture: ${ARCH_SUFFIX}" + "CMake Options:" + " BUILD_32BIT = ${BUILD_32BIT}" + " BUILD_DOCS = ${BUILD_DOCS}" + " BUILD_DRIVER = ${BUILD_DRIVER}" + " RUN_DRIVER_TESTS = ${RUN_DRIVER_TESTS}" + " DOCS_ONLY = ${DOCS_ONLY}" + " RUN_TESTS = ${RUN_TESTS}" +) + ########################################## ## BUILD/RUN ########################################## if (NOT DOCS_ONLY) - add_subdirectory(src) # Build the shared library + add_subdirectory(src) # Configure the library if (RUN_TESTS OR RUN_DRIVER_TESTS) - # Set up GoogleTest + enable_testing() + include(CTest) + # Initialize GoogleTest if any tests will be configured if (EXISTS "${PROJECT_SOURCE_DIR}/extern/googletest/CMakeLists.txt") - enable_testing() set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # Ensure GoogleTest is built as a static library if (DEFINED BUILD_SHARED_LIBS AND BUILD_SHARED_LIBS) set(BUILD_SHARED_LIBS_${LIB_NAME} ${BUILD_SHARED_LIBS}) set(BUILD_SHARED_LIBS OFF) - message(STATUS "STATUS: BUILD_SHARED_LIBS is " ${BUILD_SHARED_LIBS} " to build gtest library.") endif () - add_subdirectory("${PROJECT_SOURCE_DIR}/extern/googletest" "extern/googletest") + add_subdirectory("${PROJECT_SOURCE_DIR}/extern/googletest" "extern/googletest" EXCLUDE_FROM_ALL) include(GoogleTest) # Restore initial value of BUILD_SHARED_LIBS if (DEFINED BUILD_SHARED_LIBS_${LIB_NAME}) set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_${LIB_NAME}}) - message(STATUS "STATUS: BUILD_SHARED_LIBS is " ${BUILD_SHARED_LIBS} " to build ${LIB_NAME} library.") endif () else () message(SEND_ERROR diff --git a/CMakePresets.json b/CMakePresets.json index f974223..3ed7cf1 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -15,7 +15,8 @@ "CMAKE_BUILD_RPATH_USE_ORIGIN": "ON", "DOCS_ONLY": "OFF", "RUN_TESTS": "ON", - "BUILD_SHARED_LIBS": "ON" + "BUILD_SHARED_LIBS": "ON", + "BUILD_32BIT": "OFF" } }, { @@ -25,6 +26,7 @@ "description": "Base 'Release' configuration preset for ITS PropLib libraries", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release", + "CMAKE_CONFIGURATION_TYPES": "Release", "BUILD_DOCS": "ON", "BUILD_DRIVER": "ON", "RUN_DRIVER_TESTS": "ON" @@ -37,23 +39,44 @@ "description": "Base 'Debug' configuration preset for ITS PropLib libraries", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_CONFIGURATION_TYPES": "Debug", "BUILD_DOCS": "OFF", "BUILD_DRIVER": "OFF", "RUN_DRIVER_TESTS": "OFF" } }, { - "name": "debug", - "displayName": "Debug", - "description": "Build library and tests with debug options, skip building docs", + "name": "debug64", + "displayName": "Debug, 64-Bit", + "description": "Build library and tests with debug options. Skip building driver, driver tests, and docs.", "inherits": "proplib-config-debug-base" }, { - "name": "release", - "displayName": "Release", - "description": "Build library and tests with release options, and build docs", + "name": "release64", + "displayName": "Release, 64-Bit", + "description": "Build library, driver, docs and tests with release options.", "inherits": "proplib-config-release-base" }, + { + "name": "debug32", + "displayName": "Debug, 32-bit", + "description": "Build 32-bit library and tests with debug options. Skip building driver, driver tests, and docs.", + "inherits": "proplib-config-debug-base", + "cacheVariables": { + "CMAKE_GENERATOR_PLATFORM": "Win32", + "BUILD_32BIT": "ON" + } + }, + { + "name": "release32", + "displayName": "Release, 32-bit", + "description": "Build 32-bit library, driver, docs and tests with release options.", + "inherits": "proplib-config-release-base", + "cacheVariables": { + "CMAKE_GENERATOR_PLATFORM": "Win32", + "BUILD_32BIT": "ON" + } + }, { "name": "docsOnly", "displayName": "Doxygen only", @@ -91,18 +114,32 @@ "verbose": true }, { - "name": "debug", + "name": "debug64", + "inherits": "proplib-build-debug-base", + "displayName": "Build Debug, 64-Bit", + "description": "Build 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 library and tests with release options, and build docs", + "configurePreset": "release64" + }, + { + "name": "debug32", "inherits": "proplib-build-debug-base", - "displayName": "Build Debug", + "displayName": "Build Debug, 32-Bit", "description": "Build library and tests with debug options, skip building docs", - "configurePreset": "debug" + "configurePreset": "debug32" }, { - "name": "release", + "name": "release32", "inherits": "proplib-build-release-base", - "displayName": "Build Release", + "displayName": "Build Release, 32-Bit", "description": "Build library and tests with release options, and build docs", - "configurePreset": "release" + "configurePreset": "release32" }, { "name": "docsOnly", @@ -113,40 +150,5 @@ } ], "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": "debu", - "inherits": "proplib-test-debug-base", - "displayName": "Test Debug", - "description": "Build library and tests with debug options, skip building docs", - "configurePreset": "debug" - }, - { - "name": "release", - "inherits": "proplib-test-release-base", - "displayName": "Test Release", - "description": "Build library and tests with release options, and build docs", - "configurePreset": "release" - } ] } \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 81696d8..169a31b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -151,7 +151,7 @@ tests/ .h # Any headers used by tests go here as well. CMakeLists.txt # CTest+GTest config. Files containing tests must be included here. CMakeLists.txt # Top-level CMakeLists.txt: project metadata and options -CMakePresets.json # Presets for CMake, e.g. "release", "debug", etc. +CMakePresets.json # Presets for CMake, e.g. "release64", "debug32", etc. ... ``` @@ -194,21 +194,21 @@ generating the Doxygen documentation site. Below are some examples of how CMake can be called to compile this software. ```bash -# Configure and compile in release configuration -cmake --preset release -cmake --build --preset release +# Configure and compile in 64-bit release configuration +cmake --preset release64 +cmake --build --preset release64 -# Use the release configuration but don't build Doxygen docs -cmake --preset release -DBUILD_DOCS=OFF -cmake --build --preset release +# Use the 64-bit release configuration but don't build Doxygen docs +cmake --preset release64 -DBUILD_DOCS=OFF +cmake --build --preset release64 -# Configure and compile in debug configuration -cmake --preset debug -cmake --build --preset debug +# Configure and compile in 32-bit debug configuration +cmake --preset debug32 +cmake --build --preset debug32 -# Use the release configuration but don't run driver tests -cmake --preset release -DRUN_DRIVER_TESTS=OFF -cmake --build --preset release +# Use the 64-bit release configuration but don't run driver tests +cmake --preset release64 -DRUN_DRIVER_TESTS=OFF +cmake --build --preset release64 ``` ### Supported Platforms and Build Options diff --git a/README.md b/README.md index 9a298e5..c78b15e 100644 --- a/README.md +++ b/README.md @@ -91,18 +91,23 @@ you will find comprehensive documentation of this C++ code 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: +provided for cross-platform builds. Presets provide default sets of compiler flags, +and additional set default CMake options to control which parts of the project are +build. Below are a few examples of how this project can be built using provided presets. ```cmd # From this repository's root directory, try one of the following command pairs: -# "Release" configurations compile the library, build docs, and configure tests: -cmake --preset release -cmake --build --preset release +# "Release" configurations compile the library and driver, build docs, and configure tests: +cmake --preset release64 +cmake --build --preset release64 -# "Debug" configurations skip building the docs: -cmake --preset debug -cmake --build --preset debug +# "Debug" configurations skip building the docs, driver, and driver tests: +cmake --preset debug64 +cmake --build --preset debug64 + +# Additional options can override presets: +cmake --preset debug64 -DBUILD_DRIVER=ON # "DocsOnly" configurations only build the docs: cmake --preset docsOnly diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 780d972..cf55806 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -4,16 +4,21 @@ set(DRIVER_NAME "${LIB_NAME}Driver") set(DRIVER_VERSION ${PROJECT_VERSION}.0) set(DRIVER_HEADERS "${PROJECT_SOURCE_DIR}/app/include") +set(DRIVER_TEST_NAME "Test${DRIVER_NAME}") ########################################### ## BUILD THE COMMAND LINE DRIVER ########################################### +proplib_message("Configuring command line driver ${DRIVER_NAME}") add_subdirectory(src) +proplib_message("Done configuring command line driver ${DRIVER_NAME}") ########################################### ## BUILD AND RUN THE DRIVER TESTS ########################################### if (RUN_DRIVER_TESTS) + proplib_message("Configuring command line driver tests ${DRIVER_TEST_NAME}") set_target_properties(${DRIVER_NAME} PROPERTIES ENABLE_EXPORTS ON) add_subdirectory(tests) + proplib_message("Done configuring command line driver tests ${DRIVER_TEST_NAME}") endif() diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index 592ffa5..ef330eb 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -21,6 +21,8 @@ target_include_directories(${DRIVER_NAME} PUBLIC "${DRIVER_HEADERS}") # Link the library to the executable target_link_libraries(${DRIVER_NAME} ${LIB_NAME}) +configure_proplib_target(${DRIVER_NAME}) + # Add definitions to enable version identification inside the driver add_compile_definitions( LIBRARY_VERSION="${PROJECT_VERSION}" @@ -32,5 +34,7 @@ add_compile_definitions( # Set some target metadata set_target_properties( ${DRIVER_NAME} PROPERTIES - OUTPUT_NAME ${DRIVER_NAME}-${CMAKE_SYSTEM_NAME}-${DRIVER_VERSION} -) + OUTPUT_NAME ${DRIVER_NAME}-${DRIVER_VERSION}-${CMAKE_SYSTEM_NAME}_ + DEBUG_POSTFIX ${ARCH_SUFFIX} + RELEASE_POSTFIX ${ARCH_SUFFIX} +) \ No newline at end of file diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index a32cac5..8a9f4ef 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -1,8 +1,6 @@ ############################################ ## CONFIGURE COMMAND LINE DRIVER TESTS ############################################ -set(DRIVER_TEST_NAME "Test${DRIVER_NAME}") - ## TODO-TEMPLATE: Include source AND header files for tests here. add_executable( ${DRIVER_TEST_NAME} @@ -31,4 +29,5 @@ add_compile_definitions(DRIVER_LOCATION="$") ########################################### include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME} GTest::gtest_main) +include(GoogleTest) gtest_discover_tests(${DRIVER_TEST_NAME}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f0e63fa..c681ee6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,7 @@ ########################################### ## BUILD THE LIBRARY ########################################### -message(STATUS "STATUS: Start building the library: " ${LIB_NAME}) +proplib_message("Configuring library ${LIB_NAME}") set(LIB_HEADERS "${PROJECT_SOURCE_DIR}/include") set(LIB_FILES @@ -19,12 +19,14 @@ else () add_library(${LIB_NAME} ${LIB_FILES}) endif () - # Add the include directory target_include_directories(${LIB_NAME} PUBLIC "${LIB_HEADERS}") # link library to proplib_compiler_flags -target_link_libraries(${LIB_NAME} PUBLIC proplib_compiler_flags) +# target_link_libraries(${LIB_NAME} PUBLIC proplib_compiler_flags) + +# Configure compiler options +configure_proplib_target(${LIB_NAME}) # Add definition to get the library name and version inside the library add_compile_definitions( @@ -44,6 +46,10 @@ endif () # Set some target metadata set_target_properties( ${LIB_NAME} PROPERTIES - OUTPUT_NAME ${LIB_NAME}-${PROJECT_VERSION} + OUTPUT_NAME ${LIB_NAME}-${PROJECT_VERSION}_ VERSION ${PROJECT_VERSION} + DEBUG_POSTFIX ${ARCH_SUFFIX} + RELEASE_POSTFIX ${ARCH_SUFFIX} ) + +proplib_message("Done configuring library ${LIB_NAME}") \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1dcc0c0..f701e64 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -2,6 +2,7 @@ ## CONFIGURE UNIT TESTS ############################################ set(TEST_NAME "Test${LIB_NAME}") +proplib_message("Configuring library tests ${TEST_NAME}") ## TODO-TEMPLATE: Include all source AND header files for tests here. ## Do not include the source and header files of the library under test. @@ -16,4 +17,7 @@ add_executable( ########################################### include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) target_link_libraries(${TEST_NAME} ${LIB_NAME} GTest::gtest_main) -gtest_discover_tests(${TEST_NAME}) \ No newline at end of file +include(GoogleTest) +gtest_discover_tests(${TEST_NAME}) + +proplib_message("Done configuring library tests ${TEST_NAME}") \ No newline at end of file From b441333318612f4ea23ec8dfc1b5942721cd22bc Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 17:56:06 -0500 Subject: [PATCH 307/379] fix preset names, add 32-bit step to CI test --- .github/workflows/ctest.yml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index 0ada805..64169d0 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -48,10 +48,20 @@ jobs: with: cmakeVersion: ${{ matrix.cmakeVersion }} - - name: "CMake: Build and Test" + - name: "CMake: Build and Test (64-bit)" + if: matrix.architecture != 'x86' uses: lukka/run-cmake@v10 with: - configurePreset: release + configurePreset: release64 configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF']" - buildPreset: release - testPreset: release + buildPreset: release64 + testPreset: default + + - name: "CMake: Build and Test (32-bit)" + if: matrix.architecture == 'x86' + uses: lukka/run-cmake@v10 + with: + configurePreset: release32 + configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF']" + buildPreset: release32 + testPreset: default From 290d2bc0a3b2be3930a2ecadfef222a462339d22 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 18:58:59 -0500 Subject: [PATCH 308/379] Correct minimum required CMake version 3.15 is required for compiler-configuring generator expressions --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b1cc984..3f19254 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,8 @@ # 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) +# >=3.15 required for CMake features used in this project +cmake_minimum_required(VERSION 3.15 FATAL_ERROR) # If on macOS, handle arm64/x86_64 architectures (must be done before project()) if (APPLE) From 2450e29132677f1a3dfec5d94435aa553d052028 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 19:06:26 -0500 Subject: [PATCH 309/379] compiler-dependent handling of 32-bit compiler options --- CMakeLists.txt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f19254..fb6a238 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,14 @@ if (APPLE) elseif (WIN32) if (CMAKE_GENERATOR_PLATFORM STREQUAL "Win32") set(ARCH_SUFFIX "x86") + if (NOT BUILD_32BIT) + message(WARNING + "Generator platform is Win32 but BUILD_32BIT was not set ON.\n" + "BUILD_32BIT is being turned on based on the generator platform," + "possibly overwriting the preset or specified setting." + ) + set(BUILD_32BIT ON) + endif () else () set(ARCH_SUFFIX "x64") endif () @@ -97,16 +105,17 @@ function(configure_proplib_target proplib_target) "$<${gcc_like_cxx}:$<$:-O3;-DNDEBUG>>" # For GCC-like compilers in Debug configurations "$<${gcc_like_cxx}:$<$:-g;-O0>>" + # For GCC-like compilers in 32-bit configurations + "$<${gcc_like_cxx}:$<$:-m32>>" # For MSVC compiler in any configuration "$<${msvc_cxx}:$>" # For MSVC compiler in Release configurations "$<${msvc_cxx}:$<$:/O2;/DNDEBUG>>" # For MSVC compiler in Debug configurations "$<${msvc_cxx}:$<$:/Od;/Zi>>" + # For MSVC compiler in 32-bit configurations + "$<${msvc_cxx}:$<$:/m32>>" ) - if (CMAKE_GENERATOR_PLATFORM STREQUAL "Win32") - set_target_properties(${proplib_target} PROPERTIES COMPILE_OPTIONS "-m32" LINK_FLAGS "-m32") - endif () endfunction() # target_compile_options(proplib_compiler_flags INTERFACE # # For GCC-like compilers in any configuration From 776cd3c2bf335aa9ebbb12d5228fae8103d4cddb Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 19:08:37 -0500 Subject: [PATCH 310/379] add comment --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fb6a238..bbc34db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ cmake_minimum_required(VERSION 3.15 FATAL_ERROR) # If on macOS, handle arm64/x86_64 architectures (must be done before project()) +# For other OS, identify the architecture and save it to the ARCH_SUFFIX variable. if (APPLE) # Get the current platform's native architecture execute_process( @@ -19,7 +20,6 @@ if (APPLE) OUTPUT_VARIABLE MACOS_NATIVE_ARCHITECTURE OUTPUT_STRIP_TRAILING_WHITESPACE ) - # If running on Apple silicon, try a universal build. Otherwise, a native build. if ((CMAKE_GENERATOR STREQUAL "Xcode") AND (MACOS_NATIVE_ARCHITECTURE STREQUAL "arm64")) set(CMAKE_OSX_ARCHITECTURE "arm64;x86_64" CACHE STRING "") From 8e6997091d6e8012deed8ebe2cbe641726ee6c7b Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 19:12:42 -0500 Subject: [PATCH 311/379] Set C++11 requirements, improve compiler option generators --- CMakeLists.txt | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bbc34db..1ca2f40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,12 +85,13 @@ option(BUILD_32BIT "Build project for x86/32-bit instead of x64/64-bit" OFF) ########################################### ## SETUP ########################################### +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS ON) + # Create an interface to set common compiler flags # add_library(proplib_compiler_flags INTERFACE) -# Require at least C++11 -# target_compile_features(proplib_compiler_flags INTERFACE cxx_std_11) - # add compiler warning flags just when building this project via # the BUILD_INTERFACE generator expression set(gcc_like_cxx "$") @@ -102,19 +103,19 @@ function(configure_proplib_target proplib_target) # For GCC-like compilers in any configuration "$<${gcc_like_cxx}:$>" # For GCC-like compilers in Release configurations - "$<${gcc_like_cxx}:$<$:-O3;-DNDEBUG>>" + "$<${gcc_like_cxx}:$:-O3;-DNDEBUG>>>" # For GCC-like compilers in Debug configurations - "$<${gcc_like_cxx}:$<$:-g;-O0>>" + "$<${gcc_like_cxx}:$:-g;-O0>>>" # For GCC-like compilers in 32-bit configurations - "$<${gcc_like_cxx}:$<$:-m32>>" + "$<${gcc_like_cxx}:$:-m32>>>" # For MSVC compiler in any configuration "$<${msvc_cxx}:$>" # For MSVC compiler in Release configurations - "$<${msvc_cxx}:$<$:/O2;/DNDEBUG>>" + "$<${msvc_cxx}:$:/O2;/DNDEBUG>>>" # For MSVC compiler in Debug configurations - "$<${msvc_cxx}:$<$:/Od;/Zi>>" + "$<${msvc_cxx}:$:/Od;/Zi>>>" # For MSVC compiler in 32-bit configurations - "$<${msvc_cxx}:$<$:/m32>>" + "$<${msvc_cxx}:$:/m32>>>" ) endfunction() # target_compile_options(proplib_compiler_flags INTERFACE From 97a28be7b6cc4767607e68b4305acaee4efb3e5d Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 19:15:44 -0500 Subject: [PATCH 312/379] update comments, remove dead code --- CMakeLists.txt | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ca2f40..b9575a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,20 +85,17 @@ option(BUILD_32BIT "Build project for x86/32-bit instead of x64/64-bit" OFF) ########################################### ## SETUP ########################################### +# C++11 and some extensions (e.g., for `auto`) are the minimum required set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS ON) -# Create an interface to set common compiler flags -# add_library(proplib_compiler_flags INTERFACE) - -# add compiler warning flags just when building this project via -# the BUILD_INTERFACE generator expression +# Get 0/1 values indicating compiler type set(gcc_like_cxx "$") set(msvc_cxx "$") +# Define a function to set compile options of a provided target. function(configure_proplib_target proplib_target) - target_compile_features(${proplib_target} INTERFACE cxx_std_11) # TODO interface here? and below? target_compile_options(${proplib_target} PRIVATE # For GCC-like compilers in any configuration "$<${gcc_like_cxx}:$>" @@ -118,25 +115,6 @@ function(configure_proplib_target proplib_target) "$<${msvc_cxx}:$:/m32>>>" ) endfunction() -# target_compile_options(proplib_compiler_flags INTERFACE -# # For GCC-like compilers in any configuration -# "$<${gcc_like_cxx}:$>" -# # For GCC-like compilers in Release configurations -# "$<${gcc_like_cxx}:$<$:-O3;-DNDEBUG>>" -# # For GCC-like compilers in Debug configurations -# "$<${gcc_like_cxx}:$<$:-g;-O0>>" -# # For MSVC compiler in any configuration -# "$<${msvc_cxx}:$>" -# # For MSVC compiler in Release configurations -# "$<${msvc_cxx}:$<$:/O2;/DNDEBUG>>" -# # For MSVC compiler in Debug configurations -# "$<${msvc_cxx}:$<$:/Od;/Zi>>" -# ) - -# Force 32-bit compile when configured -# if (CMAKE_GENERATOR_PLATFORM STREQUAL "Win32") -# set_target_properties(proplib_compiler_flags PROPERTIES COMPILE_OPTIONS "-m32" LINK_FLAGS "-m32") -# endif () # Enable Hot Reload for MSVC compilers if supported. if (POLICY CMP0141) From 4fb9e6b48b8bcb9804b3a58a24da90f4c6e6be3e Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 19:17:18 -0500 Subject: [PATCH 313/379] Add CTest presets for CI testing --- .github/workflows/ctest.yml | 4 +-- CMakePresets.json | 49 +++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index 64169d0..aed9004 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -55,7 +55,7 @@ jobs: configurePreset: release64 configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF']" buildPreset: release64 - testPreset: default + testPreset: release64 - name: "CMake: Build and Test (32-bit)" if: matrix.architecture == 'x86' @@ -64,4 +64,4 @@ jobs: configurePreset: release32 configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF']" buildPreset: release32 - testPreset: default + testPreset: release32 diff --git a/CMakePresets.json b/CMakePresets.json index 3ed7cf1..361d507 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -150,5 +150,54 @@ } ], "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 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 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 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 library and tests with release options, and build docs", + "configurePreset": "release32" + } ] } \ No newline at end of file From 9fdce23a06f8a7ae9119bd08d8e7447630f9cd35 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 19:30:46 -0500 Subject: [PATCH 314/379] avoid CMAKE_GENERATOR_PLATFORM in 32-bit presets --- CMakeLists.txt | 10 +--------- CMakePresets.json | 2 -- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b9575a9..fd1d9dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,16 +28,8 @@ if (APPLE) set(ARCH_SUFFIX MACOS_NATIVE_ARCHITECTURE) endif () elseif (WIN32) - if (CMAKE_GENERATOR_PLATFORM STREQUAL "Win32") + if (BUILD_32BIT) set(ARCH_SUFFIX "x86") - if (NOT BUILD_32BIT) - message(WARNING - "Generator platform is Win32 but BUILD_32BIT was not set ON.\n" - "BUILD_32BIT is being turned on based on the generator platform," - "possibly overwriting the preset or specified setting." - ) - set(BUILD_32BIT ON) - endif () else () set(ARCH_SUFFIX "x64") endif () diff --git a/CMakePresets.json b/CMakePresets.json index 361d507..ccf0c4d 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -63,7 +63,6 @@ "description": "Build 32-bit library and tests with debug options. Skip building driver, driver tests, and docs.", "inherits": "proplib-config-debug-base", "cacheVariables": { - "CMAKE_GENERATOR_PLATFORM": "Win32", "BUILD_32BIT": "ON" } }, @@ -73,7 +72,6 @@ "description": "Build 32-bit library, driver, docs and tests with release options.", "inherits": "proplib-config-release-base", "cacheVariables": { - "CMAKE_GENERATOR_PLATFORM": "Win32", "BUILD_32BIT": "ON" } }, From a212ad2a676e9a1a1c7f2eda3c3883066858c0a9 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 19:37:09 -0500 Subject: [PATCH 315/379] Manually format datetime to avoid compiler warning --- app/src/DriverUtils.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/DriverUtils.cpp b/app/src/DriverUtils.cpp index 7071037..fe9f69b 100644 --- a/app/src/DriverUtils.cpp +++ b/app/src/DriverUtils.cpp @@ -39,7 +39,8 @@ std::string GetDatetimeString() { } #endif char mbstr[100]; - if (std::strftime(mbstr, sizeof(mbstr), "%c", &localTime) == 0) { + if (std::strftime(mbstr, sizeof(mbstr), "%a %b %d %H:%M:%S %Y", &localTime) + == 0) { return "Could not format datetime string"; } return std::string(mbstr); From b6baf82bc076881b3b3c2b8862a46cb236cc7daa Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 19:39:49 -0500 Subject: [PATCH 316/379] Fix missing variable indicators --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fd1d9dd..e976b97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,11 +21,11 @@ if (APPLE) OUTPUT_STRIP_TRAILING_WHITESPACE ) # If running on Apple silicon, try a universal build. Otherwise, a native build. - if ((CMAKE_GENERATOR STREQUAL "Xcode") AND (MACOS_NATIVE_ARCHITECTURE STREQUAL "arm64")) + if ((${CMAKE_GENERATOR} STREQUAL "Xcode") AND (${MACOS_NATIVE_ARCHITECTURE} STREQUAL "arm64")) set(CMAKE_OSX_ARCHITECTURE "arm64;x86_64" CACHE STRING "") set(ARCH_SUFFIX "universal") else () - set(ARCH_SUFFIX MACOS_NATIVE_ARCHITECTURE) + set(ARCH_SUFFIX ${MACOS_NATIVE_ARCHITECTURE}) endif () elseif (WIN32) if (BUILD_32BIT) From 7aabe1d3d5c97c4ad76477ad79173ea5c2b1a654 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 19:44:06 -0500 Subject: [PATCH 317/379] Attempt allowing universal macOS builds with Ninja generator --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e976b97..34b373a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ if (APPLE) OUTPUT_STRIP_TRAILING_WHITESPACE ) # If running on Apple silicon, try a universal build. Otherwise, a native build. - if ((${CMAKE_GENERATOR} STREQUAL "Xcode") AND (${MACOS_NATIVE_ARCHITECTURE} STREQUAL "arm64")) + if (${MACOS_NATIVE_ARCHITECTURE} STREQUAL "arm64") set(CMAKE_OSX_ARCHITECTURE "arm64;x86_64" CACHE STRING "") set(ARCH_SUFFIX "universal") else () From 2f57f74ada836cfdd79099e0716b16205b3135f6 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 19:56:26 -0500 Subject: [PATCH 318/379] update release/debug handling, set generator platform on windows --- CMakeLists.txt | 13 +++++++++++-- CMakePresets.json | 6 ++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 34b373a..3dd4f14 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,14 +29,25 @@ if (APPLE) endif () elseif (WIN32) if (BUILD_32BIT) + set(CMAKE_GENERATOR_PLATFORM "Win32") set(ARCH_SUFFIX "x86") else () + set(CMAKE_GENERATOR_PLATFORM "x64") set(ARCH_SUFFIX "x64") endif () elseif (UNIX) # Only non-Apple Linux evaluates as True here set(ARCH_SUFFIX "x86_64") endif () +# Set configuration type (different for single- or multi-config generators) +if (${PROPLIB_CONFIG_TYPE} STREQUAL "Release") + set(CMAKE_CONFIGURATION_TYPES "Release") + set(CMAKE_BUILD_TYPE "Release") +elseif (${PROPLIB_CONFIG_TYPE} STREQUAL "Debug") + set(CMAKE_CONFIGURATION_TYPES "Debug") + set(CMAKE_BUILD_TYPE "Debug") +endif () + # Convenience function for printing visible status messages. # Accepts arbitrary number of string inputs, each printed as a # new line in the status message. @@ -103,8 +114,6 @@ function(configure_proplib_target proplib_target) "$<${msvc_cxx}:$:/O2;/DNDEBUG>>>" # For MSVC compiler in Debug configurations "$<${msvc_cxx}:$:/Od;/Zi>>>" - # For MSVC compiler in 32-bit configurations - "$<${msvc_cxx}:$:/m32>>>" ) endfunction() diff --git a/CMakePresets.json b/CMakePresets.json index ccf0c4d..374dac6 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -25,8 +25,7 @@ "inherits": "proplib-config-base", "description": "Base 'Release' configuration preset for ITS PropLib libraries", "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release", - "CMAKE_CONFIGURATION_TYPES": "Release", + "PROPLIB_CONFIG_TYPE": "Release", "BUILD_DOCS": "ON", "BUILD_DRIVER": "ON", "RUN_DRIVER_TESTS": "ON" @@ -38,8 +37,7 @@ "inherits": "proplib-config-base", "description": "Base 'Debug' configuration preset for ITS PropLib libraries", "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug", - "CMAKE_CONFIGURATION_TYPES": "Debug", + "PROPLIB_CONFIG_TYPE": "Debug", "BUILD_DOCS": "OFF", "BUILD_DRIVER": "OFF", "RUN_DRIVER_TESTS": "OFF" From 941970f0849dfda813c392869836c3d754ab0ca9 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 20:01:53 -0500 Subject: [PATCH 319/379] Skip expectation of proplib_config_type for docsOnly --- CMakeLists.txt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3dd4f14..81d53bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,12 +40,14 @@ elseif (UNIX) # Only non-Apple Linux evaluates as True here endif () # Set configuration type (different for single- or multi-config generators) -if (${PROPLIB_CONFIG_TYPE} STREQUAL "Release") - set(CMAKE_CONFIGURATION_TYPES "Release") - set(CMAKE_BUILD_TYPE "Release") -elseif (${PROPLIB_CONFIG_TYPE} STREQUAL "Debug") - set(CMAKE_CONFIGURATION_TYPES "Debug") - set(CMAKE_BUILD_TYPE "Debug") +if (DEFINED PROPLIB_CONFIG_TYPE) + if (${PROPLIB_CONFIG_TYPE} STREQUAL "Release") + set(CMAKE_CONFIGURATION_TYPES "Release") + set(CMAKE_BUILD_TYPE "Release") + elseif (${PROPLIB_CONFIG_TYPE} STREQUAL "Debug") + set(CMAKE_CONFIGURATION_TYPES "Debug") + set(CMAKE_BUILD_TYPE "Debug") + endif () endif () # Convenience function for printing visible status messages. From 50d174e5cfc0f16203b33454dddead5ecd78b0be Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 20:04:46 -0500 Subject: [PATCH 320/379] Fix 32-bit builds from 64-bit windows --- CMakeLists.txt | 13 ------------- CMakePresets.json | 6 ++++-- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 81d53bc..d07e842 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,27 +29,14 @@ if (APPLE) endif () elseif (WIN32) if (BUILD_32BIT) - set(CMAKE_GENERATOR_PLATFORM "Win32") set(ARCH_SUFFIX "x86") else () - set(CMAKE_GENERATOR_PLATFORM "x64") set(ARCH_SUFFIX "x64") endif () elseif (UNIX) # Only non-Apple Linux evaluates as True here set(ARCH_SUFFIX "x86_64") endif () -# Set configuration type (different for single- or multi-config generators) -if (DEFINED PROPLIB_CONFIG_TYPE) - if (${PROPLIB_CONFIG_TYPE} STREQUAL "Release") - set(CMAKE_CONFIGURATION_TYPES "Release") - set(CMAKE_BUILD_TYPE "Release") - elseif (${PROPLIB_CONFIG_TYPE} STREQUAL "Debug") - set(CMAKE_CONFIGURATION_TYPES "Debug") - set(CMAKE_BUILD_TYPE "Debug") - endif () -endif () - # Convenience function for printing visible status messages. # Accepts arbitrary number of string inputs, each printed as a # new line in the status message. diff --git a/CMakePresets.json b/CMakePresets.json index 374dac6..ccf0c4d 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -25,7 +25,8 @@ "inherits": "proplib-config-base", "description": "Base 'Release' configuration preset for ITS PropLib libraries", "cacheVariables": { - "PROPLIB_CONFIG_TYPE": "Release", + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_CONFIGURATION_TYPES": "Release", "BUILD_DOCS": "ON", "BUILD_DRIVER": "ON", "RUN_DRIVER_TESTS": "ON" @@ -37,7 +38,8 @@ "inherits": "proplib-config-base", "description": "Base 'Debug' configuration preset for ITS PropLib libraries", "cacheVariables": { - "PROPLIB_CONFIG_TYPE": "Debug", + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_CONFIGURATION_TYPES": "Debug", "BUILD_DOCS": "OFF", "BUILD_DRIVER": "OFF", "RUN_DRIVER_TESTS": "OFF" From 25f603ef9e1f40d263b058998aacd5167d112713 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 20:19:54 -0500 Subject: [PATCH 321/379] Minimize release action matrix; add 32-bit config --- .github/workflows/release.yml | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d7fbda4..a65b39c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,20 +15,15 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: - - ubuntu-latest - - macos-latest - - windows-latest - - windows-2019 # Used for Windows 32-bit builds - architecture: [arm64, x64] - cmakeVersion: ["3.21", latest] # CMake >= 3.21 is required to use "--preset " and discover generators - exclude: # Only use x64 strategies for Linux and Windows + include: - os: windows-latest - architecture: arm64 - - os: windows-2019 + architecture: x64 + - os: windows-latest + architecture: x86 + - os: macos-latest architecture: arm64 - os: ubuntu-latest - architecture: arm64 + architecture: x64 steps: - name: Checkout repository uses: actions/checkout@v4 @@ -36,12 +31,21 @@ jobs: - name: Install CMake # (latest stable version) uses: lukka/get-cmake@latest - - name: "CMake: Build" + - name: "CMake: Build (64-bit)" + if: matrix.architecture != 'x86' + uses: lukka/run-cmake@v10 + with: + configurePreset: release64 + configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF', '-DRUN_TESTS=OFF', '-DRUN_DRIVER_TESTS=OFF']" + buildPreset: release64 + + - name: "CMake: Build (32-bit)" + if: matrix.architecture == 'x86' uses: lukka/run-cmake@v10 with: - configurePreset: release + configurePreset: release32 configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF', '-DRUN_TESTS=OFF', '-DRUN_DRIVER_TESTS=OFF']" - buildPreset: release + buildPreset: release32 - name: Upload release artifact (macOS or Linux) if: runner.os != 'Windows' From 24ae82d22b9cdd8188d51cfad913e3b420aabacb Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 20:22:18 -0500 Subject: [PATCH 322/379] Add macos-13 intel runner to matrix --- .github/workflows/ctest.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index aed9004..7e060b3 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -21,13 +21,20 @@ jobs: matrix: os: - ubuntu-latest - - macos-latest + - macos-latest # Apple + - macos-13 # Intel - windows-latest architecture: [arm64, x64, x86] cmakeVersion: ["3.21", latest] # CMake >= 3.21 is required to use "--preset " and discover generators exclude: - os: macos-latest architecture: x86 + - os: macos-latest + architecture: x64 + - os: macos-13 + architecture: x86 + - os: macos-13 + architecture: arm64 - os: windows-latest architecture: arm64 - os: ubuntu-latest From 328669dd89a7aecb3a35315b4770ff62381bf0a2 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 20:30:13 -0500 Subject: [PATCH 323/379] Adjust name for action run, add workflow_dispatch --- .github/workflows/release.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a65b39c..58eec73 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,25 +5,30 @@ name: Create Release Artifacts on: push: tags: ['v[0-9]+.*'] + workflow_dispatch: permissions: contents: write jobs: create_release_artifacts: - name: Create release artifacts + name: 'Create Release: ${{ matrix.relName}}' runs-on: ${{ matrix.os }} strategy: matrix: include: - os: windows-latest architecture: x64 + relName: Windows x64 - os: windows-latest architecture: x86 + relName: Windows x86 - os: macos-latest architecture: arm64 + relName: macOS Universal - os: ubuntu-latest architecture: x64 + relName: Linux x64 steps: - name: Checkout repository uses: actions/checkout@v4 From a3b81b1345bab47f17e7f8d456d8eaf8a04dfd66 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 11 Dec 2024 20:35:32 -0500 Subject: [PATCH 324/379] Attempt static linking for g++ compiling --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d07e842..c8fcb02 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,7 +90,7 @@ set(msvc_cxx "$") function(configure_proplib_target proplib_target) target_compile_options(${proplib_target} PRIVATE # For GCC-like compilers in any configuration - "$<${gcc_like_cxx}:$>" + "$<${gcc_like_cxx}:$>" # For GCC-like compilers in Release configurations "$<${gcc_like_cxx}:$:-O3;-DNDEBUG>>>" # For GCC-like compilers in Debug configurations @@ -100,9 +100,9 @@ function(configure_proplib_target proplib_target) # For MSVC compiler in any configuration "$<${msvc_cxx}:$>" # For MSVC compiler in Release configurations - "$<${msvc_cxx}:$:/O2;/DNDEBUG>>>" + "$<${msvc_cxx}:$:/O2;/DNDEBUG;/MT>>>" # For MSVC compiler in Debug configurations - "$<${msvc_cxx}:$:/Od;/Zi>>>" + "$<${msvc_cxx}:$:/Od;/Zi;/MTd>>>" ) endfunction() From 1f532fb3af84e4549013a3e404b05552cda2dfe2 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:03:37 -0500 Subject: [PATCH 325/379] editorial review updates --- GitHubRepoPublicReleaseApproval.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/GitHubRepoPublicReleaseApproval.md b/GitHubRepoPublicReleaseApproval.md index 85235a5..d435f83 100644 --- a/GitHubRepoPublicReleaseApproval.md +++ b/GitHubRepoPublicReleaseApproval.md @@ -18,14 +18,14 @@ mark next to each attests that the criterion has been met. * [ ] The repository includes the appropriate `LICENSE.md` file 2. [ ] Any test data necessary for the code and its unit tests to function is included in this GitHub repository, either directly or as a linked Git submodule. -3. [ ] The README.md file has passed editorial review from the ITS Publications Office. +3. [ ] The README.md file has passed editorial review by the ITS Publications Office. 4. [ ] 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. [ ] 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 +your version of this Markdown document to that branch, and then create a pull request +for that branch. The following must log in to GitHub and approve that pull request before the pull request can be merged and this repo made public: * Project Lead: TODO-TEMPLATE From b2c0cdfc389758271a9b403fadf7df4da4c29fe2 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:14:52 -0500 Subject: [PATCH 326/379] Rename targets to group alphabetically Groups all defined targets when listed, e.g., in VS solution --- app/CMakeLists.txt | 2 +- docs/CMakeLists.txt | 2 +- tests/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index cf55806..388d4cc 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -4,7 +4,7 @@ set(DRIVER_NAME "${LIB_NAME}Driver") set(DRIVER_VERSION ${PROJECT_VERSION}.0) set(DRIVER_HEADERS "${PROJECT_SOURCE_DIR}/app/include") -set(DRIVER_TEST_NAME "Test${DRIVER_NAME}") +set(DRIVER_TEST_NAME "${DRIVER_NAME}Test") ########################################### ## BUILD THE COMMAND LINE DRIVER diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index c300778..aeb6125 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -57,7 +57,7 @@ set(DOXYGEN_EXTRACT_PRIVATE "YES") set(DOXYGEN_INTERNAL_DOCS "YES") doxygen_add_docs( - "${CMAKE_PROJECT_NAME}-Docs" + "${LIB_NAME}Docs" "${PROJECT_SOURCE_DIR}/app/src" "${PROJECT_SOURCE_DIR}/app/include" "${PROJECT_SOURCE_DIR}/src" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f701e64..0fcd455 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,7 +1,7 @@ ############################################ ## CONFIGURE UNIT TESTS ############################################ -set(TEST_NAME "Test${LIB_NAME}") +set(TEST_NAME "${LIB_NAME}Test") proplib_message("Configuring library tests ${TEST_NAME}") ## TODO-TEMPLATE: Include all source AND header files for tests here. From b4cd86026e665d185550eae1ea4f6d8228dfce27 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:58:38 -0500 Subject: [PATCH 327/379] Correct use of environment variables, suppresses some CMake warnings --- CMakePresets.json | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index ccf0c4d..6973e97 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -12,10 +12,8 @@ "description": "Base configuration preset for ITS PropLib libraries", "binaryDir": "${sourceDir}/build/${presetName}", "cacheVariables": { - "CMAKE_BUILD_RPATH_USE_ORIGIN": "ON", "DOCS_ONLY": "OFF", "RUN_TESTS": "ON", - "BUILD_SHARED_LIBS": "ON", "BUILD_32BIT": "OFF" } }, @@ -25,11 +23,15 @@ "inherits": "proplib-config-base", "description": "Base 'Release' configuration preset for ITS PropLib libraries", "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release", - "CMAKE_CONFIGURATION_TYPES": "Release", + "CMAKE_BUILD_RPATH_USE_ORIGIN": "ON", + "BUILD_SHARED_LIBS": "ON", "BUILD_DOCS": "ON", "BUILD_DRIVER": "ON", "RUN_DRIVER_TESTS": "ON" + }, + "environment": { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_CONFIGURATION_TYPES": "Release" } }, { @@ -38,11 +40,15 @@ "inherits": "proplib-config-base", "description": "Base 'Debug' configuration preset for ITS PropLib libraries", "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug", - "CMAKE_CONFIGURATION_TYPES": "Debug", + "CMAKE_BUILD_RPATH_USE_ORIGIN": "ON", + "BUILD_SHARED_LIBS": "ON", "BUILD_DOCS": "OFF", "BUILD_DRIVER": "OFF", "RUN_DRIVER_TESTS": "OFF" + }, + "environment": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_CONFIGURATION_TYPES": "Debug" } }, { From dd6e305f4b6c51d26d91147b5f95f4f06cf4cecc Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:01:41 -0500 Subject: [PATCH 328/379] ignore CMakeUserPresets.json --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 738bc1d..d58f1f8 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ Thumbs.db ######### CMakeLists.txt.user CMakeCache.txt +CMakeUserPreset.json CMakeFiles CMakeScripts Testing From d040cf70638f4609a51005c7eef5f1a8d0600ab7 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:53:54 -0500 Subject: [PATCH 329/379] Ignore CMakeUserPresets.json --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d58f1f8..5fed29b 100644 --- a/.gitignore +++ b/.gitignore @@ -49,7 +49,7 @@ Thumbs.db ######### CMakeLists.txt.user CMakeCache.txt -CMakeUserPreset.json +CMakeUserPresets.json CMakeFiles CMakeScripts Testing From f832f11a3fa6d4fa57a689f4dcfad0a4fce07f58 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:58:41 -0500 Subject: [PATCH 330/379] debug release action --- .github/workflows/release.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 58eec73..bb5a287 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -64,12 +64,16 @@ jobs: ${{ github.workspace }}/bin/*Driver-Linux-* if-no-files-found: error overwrite: true + + - name: Check macOS file type + if: runner.os != 'macOS' + run: file ${{ github.workspace }}/bin/*.dylib - name: Upload release artifact (Windows) if: runner.os == 'Windows' uses: actions/upload-artifact@v4 with: - name: release-${{ matrix.os }} + name: release-${{ matrix.os }}-${{ matrix.architecture }} path: | ${{ github.workspace }}\bin\Release\*.dll ${{ github.workspace }}\bin\Release\*Driver-Windows-*.exe From ae933ef1ccc0822ee6f7464cdefe9f9f0ce0cb08 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:37:04 -0500 Subject: [PATCH 331/379] fix condition in debug --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bb5a287..33642a1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -66,7 +66,7 @@ jobs: overwrite: true - name: Check macOS file type - if: runner.os != 'macOS' + if: runner.os == 'macOS' run: file ${{ github.workspace }}/bin/*.dylib - name: Upload release artifact (Windows) From 4b59f178b02bc300bdc6eff0ce47f83bd4e5cce5 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 12 Dec 2024 16:36:43 -0500 Subject: [PATCH 332/379] fix typo, add info to status message --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c8fcb02..0234cae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ if (APPLE) ) # If running on Apple silicon, try a universal build. Otherwise, a native build. if (${MACOS_NATIVE_ARCHITECTURE} STREQUAL "arm64") - set(CMAKE_OSX_ARCHITECTURE "arm64;x86_64" CACHE STRING "") + set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "") set(ARCH_SUFFIX "universal") else () set(ARCH_SUFFIX ${MACOS_NATIVE_ARCHITECTURE}) @@ -119,6 +119,9 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" CACHE STRING "Set proplib_message( "Initial Configuration Complete" + "Generator platform: ${CMAKE_GENERATOR_PLATFORM}" + "C++ Compiler: ${CMAKE_CXX_COMPILER}" + "CMake Build Type: ${CMAKE_BUILD_TYPE}" "Target architecture: ${ARCH_SUFFIX}" "CMake Options:" " BUILD_32BIT = ${BUILD_32BIT}" From 07798e4e6aa7fdb9b836d91b40e8252c49130582 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 12 Dec 2024 17:04:16 -0500 Subject: [PATCH 333/379] Fix CITATION.cff validator for Ubuntu 24.04 runners --- .github/workflows/cff-validator.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/cff-validator.yml b/.github/workflows/cff-validator.yml index 83d75d0..6fce818 100644 --- a/.github/workflows/cff-validator.yml +++ b/.github/workflows/cff-validator.yml @@ -23,3 +23,5 @@ jobs: uses: actions/checkout@v4 - name: Validate CITATION.cff uses: dieghernan/cff-validator@v3 + with: + install-r: true From 645b226e18d239584e94d1d7e30d73ab6ff3dd69 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 18 Dec 2024 09:58:15 -0500 Subject: [PATCH 334/379] Remove command-line driver README, link to wiki --- README.md | 3 ++- app/README.md | 67 --------------------------------------------------- 2 files changed, 2 insertions(+), 68 deletions(-) delete mode 100644 app/README.md diff --git a/README.md b/README.md index c78b15e..affa347 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,8 @@ examples for all supported languages. An executable is also provided which can be used to run the functions provided by this library using plain text input and output files. Installation and usage -details for the command-line driver are provided in [its own README](./app/README.md). +details for the command-line driver are also provided on +[the wiki](https://ntia.github.io/propagation-library-wiki/models/TODO-TEMPLATE/driver). If you're a developer and would like to contribute to or extend this repository, you will find comprehensive documentation of this C++ code diff --git a/app/README.md b/app/README.md deleted file mode 100644 index e314a4e..0000000 --- a/app/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# Command-line Driver # - -This document explains the use of the included command-line driver. This is a -supplemental software tool to allow a user to call the compiled propagation library -from the command-line using text files to provide inputs and store outputs. - -## Input Files ## - -Inputs to the command-line driver are specified in an ASCII text file using -the common `key,value` format. Each line holds a single `key,value` combination, -with the `key` representing the model input variable name and the `value` representing -its value. - -## Output Files ## - -After parsing the inputs and running the software, the command-line driver will -generate an output report file containing the results. This file contains a record -of the input parameters, along with the model outputs, and human-readable supporting -documentation. - -## Execution ## - -Executing the command-line driver requires specifying input arguments, defined -in the below table: - -(TODO-TEMPLATE review and update the flags below) - -| Flag | Type | Required | Description | -|--------|--------|----------|-----------------------------------------------------------------------| -| `-i` | string | true | File specifying model input parameters in `key,value` format | -| `-o` | string | true | Filename where output results should be written | -| `-dbg` | N/A | false | If specified, intermediate values will be written to the output file | - -Additional arguments are available to print help text and version information: - -| Flag | Description | -|------|--------------------------------------------------------| -| `-h` | Display help text | -| `-v` | Display version information for the library and driver | - -Input arguments are not case sensitive and do not have to be specified in a certain -order. A generic example of calling the command-line driver on Windows is: - -(TODO-TEMPLATE: update example driver command below) - -```cmd -.exe -i -dbg -o -``` - -### Examples ### - -The following files are included as references for the functionality of this software. -Using these input files and the commands specified should produce outputs identical -to the provided corresponding output files. - -(TODO-TEMPLATE: Provide all included examples in the table below) - -| Input File | Terrain File | Output File | Arguments | -|---------------------------|-----------------------------------------------------|-----------------------------|----------------------------------------------------| -| [`in.txt`](./data/in.txt) | [`terrain_profile.txt`](./data/terrain_profile.txt) | [`out.txt`](./data/out.txt) | `-i in.txt -t terrain_profile.txt -o out.txt -dbg` | - -## Command-line Driver Errors ## - -In addition to the return codes defined by the library itself, the command-line -driver implements its own set of return codes in [`app/include/ReturnCodes.h`](./include/ReturnCodes.h). -A helper function to map human-readable status messages to these codes in -[`app/src/ReturnCodes.cpp`](./src/ReturnCodes.cpp). From 7636eaa44ecaae7c7787ce0c9d3b1627b8d0be02 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 18 Dec 2024 09:58:28 -0500 Subject: [PATCH 335/379] Delete app/data folder Examples will instead be provided via the wiki --- app/data/README.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 app/data/README.md diff --git a/app/data/README.md b/app/data/README.md deleted file mode 100644 index 329f20e..0000000 --- a/app/data/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# TODO-TEMPLATE - -Populate this folder with example input and output files for -use with the command-line driver. After populating this folder, -delete this README file. From 219c717fbaa0bb3be2016b018d781f67fcfee3db Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 18 Dec 2024 10:26:00 -0500 Subject: [PATCH 336/379] Remove keyword causing misleading zenodo content --- .zenodo.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.zenodo.json b/.zenodo.json index 59d66ac..c3c6f74 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -10,7 +10,6 @@ } ], "description": "TODO-TEMPLATE. Make this the same as the abstract.", - "access_right": "open", "keywords": [ "TODO-TEMPLATE", "TODO-TEMPLATE" From 127fb2077893349575e079eaa6cbfacbb6f38af5 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 18 Dec 2024 16:44:12 -0500 Subject: [PATCH 337/379] attempt fixing universal macos build --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0234cae..68cf742 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ if (APPLE) ) # If running on Apple silicon, try a universal build. Otherwise, a native build. if (${MACOS_NATIVE_ARCHITECTURE} STREQUAL "arm64") - set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "") + set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE) set(ARCH_SUFFIX "universal") else () set(ARCH_SUFFIX ${MACOS_NATIVE_ARCHITECTURE}) From 1ebc864a6a7b9250a33e7d03f4c646d4787f1a25 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 18 Dec 2024 16:54:10 -0500 Subject: [PATCH 338/379] Remove macOS debugging step --- .github/workflows/release.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 33642a1..95d36ae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,10 +65,6 @@ jobs: if-no-files-found: error overwrite: true - - name: Check macOS file type - if: runner.os == 'macOS' - run: file ${{ github.workspace }}/bin/*.dylib - - name: Upload release artifact (Windows) if: runner.os == 'Windows' uses: actions/upload-artifact@v4 From 28b9098124859070f40b4f5a8f46c9db730989ce Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 18 Dec 2024 16:54:32 -0500 Subject: [PATCH 339/379] Consistent naming of binaries --- app/src/CMakeLists.txt | 2 +- src/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index ef330eb..b839aa9 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -34,7 +34,7 @@ add_compile_definitions( # Set some target metadata set_target_properties( ${DRIVER_NAME} PROPERTIES - OUTPUT_NAME ${DRIVER_NAME}-${DRIVER_VERSION}-${CMAKE_SYSTEM_NAME}_ + OUTPUT_NAME ${DRIVER_NAME}-${DRIVER_VERSION}-${CMAKE_SYSTEM_NAME}- DEBUG_POSTFIX ${ARCH_SUFFIX} RELEASE_POSTFIX ${ARCH_SUFFIX} ) \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c681ee6..33976f3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -46,7 +46,7 @@ endif () # Set some target metadata set_target_properties( ${LIB_NAME} PROPERTIES - OUTPUT_NAME ${LIB_NAME}-${PROJECT_VERSION}_ + OUTPUT_NAME ${LIB_NAME}-${PROJECT_VERSION}- VERSION ${PROJECT_VERSION} DEBUG_POSTFIX ${ARCH_SUFFIX} RELEASE_POSTFIX ${ARCH_SUFFIX} From bed1684992e5f3df000bfe5f1d5cbd69910bbcf9 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 18 Dec 2024 16:57:59 -0500 Subject: [PATCH 340/379] cleanup output artifact names --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 95d36ae..cde7f87 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -69,7 +69,7 @@ jobs: if: runner.os == 'Windows' uses: actions/upload-artifact@v4 with: - name: release-${{ matrix.os }}-${{ matrix.architecture }} + name: "${{ github.repository }} Release: ${{ runner.os }} ${{ matrix.architecture }}" path: | ${{ github.workspace }}\bin\Release\*.dll ${{ github.workspace }}\bin\Release\*Driver-Windows-*.exe From 8bace54d77661d336c9236d8b93827452b32ddf0 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 18 Dec 2024 17:01:44 -0500 Subject: [PATCH 341/379] Add C++ header upload to release action --- .github/workflows/release.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cde7f87..53c0448 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,7 +56,7 @@ jobs: if: runner.os != 'Windows' uses: actions/upload-artifact@v4 with: - name: release-${{ matrix.os }} + name: "${{ github.repository }} Release: ${{ runner.os }} ${{ matrix.architecture }}" path: | ${{ github.workspace }}/bin/*.dylib ${{ github.workspace }}/bin/*.so @@ -75,3 +75,12 @@ jobs: ${{ github.workspace }}\bin\Release\*Driver-Windows-*.exe if-no-files-found: error overwrite: true + + - name: Upload release artifact (C++ Header) # TODO-TEMPLATE: Specify the correct header below + if: runner.os == 'Linux' + uses: actions/upload-artifact@v4 + with: + name: "${{ github.repository }} Release: C++ Library Header" + path: ${{ github.workspace }}/include/PropLibTemplate.h + if-no-files-found: error + overwrite: true From 0f823529b0c7c99463122c49e1874c93eb254cfa Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 18 Dec 2024 17:08:20 -0500 Subject: [PATCH 342/379] combine platforms into one step --- .github/workflows/release.yml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 53c0448..ed77701 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -52,7 +52,7 @@ jobs: configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF', '-DRUN_TESTS=OFF', '-DRUN_DRIVER_TESTS=OFF']" buildPreset: release32 - - name: Upload release artifact (macOS or Linux) + - name: Upload release artifacts (binaries) if: runner.os != 'Windows' uses: actions/upload-artifact@v4 with: @@ -62,20 +62,11 @@ jobs: ${{ github.workspace }}/bin/*.so ${{ github.workspace }}/bin/*Driver-Darwin-* ${{ github.workspace }}/bin/*Driver-Linux-* - if-no-files-found: error - overwrite: true - - - name: Upload release artifact (Windows) - if: runner.os == 'Windows' - uses: actions/upload-artifact@v4 - with: - name: "${{ github.repository }} Release: ${{ runner.os }} ${{ matrix.architecture }}" - path: | ${{ github.workspace }}\bin\Release\*.dll ${{ github.workspace }}\bin\Release\*Driver-Windows-*.exe if-no-files-found: error overwrite: true - + - name: Upload release artifact (C++ Header) # TODO-TEMPLATE: Specify the correct header below if: runner.os == 'Linux' uses: actions/upload-artifact@v4 From e9a4259dd2aa73da276fbff19422e50e7ea06882 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 18 Dec 2024 17:12:14 -0500 Subject: [PATCH 343/379] valid artifact names --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ed77701..f0f0f9c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,7 +56,7 @@ jobs: if: runner.os != 'Windows' uses: actions/upload-artifact@v4 with: - name: "${{ github.repository }} Release: ${{ runner.os }} ${{ matrix.architecture }}" + name: "${{ github.event.repository.name }}-release-${{ runner.os }}-${{ matrix.architecture }}" path: | ${{ github.workspace }}/bin/*.dylib ${{ github.workspace }}/bin/*.so @@ -71,7 +71,7 @@ jobs: if: runner.os == 'Linux' uses: actions/upload-artifact@v4 with: - name: "${{ github.repository }} Release: C++ Library Header" + name: "${{ github.event.repository.name }}-release-cpp-header" path: ${{ github.workspace }}/include/PropLibTemplate.h if-no-files-found: error overwrite: true From 2343fb31d2eb56b3d5c8eea18527605c0db316a6 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 18 Dec 2024 17:18:04 -0500 Subject: [PATCH 344/379] debug multiplatform artifact creation --- .github/workflows/release.yml | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f0f0f9c..02aef23 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,7 +26,7 @@ jobs: - os: macos-latest architecture: arm64 relName: macOS Universal - - os: ubuntu-latest + - os: ubuntu-24.04 architecture: x64 relName: Linux x64 steps: @@ -55,15 +55,20 @@ jobs: - name: Upload release artifacts (binaries) if: runner.os != 'Windows' uses: actions/upload-artifact@v4 + with: + name: "${{ github.event.repository.name }}-release-${{ runner.os }}" + path: ${{ github.workspace }}/bin/* + if-no-files-found: error + overwrite: true + + - name: Upload release artifact (Windows) + if: runner.os == 'Windows' + uses: actions/upload-artifact@v4 with: name: "${{ github.event.repository.name }}-release-${{ runner.os }}-${{ matrix.architecture }}" path: | - ${{ github.workspace }}/bin/*.dylib - ${{ github.workspace }}/bin/*.so - ${{ github.workspace }}/bin/*Driver-Darwin-* - ${{ github.workspace }}/bin/*Driver-Linux-* ${{ github.workspace }}\bin\Release\*.dll - ${{ github.workspace }}\bin\Release\*Driver-Windows-*.exe + ${{ github.workspace }}\bin\Release\*Driver-*.exe if-no-files-found: error overwrite: true From 72f04b2296d639c3a692f805b0d0e22275479d78 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Wed, 18 Dec 2024 17:25:01 -0500 Subject: [PATCH 345/379] Fixed macos and linux file names --- .github/workflows/release.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 02aef23..61fa85e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,7 +26,7 @@ jobs: - os: macos-latest architecture: arm64 relName: macOS Universal - - os: ubuntu-24.04 + - os: ubuntu-latest architecture: x64 relName: Linux x64 steps: @@ -57,7 +57,11 @@ jobs: uses: actions/upload-artifact@v4 with: name: "${{ github.event.repository.name }}-release-${{ runner.os }}" - path: ${{ github.workspace }}/bin/* + path: | + ${{ github.workspace }}/bin/*-universal.dylib + ${{ github.workspace }}/bin/*-x86_64.so + ${{ github.workspace }}/bin/*Driver*-*-Darwin-universal + ${{ github.workspace }}/bin/*Driver*-*-Linux-x86-64 if-no-files-found: error overwrite: true From 371033f1a1f6c17de8235b02e2899415416e5b00 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 2 Jan 2025 16:46:36 -0500 Subject: [PATCH 346/379] Correct environment variable usage, update status message --- CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 68cf742..fc9e30d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ if (APPLE) endif () elseif (WIN32) if (BUILD_32BIT) + set(ENV{CMAKE_GENERATOR_PLATFORM} "Win32") set(ARCH_SUFFIX "x86") else () set(ARCH_SUFFIX "x64") @@ -119,9 +120,9 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" CACHE STRING "Set proplib_message( "Initial Configuration Complete" - "Generator platform: ${CMAKE_GENERATOR_PLATFORM}" + "Generator: ${CMAKE_GENERATOR}" + "Generator platform: $ENV{CMAKE_GENERATOR_PLATFORM}" "C++ Compiler: ${CMAKE_CXX_COMPILER}" - "CMake Build Type: ${CMAKE_BUILD_TYPE}" "Target architecture: ${ARCH_SUFFIX}" "CMake Options:" " BUILD_32BIT = ${BUILD_32BIT}" From 1e0cdde390f5e630c631a64c93c2d49dc444df10 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 2 Jan 2025 16:48:53 -0500 Subject: [PATCH 347/379] Clarify why status may be empty --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fc9e30d..e3b4a1e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,7 +121,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" CACHE STRING "Set proplib_message( "Initial Configuration Complete" "Generator: ${CMAKE_GENERATOR}" - "Generator platform: $ENV{CMAKE_GENERATOR_PLATFORM}" + "Generator platform (for multi-config generators only): $ENV{CMAKE_GENERATOR_PLATFORM}" "C++ Compiler: ${CMAKE_CXX_COMPILER}" "Target architecture: ${ARCH_SUFFIX}" "CMake Options:" From 32e83bfa88a90f01fd6386762743c433efaab795 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 2 Jan 2025 16:52:31 -0500 Subject: [PATCH 348/379] Add architecture arg for 32 bit build in release action --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 61fa85e..cd6ce4b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -49,7 +49,7 @@ jobs: uses: lukka/run-cmake@v10 with: configurePreset: release32 - configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF', '-DRUN_TESTS=OFF', '-DRUN_DRIVER_TESTS=OFF']" + configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF', '-DRUN_TESTS=OFF', '-DRUN_DRIVER_TESTS=OFF', '-A', 'Win32']" buildPreset: release32 - name: Upload release artifacts (binaries) From 03cc81ba696a16fb278b75110f3df9b8de51b65b Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 2 Jan 2025 17:12:16 -0500 Subject: [PATCH 349/379] Warn if preset architecture conflicts with -A argument --- CMakeLists.txt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e3b4a1e..f39f779 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,16 @@ # >=3.15 required for CMake features used in this project cmake_minimum_required(VERSION 3.15 FATAL_ERROR) +# Warn if '-A Win32' is provided but BUILD_32BIT has not been set. This may +# indicate accidental use of a 64-bit CMake preset while intending to build for 32-bit. +if (DEFINED ENV{CMAKE_GENERATOR_PLATFORM}) + if ($ENV{CMAKE_GENERATOR_PLATFORM} STREQUAL "Win32") + if (NOT BUILD_32BIT) + message(WARNING "Generator platform is Win32 but 32-bit preset is not being used.") + endif () + endif () +endif () + # If on macOS, handle arm64/x86_64 architectures (must be done before project()) # For other OS, identify the architecture and save it to the ARCH_SUFFIX variable. if (APPLE) @@ -29,7 +39,6 @@ if (APPLE) endif () elseif (WIN32) if (BUILD_32BIT) - set(ENV{CMAKE_GENERATOR_PLATFORM} "Win32") set(ARCH_SUFFIX "x86") else () set(ARCH_SUFFIX "x64") From 53912b85ee32227fd97b8f3a82c971fef374327a Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 2 Jan 2025 17:23:03 -0500 Subject: [PATCH 350/379] Change MSVC calling convention to __cdecl --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f39f779..cfc3851 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,7 +108,7 @@ function(configure_proplib_target proplib_target) # For GCC-like compilers in 32-bit configurations "$<${gcc_like_cxx}:$:-m32>>>" # For MSVC compiler in any configuration - "$<${msvc_cxx}:$>" + "$<${msvc_cxx}:$>" # For MSVC compiler in Release configurations "$<${msvc_cxx}:$:/O2;/DNDEBUG;/MT>>>" # For MSVC compiler in Debug configurations From 74738320fff58a9f15a2c6d1723575e652f951c8 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 2 Jan 2025 17:36:05 -0500 Subject: [PATCH 351/379] Fix filename for linux driver in release action --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cd6ce4b..209174b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -61,7 +61,7 @@ jobs: ${{ github.workspace }}/bin/*-universal.dylib ${{ github.workspace }}/bin/*-x86_64.so ${{ github.workspace }}/bin/*Driver*-*-Darwin-universal - ${{ github.workspace }}/bin/*Driver*-*-Linux-x86-64 + ${{ github.workspace }}/bin/*Driver*-*-Linux-x86_64 if-no-files-found: error overwrite: true From 44858826bec6a45a3baa8a2cf4212eb4e81d26ed Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 2 Jan 2025 17:42:26 -0500 Subject: [PATCH 352/379] Empty branch for review --- .clang-format | 103 -------- .github/workflows/cff-validator.yml | 27 -- .github/workflows/ctest.yml | 74 ------ .github/workflows/doxygen.yml | 72 ------ .github/workflows/release.yml | 86 ------- .gitignore | 73 ------ .gitmodules | 7 - .zenodo.json | 50 ---- CITATION.cff | 32 --- CMakeLists.txt | 186 -------------- CMakePresets.json | 207 --------------- CONTRIBUTING.md | 370 --------------------------- GitHubRepoPublicReleaseApproval.md | 32 --- LICENSE.md | 34 --- README.md | 153 ----------- app/CMakeLists.txt | 24 -- app/include/CommaSeparatedIterator.h | 42 --- app/include/Driver.h | 44 ---- app/include/ReturnCodes.h | 32 --- app/include/Structs.h | 17 -- app/src/CMakeLists.txt | 40 --- app/src/CommaSeparatedIterator.cpp | 78 ------ app/src/Driver.cpp | 188 -------------- app/src/DriverUtils.cpp | 151 ----------- app/src/ReturnCodes.cpp | 50 ---- app/tests/CMakeLists.txt | 33 --- app/tests/TempTextFile.cpp | 61 ----- app/tests/TempTextFile.h | 37 --- app/tests/TestDriver.cpp | 67 ----- app/tests/TestDriver.h | 180 ------------- docs/CMakeLists.txt | 68 ----- docs/doxy_custom.css | 44 ---- docs/doxy_footer.html | 47 ---- docs/doxy_header.html | 113 -------- docs/doxy_mainpage.md | 33 --- docs/images/ITSlogoOnly400.png | Bin 14748 -> 0 bytes docs/images/apple-touch-icon.png | Bin 5838 -> 0 bytes docs/images/favicon-16x16.png | Bin 2197 -> 0 bytes docs/images/favicon-32x32.png | Bin 2450 -> 0 bytes docs/images/ntia-logo-400px.png | Bin 37216 -> 0 bytes extern/doxygen-awesome-css | 1 - extern/googletest | 1 - include/PropLibTemplate.h | 57 ----- src/CMakeLists.txt | 55 ---- src/ReturnCodes.cpp | 81 ------ tests/CMakeLists.txt | 23 -- tests/TestUtils.cpp | 46 ---- tests/TestUtils.h | 11 - 48 files changed, 3130 deletions(-) delete mode 100644 .clang-format delete mode 100644 .github/workflows/cff-validator.yml delete mode 100644 .github/workflows/ctest.yml delete mode 100644 .github/workflows/doxygen.yml delete mode 100644 .github/workflows/release.yml delete mode 100644 .gitignore delete mode 100644 .gitmodules delete mode 100644 .zenodo.json delete mode 100644 CITATION.cff delete mode 100644 CMakeLists.txt delete mode 100644 CMakePresets.json delete mode 100644 CONTRIBUTING.md delete mode 100644 GitHubRepoPublicReleaseApproval.md delete mode 100644 LICENSE.md delete mode 100644 README.md delete mode 100644 app/CMakeLists.txt delete mode 100644 app/include/CommaSeparatedIterator.h delete mode 100644 app/include/Driver.h delete mode 100644 app/include/ReturnCodes.h delete mode 100644 app/include/Structs.h delete mode 100644 app/src/CMakeLists.txt delete mode 100644 app/src/CommaSeparatedIterator.cpp delete mode 100644 app/src/Driver.cpp delete mode 100644 app/src/DriverUtils.cpp delete mode 100644 app/src/ReturnCodes.cpp delete mode 100644 app/tests/CMakeLists.txt delete mode 100644 app/tests/TempTextFile.cpp delete mode 100644 app/tests/TempTextFile.h delete mode 100644 app/tests/TestDriver.cpp delete mode 100644 app/tests/TestDriver.h delete mode 100644 docs/CMakeLists.txt delete mode 100644 docs/doxy_custom.css delete mode 100644 docs/doxy_footer.html delete mode 100644 docs/doxy_header.html delete mode 100644 docs/doxy_mainpage.md delete mode 100644 docs/images/ITSlogoOnly400.png delete mode 100644 docs/images/apple-touch-icon.png delete mode 100644 docs/images/favicon-16x16.png delete mode 100644 docs/images/favicon-32x32.png delete mode 100644 docs/images/ntia-logo-400px.png delete mode 160000 extern/doxygen-awesome-css delete mode 160000 extern/googletest delete mode 100644 include/PropLibTemplate.h delete mode 100644 src/CMakeLists.txt delete mode 100644 src/ReturnCodes.cpp delete mode 100644 tests/CMakeLists.txt delete mode 100644 tests/TestUtils.cpp delete mode 100644 tests/TestUtils.h diff --git a/.clang-format b/.clang-format deleted file mode 100644 index 96c3869..0000000 --- a/.clang-format +++ /dev/null @@ -1,103 +0,0 @@ -# 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/cff-validator.yml b/.github/workflows/cff-validator.yml deleted file mode 100644 index 6fce818..0000000 --- a/.github/workflows/cff-validator.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Validate CITATION.cff - -on: - push: - paths: - - 'CITATION.cff' - - '.github/workflows/cff-validator.yml' - pull_request: - paths: - - 'CITATION.cff' - - '.github/workflows/cff-validator.yml' - workflow_dispatch: - -jobs: - Validate-CITATION-cff: - runs-on: ubuntu-latest - name: Validate CITATION.cff - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Validate CITATION.cff - uses: dieghernan/cff-validator@v3 - with: - install-r: true diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml deleted file mode 100644 index 7e060b3..0000000 --- a/.github/workflows/ctest.yml +++ /dev/null @@ -1,74 +0,0 @@ -# This action compiles the library and driver and runs all unit tests using an OS and CMake matrix -name: Unit Tests - -on: - push: - branches: ["main", "dev"] - pull_request: - branches: ["main", "dev"] - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} - cancel-in-progress: true - -# Define the matrix for different operating systems -jobs: - build-and-test: - name: ${{ matrix.os }} / ${{ matrix.architecture }} / CMake ${{ matrix.cmakeVersion }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: - - ubuntu-latest - - macos-latest # Apple - - macos-13 # Intel - - windows-latest - architecture: [arm64, x64, x86] - cmakeVersion: ["3.21", latest] # CMake >= 3.21 is required to use "--preset " and discover generators - exclude: - - os: macos-latest - architecture: x86 - - os: macos-latest - architecture: x64 - - os: macos-13 - architecture: x86 - - os: macos-13 - architecture: arm64 - - os: windows-latest - architecture: arm64 - - os: ubuntu-latest - architecture: arm64 - - os: ubuntu-latest - architecture: x86 - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Clone required submodules - run: | - git submodule init extern/googletest - git submodule update - - - name: Install CMake - uses: lukka/get-cmake@latest - with: - cmakeVersion: ${{ matrix.cmakeVersion }} - - - name: "CMake: Build and Test (64-bit)" - if: matrix.architecture != 'x86' - uses: lukka/run-cmake@v10 - with: - configurePreset: release64 - configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF']" - buildPreset: release64 - testPreset: release64 - - - name: "CMake: Build and Test (32-bit)" - if: matrix.architecture == 'x86' - uses: lukka/run-cmake@v10 - with: - configurePreset: release32 - configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF']" - buildPreset: release32 - testPreset: release32 diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml deleted file mode 100644 index a9287dd..0000000 --- a/.github/workflows/doxygen.yml +++ /dev/null @@ -1,72 +0,0 @@ -# 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"] - push: - 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.12.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 deleted file mode 100644 index 209174b..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,86 +0,0 @@ -# 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: ${{ matrix.relName}}' - runs-on: ${{ matrix.os }} - strategy: - matrix: - include: - - os: windows-latest - architecture: x64 - relName: Windows x64 - - os: windows-latest - architecture: x86 - relName: Windows x86 - - os: macos-latest - architecture: arm64 - relName: macOS Universal - - os: ubuntu-latest - architecture: x64 - relName: Linux x64 - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install CMake # (latest stable version) - uses: lukka/get-cmake@latest - - - name: "CMake: Build (64-bit)" - if: matrix.architecture != 'x86' - uses: lukka/run-cmake@v10 - with: - configurePreset: release64 - configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF', '-DRUN_TESTS=OFF', '-DRUN_DRIVER_TESTS=OFF']" - buildPreset: release64 - - - name: "CMake: Build (32-bit)" - if: matrix.architecture == 'x86' - uses: lukka/run-cmake@v10 - with: - configurePreset: release32 - configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF', '-DRUN_TESTS=OFF', '-DRUN_DRIVER_TESTS=OFF', '-A', 'Win32']" - buildPreset: release32 - - - name: Upload release artifacts (binaries) - if: runner.os != 'Windows' - uses: actions/upload-artifact@v4 - with: - name: "${{ github.event.repository.name }}-release-${{ runner.os }}" - path: | - ${{ github.workspace }}/bin/*-universal.dylib - ${{ github.workspace }}/bin/*-x86_64.so - ${{ github.workspace }}/bin/*Driver*-*-Darwin-universal - ${{ github.workspace }}/bin/*Driver*-*-Linux-x86_64 - if-no-files-found: error - overwrite: true - - - name: Upload release artifact (Windows) - if: runner.os == 'Windows' - uses: actions/upload-artifact@v4 - with: - name: "${{ github.event.repository.name }}-release-${{ runner.os }}-${{ matrix.architecture }}" - path: | - ${{ github.workspace }}\bin\Release\*.dll - ${{ github.workspace }}\bin\Release\*Driver-*.exe - if-no-files-found: error - overwrite: true - - - name: Upload release artifact (C++ Header) # TODO-TEMPLATE: Specify the correct header below - if: runner.os == 'Linux' - uses: actions/upload-artifact@v4 - with: - name: "${{ github.event.repository.name }}-release-cpp-header" - path: ${{ github.workspace }}/include/PropLibTemplate.h - if-no-files-found: error - overwrite: true diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 5fed29b..0000000 --- a/.gitignore +++ /dev/null @@ -1,73 +0,0 @@ -############ -## Windows -############ - -# Windows image file caches -Thumbs.db -*.o - -################# -## Visual Studio -################# - -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -# User-specific files -**/.vs -**/bin -**/Debug/ -**/Release/ -**/dotnet/packages -**/dotnet/nuget -*.tlog -*.obj -*.log -*.lastbuildstate -*.manifest -*.users -*.user -*.res -*.opendb -*.db -*.unsuccessfulbuild -*.ipch -*.pdb -*.exp -*.ilk -*.idb -*.opensdf -*.sdf -*.u2d -*.suo -*.aps -**/obj -**/x64 -**/x86 - -######### -## CMake -######### -CMakeLists.txt.user -CMakeCache.txt -CMakeUserPresets.json -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 deleted file mode 100644 index 2e325a1..0000000 --- a/.gitmodules +++ /dev/null @@ -1,7 +0,0 @@ -[submodule "extern/googletest"] - path = extern/googletest - url = https://github.com/google/googletest - branch = v1.12.x -[submodule "extern/doxygen-awesome-css"] - path = extern/doxygen-awesome-css - url = https://github.com/jothepro/doxygen-awesome-css diff --git a/.zenodo.json b/.zenodo.json deleted file mode 100644 index c3c6f74..0000000 --- a/.zenodo.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "upload_type": "software", - "publication_date": "TODO-TEMPLATE", - "title": "TODO-TEMPLATE", - "creators": [ - { - "name": "TODO-TEMPLATE", - "affiliation": "U.S. Department of Commerce, National Telecommunications and Information Administration, Institute for Telecommunication Sciences", - "orcid": "TODO-TEMPLATE" - } - ], - "description": "TODO-TEMPLATE. Make this the same as the abstract.", - "keywords": [ - "TODO-TEMPLATE", - "TODO-TEMPLATE" - ], - "related_identifiers": [ - { - "identifier": "https://github.com/NTIA/TODO-TEMPLATE-dotnet", - "relation": "isSupplementedBy", - "resource_type": "software" - }, - { - "identifier": "https://github.com/NTIA/TODO-TEMPLATE-matlab", - "relation": "isSupplementedBy", - "resource_type": "software" - }, - { - "identifier": "https://github.com/NTIA/TODO-TEMPLATE-python", - "relation": "isSupplementedBy", - "resource_type": "software" - }, - { - "identifier": "https://github.com/NTIA/TODO-TEMPLATE-test-data", - "relation": "isSupplementedBy", - "resource_type": "dataset" - }, - { - "identifier": "https://ntia.github.io/TODO-TEMPLATE/", - "relation": "isDocumentedBy", - "resource_type": "softwaredocumentation" - }, - { - "identifier": "https://ntia.github.io/propagation-library-wiki/models/TODO-TEMPLATE/", - "relation": "isDocumentedBy", - "resource_type": "softwaredocumentation" - } - ], - "version": "TODO-TEMPLATE" -} \ No newline at end of file diff --git a/CITATION.cff b/CITATION.cff deleted file mode 100644 index c5036b1..0000000 --- a/CITATION.cff +++ /dev/null @@ -1,32 +0,0 @@ -cff-version: 1.2.0 -title: >- - TODO-TEMPLATE -message: Please cite this software using these metadata. -type: software -authors: - - given-names: TODO-TEMPLATE - family-names: TODO-TEMPLATE - email: TODO-TEMPLATE@ntia.gov - affiliation: >- - U.S. Department of Commerce, National - Telecommunications and Information Administration, - Institute for Telecommunication Sciences - orcid: 'https://orcid.org/0000-0000-0000-0000' - - name: >- - U.S. Department of Commerce, National - Telecommunications and Information Administration, - Institute for Telecommunication Sciences - address: 325 Broadway - city: Boulder - country: US - post-code: '80305' - region: Colorado - alias: NTIA/ITS - email: code@ntia.gov - website: 'https://its.ntia.gov' -repository-code: 'https://github.com/NTIA/TODO-TEMPLATE' -url: 'https://ntia.github.io/propagation-library-wiki/models/TODO-TEMPLATE/' -keywords: - - propagation - - TODO-TEMPLATE -version: 1.0.0 diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index cfc3851..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,186 +0,0 @@ -############################################################ -## CMakeList.txt : Top-level CMake project file, do global -## configuration and include sub-projects here. -############################################################ - -# >=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.15 required for CMake features used in this project -cmake_minimum_required(VERSION 3.15 FATAL_ERROR) - -# Warn if '-A Win32' is provided but BUILD_32BIT has not been set. This may -# indicate accidental use of a 64-bit CMake preset while intending to build for 32-bit. -if (DEFINED ENV{CMAKE_GENERATOR_PLATFORM}) - if ($ENV{CMAKE_GENERATOR_PLATFORM} STREQUAL "Win32") - if (NOT BUILD_32BIT) - message(WARNING "Generator platform is Win32 but 32-bit preset is not being used.") - endif () - endif () -endif () - -# If on macOS, handle arm64/x86_64 architectures (must be done before project()) -# For other OS, identify the architecture and save it to the ARCH_SUFFIX variable. -if (APPLE) - # Get the current platform's native architecture - execute_process( - COMMAND uname -m - RESULT_VARIABLE result - OUTPUT_VARIABLE MACOS_NATIVE_ARCHITECTURE - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - # If running on Apple silicon, try a universal build. Otherwise, a native build. - if (${MACOS_NATIVE_ARCHITECTURE} STREQUAL "arm64") - set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE) - set(ARCH_SUFFIX "universal") - else () - set(ARCH_SUFFIX ${MACOS_NATIVE_ARCHITECTURE}) - endif () -elseif (WIN32) - if (BUILD_32BIT) - set(ARCH_SUFFIX "x86") - else () - set(ARCH_SUFFIX "x64") - endif () -elseif (UNIX) # Only non-Apple Linux evaluates as True here - set(ARCH_SUFFIX "x86_64") -endif () - -# Convenience function for printing visible status messages. -# Accepts arbitrary number of string inputs, each printed as a -# new line in the status message. -function(proplib_message) - string(REPLACE ";" "\n-- " all_args "${ARGN}") - message(STATUS - "==========[PROPLIB STATUS MESSAGE]==========\n" - "-- ${all_args}\n-- " - "============================================\n" - ) -endfunction() - -########################################### -## PROJECT METADATA -########################################### -# TODO-TEMPLATE: Add project metadata here. See CREATING-REPOSITORIES.md for an example. -set(LIB_NAME "PropLibTemplate") # Name of library/target -set(LIB_NAMESPACE "ITS") # Namespace for the named library -project( - "${LIB_NAMESPACE}.${LIB_NAME}" - VERSION 1.0 - DESCRIPTION "TODO-TEMPLATE: BRIEF DESCRIPTION OF YOUR SOFTWARE" - HOMEPAGE_URL "TODO-TEMPLATE: HOMEPAGE LINK, E.G. TO RELEVANT PAGE ON PROPLIB WIKI" - LANGUAGES "CXX" -) - -########################################### -## CMAKE OPTIONS AND DEFAULTS -########################################### -# Define options. Defaults to: compile 64-bit library and driver, build docs, run tests -option(BUILD_DOCS "Generate documentation site with Doxygen" ON) -option(BUILD_DRIVER "Build the command-line driver executable" ON) -option(RUN_DRIVER_TESTS "Test the command-line driver executable" ON) -option(DOCS_ONLY "Skip all steps except generating the documentation site" OFF) -option(RUN_TESTS "Run unit tests for the main library" ON) -option(BUILD_32BIT "Build project for x86/32-bit instead of x64/64-bit" OFF) - -########################################### -## SETUP -########################################### -# C++11 and some extensions (e.g., for `auto`) are the minimum required -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS ON) - -# Get 0/1 values indicating compiler type -set(gcc_like_cxx "$") -set(msvc_cxx "$") - -# Define a function to set compile options of a provided target. -function(configure_proplib_target proplib_target) - target_compile_options(${proplib_target} PRIVATE - # For GCC-like compilers in any configuration - "$<${gcc_like_cxx}:$>" - # For GCC-like compilers in Release configurations - "$<${gcc_like_cxx}:$:-O3;-DNDEBUG>>>" - # For GCC-like compilers in Debug configurations - "$<${gcc_like_cxx}:$:-g;-O0>>>" - # For GCC-like compilers in 32-bit configurations - "$<${gcc_like_cxx}:$:-m32>>>" - # For MSVC compiler in any configuration - "$<${msvc_cxx}:$>" - # For MSVC compiler in Release configurations - "$<${msvc_cxx}:$:/O2;/DNDEBUG;/MT>>>" - # For MSVC compiler in Debug configurations - "$<${msvc_cxx}:$:/Od;/Zi;/MTd>>>" - ) -endfunction() - -# 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 () - -# control where the static and shared libraries are built -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" CACHE STRING "Set the CMAKE Archive Output Directory") -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" CACHE STRING "Set the CMAKE Library Output Directory") -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" CACHE STRING "Set the CMAKE Runtime Output Directory") - -proplib_message( - "Initial Configuration Complete" - "Generator: ${CMAKE_GENERATOR}" - "Generator platform (for multi-config generators only): $ENV{CMAKE_GENERATOR_PLATFORM}" - "C++ Compiler: ${CMAKE_CXX_COMPILER}" - "Target architecture: ${ARCH_SUFFIX}" - "CMake Options:" - " BUILD_32BIT = ${BUILD_32BIT}" - " BUILD_DOCS = ${BUILD_DOCS}" - " BUILD_DRIVER = ${BUILD_DRIVER}" - " RUN_DRIVER_TESTS = ${RUN_DRIVER_TESTS}" - " DOCS_ONLY = ${DOCS_ONLY}" - " RUN_TESTS = ${RUN_TESTS}" -) - -########################################## -## BUILD/RUN -########################################## -if (NOT DOCS_ONLY) - add_subdirectory(src) # Configure the library - if (RUN_TESTS OR RUN_DRIVER_TESTS) - enable_testing() - include(CTest) - # Initialize GoogleTest if any tests will be configured - if (EXISTS "${PROJECT_SOURCE_DIR}/extern/googletest/CMakeLists.txt") - set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - # Ensure GoogleTest is built as a static library - if (DEFINED BUILD_SHARED_LIBS AND BUILD_SHARED_LIBS) - set(BUILD_SHARED_LIBS_${LIB_NAME} ${BUILD_SHARED_LIBS}) - set(BUILD_SHARED_LIBS OFF) - endif () - add_subdirectory("${PROJECT_SOURCE_DIR}/extern/googletest" "extern/googletest" EXCLUDE_FROM_ALL) - include(GoogleTest) - # Restore initial value of BUILD_SHARED_LIBS - if (DEFINED BUILD_SHARED_LIBS_${LIB_NAME}) - set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_${LIB_NAME}}) - endif () - 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 () - if (RUN_TESTS) # Build and run unit tests - add_subdirectory(tests) - endif () - if (BUILD_DRIVER OR RUN_DRIVER_TESTS) - add_subdirectory(app) - endif () -endif () - -# Generate documentation -if (BUILD_DOCS OR DOCS_ONLY) - add_subdirectory(docs) -endif () diff --git a/CMakePresets.json b/CMakePresets.json deleted file mode 100644 index 6973e97..0000000 --- a/CMakePresets.json +++ /dev/null @@ -1,207 +0,0 @@ -{ - "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": { - "DOCS_ONLY": "OFF", - "RUN_TESTS": "ON", - "BUILD_32BIT": "OFF" - } - }, - { - "name": "proplib-config-release-base", - "hidden": true, - "inherits": "proplib-config-base", - "description": "Base 'Release' configuration preset for ITS PropLib libraries", - "cacheVariables": { - "CMAKE_BUILD_RPATH_USE_ORIGIN": "ON", - "BUILD_SHARED_LIBS": "ON", - "BUILD_DOCS": "ON", - "BUILD_DRIVER": "ON", - "RUN_DRIVER_TESTS": "ON" - }, - "environment": { - "CMAKE_BUILD_TYPE": "Release", - "CMAKE_CONFIGURATION_TYPES": "Release" - } - }, - { - "name": "proplib-config-debug-base", - "hidden": true, - "inherits": "proplib-config-base", - "description": "Base 'Debug' configuration preset for ITS PropLib libraries", - "cacheVariables": { - "CMAKE_BUILD_RPATH_USE_ORIGIN": "ON", - "BUILD_SHARED_LIBS": "ON", - "BUILD_DOCS": "OFF", - "BUILD_DRIVER": "OFF", - "RUN_DRIVER_TESTS": "OFF" - }, - "environment": { - "CMAKE_BUILD_TYPE": "Debug", - "CMAKE_CONFIGURATION_TYPES": "Debug" - } - }, - { - "name": "debug64", - "displayName": "Debug, 64-Bit", - "description": "Build library and tests with debug options. Skip building driver, driver tests, and docs.", - "inherits": "proplib-config-debug-base" - }, - { - "name": "release64", - "displayName": "Release, 64-Bit", - "description": "Build library, driver, docs and tests with release options.", - "inherits": "proplib-config-release-base" - }, - { - "name": "debug32", - "displayName": "Debug, 32-bit", - "description": "Build 32-bit library and tests with debug options. Skip building driver, driver tests, and docs.", - "inherits": "proplib-config-debug-base", - "cacheVariables": { - "BUILD_32BIT": "ON" - } - }, - { - "name": "release32", - "displayName": "Release, 32-bit", - "description": "Build 32-bit library, driver, docs and tests with release options.", - "inherits": "proplib-config-release-base", - "cacheVariables": { - "BUILD_32BIT": "ON" - } - }, - { - "name": "docsOnly", - "displayName": "Doxygen only", - "description": "Do not build or test the library or driver; only build Doxygen docs.", - "inherits": "proplib-config-base", - "cacheVariables": { - "BUILD_DOCS": "ON", - "DOCS_ONLY": "ON", - "RUN_TESTS": "OFF", - "BUILD_DRIVER": "OFF", - "RUN_DRIVER_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 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 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 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 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 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 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 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 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 deleted file mode 100644 index 169a31b..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,370 +0,0 @@ -# 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) -- [Testing Code](#testing-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 Branches - -Our repositories use the following approach to organize and keep track of branches. -The `main` branch typically represents the most recently released version of the software. -The `dev` branch stages changes before they are merged into `main` and a new release is created. -New features or bug fixes should be developed on individual "feature branches" with descriptive names. -When complete, features branches should merge into `dev`. - -### Git Submodules - -PropLib C++ repositories make use of Git submodules to reference certain development -dependencies, e.g. GoogleTest. Depending on the CMake preset or options used, submodules -may be required to successfully build and/or test the software. When cloning a repository, -submodules are not additionally cloned by default. Use the following commands to initialize -and clone any submodules in a repository: - -```cmd -git submodule init -git submodule update -``` - -### 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 (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 to determine -whether or not they should be merged. We may ask questions or request additional -changes which must be addressed. For example, we may request changes so that the code -meets structure, formatting, accuracy, or testing requirements. - -If your PR is approved and merged, your changes will be a part of the `dev` -branch of the repository, where they will stay until a new release is made. At that -point, `dev` will merge into `main` and a new release will be created. The maintainers -of a repository hold the authority on when a new release should be created. For example, -important bug fixes may take higher priority, while small improvements may stay on `dev` -for a while. Rest assured, even if a new release is not immediately made, your approved -changes will be always 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 (with -[GoogleTest](https://github.com/google/googletest) and -[CTest](https://cmake.org/cmake/help/latest/manual/ctest.1.html)), 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 -app/ # The command-line driver which can run the library - data/ # Example input and output files for use with the driver - include/ # Headers used by the command-line driver - src/ # Source code for the command-line driver - tests/ # Header and source files for testing the command-line driver - CMakeLists.txt # Configuration for the command-line driver and its tests - README.md # Usage information for the command-line driver -docs/ - CMakeLists.txt # Doxygen configuration - ... # Static files (images, HTML, CS, Markdown) used by Doxygen -extern/ - ... # External Git submodules/dependencies -include/ - .h # Library interface header file goes here, e.g. "ITM.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. Files containing tests must be included here. -CMakeLists.txt # Top-level CMakeLists.txt: project metadata and options -CMakePresets.json # Presets for CMake, e.g. "release64", "debug32", etc. -... -``` - -### CMake Options and CMake Presets - -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. The -top-level `CMakeLists.txt` stores the primary project configuration and includes -the lower-level configurations based on the preset or specified CMake options. - -The following CMake options are used for top-level project configuration: - -| Option | Default | Definition | -|--------------------|---------|------------------------------------------| -| `BUILD_DOCS` | `ON` | Generate documentation site with Doxygen | -| `BUILD_DRIVER` | `ON` | Build the command-line driver executable | -| `RUN_DRIVER_TESTS` | `ON` | Test the command-line driver executable | -| `DOCS_ONLY` | `OFF` | Skip all steps _except_ generating the documentation site | -| `RUN_TESTS` | `ON` | Run unit tests for the main library | - -[CMake Presets](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html) are -provided to support common build configurations. These are specified in the -`CMakePresets.json` file. The `release` preset will compile the library and driver -with optimizations, build the documentation site, and run all unit tests. The `debug` preset -will skip building the documentation site, driver, and driver tests, which can be useful for -rapid development and testing. Additionally, the Debug configuration will attempt to pass -debug flags to the compiler. Finally, the "docsOnly" preset skips all steps except for -generating the Doxygen documentation site. - -| Option | `release` preset | `debug` preset | `docsOnly` preset | -|--------------------|------------------|----------------|-------------------| -| `DOCS_ONLY` | `OFF` | `OFF` | `ON` | -| `RUN_TESTS` | `ON` | `ON` | `OFF` | -| `CMAKE_BUILD_TYPE` | `Release` | `Debug` | not set | -| `BUILD_DOCS` | `ON` | `OFF` | `ON` | -| `BUILD_DRIVER` | `ON` | `OFF` | `OFF` | -| `RUN_DRIVER_TESTS` | `ON` | `OFF` | `OFF` | - -Below are some examples of how CMake can be called to compile this software. - -```bash -# Configure and compile in 64-bit release configuration -cmake --preset release64 -cmake --build --preset release64 - -# Use the 64-bit release configuration but don't build Doxygen docs -cmake --preset release64 -DBUILD_DOCS=OFF -cmake --build --preset release64 - -# Configure and compile in 32-bit debug configuration -cmake --preset debug32 -cmake --build --preset debug32 - -# Use the 64-bit release configuration but don't run driver tests -cmake --preset release64 -DRUN_DRIVER_TESTS=OFF -cmake --build --preset release64 -``` - -### 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. Except for inline documentation, -use the JavaDoc banner style [described by Doxygen](https://www.doxygen.nl/manual/docblocks.html) - -```cpp -constexpr double = PI 3.1415; /**< Inline format, e.g. for constants */ - -/******************************************************************************* - * 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$. This whole documentation block - * is using the JavaDoc banner style! - * - * @param[in] x The input and its expected units - * @return The result @f$ y = 2x @f$ - ******************************************************************************/ -double doubleTheInput(double x) -{ - return 2 * x; -} -``` - -### Doxygen for C++ Libraries - -The base C++ libraries include Doxygen configurations which generate static -websites from code comments. These documentation sites are published as developer -reference documentation using GitHub Pages. When building the Doxygen site locally, -The site is generated in `docs/html/` and the main page can be accessed at `docs/html/index.html`. -When new releases are made, GitHub Actions workflows are triggered which build and deploy -the Doxygen site to GitHub Pages. - -### MATLAB Wrappers - -MATLAB® wrappers are implemented as toolboxes which interface with the shared library -compiled from C++ source code. The project structure is informed by the best practices -provided by MathWorks® in their [`toolboxdesign` repository](https://github.com/mathworks/toolboxdesign). -Here is an example of how a function may be documented in a MATLAB wrapper. Note the -documentation with code, where input and output arguments are provided for autocompletion. - -```matlab -function y = DoubleTheInput(x) -% DoubleTheInput - produces an output which is twice its input. -% -% Syntax: -% y = DoubleTheInput(x) -% -% Input Arguments: -% x (double) - A number which needs doubling -% -% Output Arguments: -% y (double) - The result, 2*x -% -% Description: -% Functions more complex than this one may warrant an additional, -% longer description. -arguments (Input) - x double -end -arguments (Output) - y double -end -... -``` - -### 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 -``` - -### .NET Wrappers - -PropLib .NET wrappers are written in C# and documentation comments are written in -[XML format](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/documentation-comments) -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; - } -} -``` - -## Testing Code - -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`. - -After compiling the library, you can run unit tests as follows. First, change your -working directory to the `build` directory, then run: - -```bash -ctest -``` diff --git a/GitHubRepoPublicReleaseApproval.md b/GitHubRepoPublicReleaseApproval.md deleted file mode 100644 index d435f83..0000000 --- a/GitHubRepoPublicReleaseApproval.md +++ /dev/null @@ -1,32 +0,0 @@ -# GitHub Repository Public Release Approval - -**Project Name:** NTIA/OSM Research and Development - -**Software Name:** TODO-TEMPLATE - -The project identified above, which is contained within the repository this -document is stored in, has met the following criteria for public release: - -1. [ ] 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. - * [ ] Unit tests are available and the software has been tested against the unit tests. - * [ ] The software can be compiled and/or used on Windows, macOS, and Linux. - * [ ] The repository structure and contents follow from the ITS PropLib template, and - all template or placeholder code has been removed. - * [ ] The repository includes the appropriate `LICENSE.md` file -2. [ ] Any test data necessary for the code and its unit tests to function is included in this -GitHub repository, either directly or as a linked Git submodule. -3. [ ] The README.md file has passed editorial review by the ITS Publications Office. -4. [ ] 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. [ ] 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, and then create a pull request -for that branch. The following must log in to GitHub and approve that pull request -before the pull request can be merged and this repo made public: - -* Project Lead: TODO-TEMPLATE -* Supervising Division Chief or Release Authority: TODO-TEMPLATE diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index add8ca7..0000000 --- a/LICENSE.md +++ /dev/null @@ -1,34 +0,0 @@ -# SOFTWARE DISCLAIMER / RELEASE - -This software was developed by employees of the National Telecommunications and Information -Administration (NTIA), an agency of the Federal Government and is provided to you -as a public service. Pursuant to Title 15 United States Code Section 105, works -of NTIA employees are not subject to copyright protection within the United States. - -The software is provided by NTIA “AS IS.” NTIA MAKES NO WARRANTY OF ANY KIND, EXPRESS, -IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT AND DATA ACCURACY. NTIA does -not warrant or make any representations regarding the use of the software or the -results thereof, including but not limited to the correctness, accuracy, reliability -or usefulness of the software. - -To the extent that NTIA holds rights in countries other than the United States, -you are hereby granted the non-exclusive irrevocable and unconditional right to -print, publish, prepare derivative works and distribute the NTIA software, in any -medium, or authorize others to do so on your behalf, on a royalty-free basis throughout -the World. - -You may improve, modify, and create derivative works of the software or any portion -of the software, and you may copy and distribute such modifications or works. Modified -works should carry a notice stating that you changed the software and should note -the date and nature of any such change. - -You are solely responsible for determining the appropriateness of using and distributing -the software and you assume all risks associated with its use, including but not -limited to the risks and costs of program errors, compliance with applicable laws, -damage to or loss of data, programs or equipment, and the unavailability or interruption -of operation. This software is not intended to be used in any situation where a failure -could cause risk of injury or damage to property. - -Please provide appropriate acknowledgments of NTIA’s creation of the software in -any copies or derivative works of this software. diff --git a/README.md b/README.md deleted file mode 100644 index affa347..0000000 --- a/README.md +++ /dev/null @@ -1,153 +0,0 @@ -# NTIA/ITS Propagation Library Template Project # - - -[![NTIA/ITS PropLib][proplib-badge]][proplib-link] - -[proplib-badge]: https://img.shields.io/badge/PropLib-badge?label=%F0%9F%87%BA%F0%9F%87%B8%20NTIA%2FITS&labelColor=162E51&color=D63E04 -[proplib-link]: https://ntia.github.io/propagation-library-wiki -[gh-actions-test-badge]: https://img.shields.io/github/actions/workflow/status/NTIA/proplib-template/ctest.yml?branch=main&logo=cmake&label=Build%2FTests&labelColor=162E51 -[gh-actions-test-link]: https://github.com/NTIA/proplib-template/actions/workflows/ctest.yml -[gh-actions-docs-badge]: https://img.shields.io/github/actions/workflow/status/NTIA/proplib-template/doxygen.yml?branch=main&logo=c%2B%2B&label=Docs&labelColor=162E51 -[gh-pages-docs-link]: https://ntia.github.io/proplib-template -[gh-releases-badge]: https://img.shields.io/github/v/release/NTIA/proplib-template?logo=github&label=Release&labelColor=162E51&color=D63E04 -[gh-releases-link]: https://github.com/NTIA/proplib-template/releases -[gh-issues-badge]: https://img.shields.io/github/issues/NTIA/proplib-template?logo=github&label=Issues&labelColor=162E51 -[gh-issues-link]: https://github.com/NTIA/proplib-template/issues -[doi-badge]: https://zenodo.org/badge/TODO-TEMPLATE.svg -[doi-link]: https://zenodo.org/badge/latestdoi/TODO-TEMPLATE - - -This code repository is a template repository for software in the NTIA/ITS -Propagation Library (PropLib). This template is intended for developers wishing -to develop a cross-platform C++ library as part of PropLib. Instructions on how -to use this repository are found in its [GitHub Wiki](https://github.com/NTIA/proplib-template/wiki). - -Additional template repositories exist for building .NET, MATLAB, and Python -wrappers for PropLib C++ libraries. Finally, another template is available as -an example of a submodule repository to provide common test data to all versions -of the software. See: - -- [NTIA/proplib-template-dotnet](https://github.com/NTIA/proplib-template-dotnet) -- [NTIA/proplib-template-matlab](https://github.com/NTIA/proplib-template-matlab) -- [NTIA/proplib-template-python](https://github.com/NTIA/proplib-template-python) -- [NTIA/proplib-template-test-data](https://github.com/NTIA/proplib-template-test-data) - -## Contact ## - -For questions about using this template repository, contact - - diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt deleted file mode 100644 index 388d4cc..0000000 --- a/app/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -########################################### -## CONFIGURE THE COMMAND LINE DRIVER -########################################### -set(DRIVER_NAME "${LIB_NAME}Driver") -set(DRIVER_VERSION ${PROJECT_VERSION}.0) -set(DRIVER_HEADERS "${PROJECT_SOURCE_DIR}/app/include") -set(DRIVER_TEST_NAME "${DRIVER_NAME}Test") - -########################################### -## BUILD THE COMMAND LINE DRIVER -########################################### -proplib_message("Configuring command line driver ${DRIVER_NAME}") -add_subdirectory(src) -proplib_message("Done configuring command line driver ${DRIVER_NAME}") - -########################################### -## BUILD AND RUN THE DRIVER TESTS -########################################### -if (RUN_DRIVER_TESTS) - proplib_message("Configuring command line driver tests ${DRIVER_TEST_NAME}") - set_target_properties(${DRIVER_NAME} PROPERTIES ENABLE_EXPORTS ON) - add_subdirectory(tests) - proplib_message("Done configuring command line driver tests ${DRIVER_TEST_NAME}") -endif() diff --git a/app/include/CommaSeparatedIterator.h b/app/include/CommaSeparatedIterator.h deleted file mode 100644 index a83a0ac..0000000 --- a/app/include/CommaSeparatedIterator.h +++ /dev/null @@ -1,42 +0,0 @@ -/** @file CommaSeparatedIterator.h - * Iterator class for reading comma-delimited input text streams. - */ -#pragma once - -#include // for std::istream -#include // For std::string -#include // for std::pair - -/******************************************************************************* - * @class CommaSeparatedIterator - * An iterator that reads lines from an input stream, splitting each line - * into two strings based on a comma delimiter. - * - * This iterator can work with both `std::stringstream` and `std::ifstream`. - ******************************************************************************/ -class CommaSeparatedIterator { - public: - /** Type alias for the value returned by the iterator (pair of strings) */ - using value_type = std::pair; - - /*********************************************************************** - * Constructor method - * - * @param[in] stream The input stream which will be read - **********************************************************************/ - CommaSeparatedIterator(std::istream &stream); - - /** Pre-increment operator to advance the iterator to the next line */ - CommaSeparatedIterator &operator++(); - - /** Dereference operator to obtain the current pair of substrings */ - value_type operator*() const; - - /** Conversion to boolean to check if the iterator is valid */ - explicit operator bool() const; - private: - std::istream &stream_; /**< Reference to the input stream */ - std::string line_; /**< Current line read from the stream */ - std::string first_; /**< First string from the current line */ - std::string second_; /**< Second string from the current line */ -}; diff --git a/app/include/Driver.h b/app/include/Driver.h deleted file mode 100644 index 4f5645c..0000000 --- a/app/include/Driver.h +++ /dev/null @@ -1,44 +0,0 @@ -/** @file Driver.h - * Interface header for this driver executable - */ -#pragma once - -#include "CommaSeparatedIterator.h" -#include "ReturnCodes.h" -#include "Structs.h" - -// TODO-TEMPLATE: Include your library's main interface header -#include "PropLibTemplate.h" - -#include // for std::left, std::setw -#include // for std::cout -#include // for std::endl, std::ostream -#include // for std::string - -///////////////////////////// -// Macros - -/** Shortcut for concise print-to-file statements in driver */ -#define PRINT << std::endl << std::left << std::setw(25) << -/** Shortcut for setting fixed whitespace padding in driver file output */ -#define SETW13 << std::setw(13) << - -////////////////////////////// -// Library Namespace -// TODO-TEMPLATE: use the namespace of your library -using namespace ITS; - -///////////////////////////// -// Functions -void Help(std::ostream &os = std::cout); -DrvrReturnCode ParseArguments(int argc, char **argv, DrvrParams ¶ms); -DrvrReturnCode ValidateInputs(const DrvrParams ¶ms); - -// Driver Utils -std::string GetDatetimeString(); -DrvrReturnCode ParseBoolean(const std::string &str, bool &value); -DrvrReturnCode ParseDouble(const std::string &str, double &value); -DrvrReturnCode ParseInteger(const std::string &str, int &value); -void PrintLabel(std::ostream &os, const std::string &lbl); -void StringToLower(std::string &str); -void Version(std::ostream &os = std::cout); \ No newline at end of file diff --git a/app/include/ReturnCodes.h b/app/include/ReturnCodes.h deleted file mode 100644 index 5866bba..0000000 --- a/app/include/ReturnCodes.h +++ /dev/null @@ -1,32 +0,0 @@ -/** @file ReturnCodes.h - * Defines return codes for the driver - */ -#pragma once - -#include // for std::string - -// TODO-TEMPLATE: Add driver return codes here and corresponding entries in app/src/ReturnCodes.cpp - -/******************************************************************************* - * Return Codes defined by this driver software (128-255) - ******************************************************************************/ -// clang-format off -enum DrvrReturnCode { - // Primary Return Codes - DRVR__SUCCESS = 128, /**< Successful execution */ - DRVR__RETURN_SUCCESS, /**< Indicates driver should exit successfully */ - DRVRERR__MISSING_OPTION, /**< No value provided for given argument */ - DRVRERR__INVALID_OPTION, /**< Unknown option specified */ - DRVRERR__OPENING_INPUT_FILE, /**< Failed to open the input file for reading */ - DRVRERR__OPENING_OUTPUT_FILE, /**< Failed to open the output file for writing */ - - // Input File Parsing Errors - DRVRERR__PARSE = 160, /**< Failed parsing inputs; unknown parameter */ - - // Validation Errors - DRVRERR__VALIDATION_IN_FILE = 192, /**< Input file not specified */ - DRVRERR__VALIDATION_OUT_FILE, /**< Output file not specified */ -}; -// clang-format on - -std::string GetDrvrReturnStatusMsg(int code); diff --git a/app/include/Structs.h b/app/include/Structs.h deleted file mode 100644 index 81409a6..0000000 --- a/app/include/Structs.h +++ /dev/null @@ -1,17 +0,0 @@ -/** @file Structs.h - * Contains data structures and type macros used by this software -*/ -#pragma once - -#include // for std::string - -///////////////////////////// -// Data Structures - -// TODO-TEMPLATE: Basic struct provided to hold input/output file names and DBG flag -/** Parameters provided to the command line driver */ -struct DrvrParams { - std::string in_file = ""; /**< Input file */ - std::string out_file = ""; /**< Output file */ - bool DBG = false; /**< Dump intermediate values to file? */ -}; \ No newline at end of file diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt deleted file mode 100644 index b839aa9..0000000 --- a/app/src/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -########################################### -## BUILD THE DRIVER -########################################### -## TODO-TEMPLATE: Include source AND header files here. Do not include -## source/header files already included in `add_library()` in `src/CMakeLists.txt` -add_executable( - ${DRIVER_NAME} - "CommaSeparatedIterator.cpp" - "Driver.cpp" - "DriverUtils.cpp" - "ReturnCodes.cpp" - "${DRIVER_HEADERS}/CommaSeparatedIterator.h" - "${DRIVER_HEADERS}/Driver.h" - "${DRIVER_HEADERS}/ReturnCodes.h" - "${DRIVER_HEADERS}/Structs.h" -) - -# Add the include directory -target_include_directories(${DRIVER_NAME} PUBLIC "${DRIVER_HEADERS}") - -# Link the library to the executable -target_link_libraries(${DRIVER_NAME} ${LIB_NAME}) - -configure_proplib_target(${DRIVER_NAME}) - -# Add definitions to enable version identification inside the driver -add_compile_definitions( - LIBRARY_VERSION="${PROJECT_VERSION}" - DRIVER_VERSION="${DRIVER_VERSION}" - LIBRARY_NAME="${LIB_NAME}" - DRIVER_NAME="${DRIVER_NAME}" -) - -# Set some target metadata -set_target_properties( - ${DRIVER_NAME} PROPERTIES - OUTPUT_NAME ${DRIVER_NAME}-${DRIVER_VERSION}-${CMAKE_SYSTEM_NAME}- - DEBUG_POSTFIX ${ARCH_SUFFIX} - RELEASE_POSTFIX ${ARCH_SUFFIX} -) \ No newline at end of file diff --git a/app/src/CommaSeparatedIterator.cpp b/app/src/CommaSeparatedIterator.cpp deleted file mode 100644 index 022a952..0000000 --- a/app/src/CommaSeparatedIterator.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/** @file CommaSeparatedIterator.cpp - * Implementation of class to read comma-delimited input text streams. -*/ -#include "CommaSeparatedIterator.h" - -#include "Driver.h" - -#include // for std::size_t -#include // for std::istream -#include // for std::runtime_error -#include // for std::getline, std::string - -CommaSeparatedIterator::CommaSeparatedIterator(std::istream &stream): - stream_(stream) { - ++(*this); // Move to the first line -} - -/*********************************************************************** - * Pre-increment operator. - * - * Advances the iterator to the next line and splits it into two substrings. - * If the end of the stream is reached, both substrings will be empty. Both - * parsed substrings are converted to lowercase. - * - * @return A reference to the updated iterator. - **********************************************************************/ -CommaSeparatedIterator &CommaSeparatedIterator::operator++() { - if (std::getline(stream_, line_)) { - // Skip line if empty - if (line_.empty()) { - return ++(*this); - } - - // Parse line by comma delimiter - std::size_t pos = line_.find(','); - if (pos != std::string::npos) { - first_ = line_.substr(0, pos); - second_ = line_.substr(pos + 1); - } else { - first_ = line_; - second_ = ""; - } - - // Convert both substrings to lowercase - StringToLower(first_); - StringToLower(second_); - } else { - if (stream_.bad()) { - throw std::runtime_error("Error reading stream."); - } - // End of stream reached - first_ = second_ = ""; - } - - return *this; -} - -/*********************************************************************** - * Dereference operator. - * - * Returns the current pair of substrings (first and second). - * - * @return A pair containing the two substrings from the current line. - **********************************************************************/ -CommaSeparatedIterator::value_type CommaSeparatedIterator::operator*() const { - return {first_, second_}; -} - -/*********************************************************************** - * Conversion to boolean. - * - * Checks if the iterator is still valid (not at the end of the input). - * - * @return True if there are still lines to read, otherwise false. - **********************************************************************/ -CommaSeparatedIterator::operator bool() const { - return stream_.good() || !first_.empty() || !second_.empty(); -} \ No newline at end of file diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp deleted file mode 100644 index e9162e8..0000000 --- a/app/src/Driver.cpp +++ /dev/null @@ -1,188 +0,0 @@ -/** @file Driver.cpp - * Implements the main function of the executable, and other high-level functions - */ -#include "Driver.h" - -#include // for std::find -#include // for std::ifstream, std::ofstream -#include // for std::setw -#include // for std::left -#include // for std::cerr -#include // for std::endl -#include // for std::string -#include // for std::vector - -/******************************************************************************* - * Main function of the driver executable - * - * @param[in] argc Number of arguments entered on the command line - * @param[in] argv Array containing the provided command-line arguments - * @return Return code - ******************************************************************************/ -int main(int argc, char **argv) { - int rtn; - DrvrParams params; - - // Parse command line arguments - rtn = ParseArguments(argc, argv, params); - if (rtn == DRVR__RETURN_SUCCESS) { - return SUCCESS; - } else if (rtn != DRVR__SUCCESS) { - Help(); - return rtn; - } - - // Ensure required options were provided - rtn = ValidateInputs(params); - if (rtn != DRVR__SUCCESS) { - Help(); - return rtn; - } - - // TODO-TEMPLATE: Add driver logic, e.g. validating inputs and calling the model - - // TODO-TEMPLATE this code block exists for a unit test to pass. Similar logic - // should be added to functions which parse input files. - std::ifstream file(params.in_file); - if (!file) { - std::cerr << "Failed to open file " << params.in_file << std::endl; - return DRVRERR__OPENING_INPUT_FILE; - } - - // Return driver error code if one was returned - if (rtn > DRVR__RETURN_SUCCESS) { - std::cerr << GetDrvrReturnStatusMsg(rtn) << std::endl; - return rtn; - } - - // Open output file for writing - std::ofstream fp(params.out_file); - if (!fp) { - std::cerr << "Error opening output file. Exiting." << std::endl; - return DRVRERR__OPENING_OUTPUT_FILE; - } - - // Print generator information to file - fp << std::left << std::setw(25) << "Model" << LIBRARY_NAME; - fp PRINT "Library Version" << "v" << LIBRARY_VERSION; - fp PRINT "Driver Version" << "v" << DRIVER_VERSION; - fp PRINT "Date Generated" << GetDatetimeString(); - fp PRINT "Input Arguments"; - for (int i = 1; i < argc; i++) { - fp << argv[i] << " "; - } - fp << std::endl << std::endl; - // TODO-TEMPLATE populate the rest of the output file - fp.close(); - return SUCCESS; -} - -/******************************************************************************* - * Parse the command line arguments - * - * @param[in] argc Number of arguments - * @param[in] argv Command line arguments - * @param[out] params Structure with user input params - * @return Return code - ******************************************************************************/ -DrvrReturnCode ParseArguments(int argc, char **argv, DrvrParams ¶ms) { - // TODO-TEMPLATE: Populate vector with all valid arguments - const std::vector validArgs - = {"-i", "-o", "-dbg", "-h", "--help", "-v", "--version"}; - - for (int i = 1; i < argc; i++) { - // Parse arg to lowercase string - std::string arg(argv[i]); - StringToLower(arg); - - // Check if provided flag is valid - if (std::find(validArgs.begin(), validArgs.end(), arg) - == validArgs.end()) { - // Invalid argument provided - std::cerr << "Unknown option: " << argv[i] << std::endl; - return DRVRERR__INVALID_OPTION; - } - - // Handle simple flags which don't have associated values (e.g., "-v", "-DBG") - if (arg == "-v" || arg == "--version") { - Version(); - return DRVR__RETURN_SUCCESS; - } else if (arg == "-h" || arg == "--help") { - Help(); - return DRVR__RETURN_SUCCESS; - // TODO-TEMPLATE handle any model input flags here - } else if (arg == "-dbg") { - params.DBG = true; - continue; - } - - // Check if end of arguments reached or next argument is another flag - if (i + 1 >= argc || argv[i + 1][0] == '-') { - std::cerr << "Error: no value given for " << arg << std::endl; - return DRVRERR__MISSING_OPTION; - } - - // TODO-TEMPLATE: Handle inputs which provide values (e.g. "-i in.txt"). - // Template code will set in_file and out_file in DrvrParams based on -i - // and -o options. It will also set DrvrParams.DBG based on a -dbg flag - if (arg == "-i") { - params.in_file = argv[i + 1]; - i++; - } else if (arg == "-o") { - params.out_file = argv[i + 1]; - i++; - } - } - - return DRVR__SUCCESS; -} - -/******************************************************************************* - * Print help instructions to the terminal - * - * @param[in] os Output stream for writing; defaults to `std::cout` - ******************************************************************************/ -void Help(std::ostream &os) { - // TODO-TEMPLATE: Update driver help message - os << std::endl << "Usage: .\\ [Options]" << std::endl; - os << "Options (not case sensitive)" << std::endl; - os << "\t-i :: Input file name" << std::endl; - os << "\t-t :: Terrain file name" << std::endl; - os << "\t-o :: Output file name" << std::endl; - os << "\t-dbg :: Dump intermediate values to output file [optional]" - << std::endl; - os << std::endl << "Examples:" << std::endl; - os << "\t[WINDOWS] " << DRIVER_NAME << ".exe -i in.txt -o out.txt" - << std::endl; - os << "\t[LINUX] .\\" << DRIVER_NAME << " -i in.txt -o out.txt" - << std::endl; - os << "Other Options (which don't run the model)" << std::endl; - os << "\t-h :: Display this help message" << std::endl; - os << "\t-v :: Display program version information" << std::endl; - os << std::endl; -}; - -/******************************************************************************* - * Validate that required inputs are present for the mode specified by the user. - * - * This function DOES NOT check the validity of the parameter values, only that - * required parameters have been specified by the user - * - * @param[in] params Structure with user input parameters - * @return Return code - ******************************************************************************/ -DrvrReturnCode ValidateInputs(const DrvrParams ¶ms) { - DrvrParams not_set; - DrvrReturnCode rtn = DRVR__SUCCESS; - // TODO-TEMPLATE: Check that required inputs were provided. - // This template code checks that input/output files were given with -i and -o - if (params.in_file == not_set.in_file) - rtn = DRVRERR__VALIDATION_IN_FILE; - if (params.out_file == not_set.out_file) - rtn = DRVRERR__VALIDATION_OUT_FILE; - - if (rtn != DRVR__SUCCESS) - std::cerr << GetDrvrReturnStatusMsg(rtn) << std::endl; - - return rtn; -} diff --git a/app/src/DriverUtils.cpp b/app/src/DriverUtils.cpp deleted file mode 100644 index fe9f69b..0000000 --- a/app/src/DriverUtils.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/** @file DriverUtils.cpp - * Implements various model-agnostic utility functions for the driver - */ -#include "Driver.h" - -#ifdef _WIN32 - // Ensure localtime_s is available on Windows - #ifndef __STDC_LIB_EXT1__ - #define __STDC_LIB_EXT1__ - #endif - #ifndef __STDC_WANT_LIB_EXT1__ - #define __STDC_WANT_LIB_EXT1__ 1 - #endif -#endif - -#include // for std::transform -#include // for std::tolower -#include // for std::size_t -#include // for localtime_{s,r}, std::{time, time_t, tm, strftime} -#include // for std::setfill, std::setw -#include // for std::cerr, std::endl -#include // for std::ostream -#include // for std::stod, std::stoi, std::string - -/****************************************************************************** - * Get a string containing the current date and time information. - * - * @return A localized standard date and time string (locale dependent) - ******************************************************************************/ -std::string GetDatetimeString() { - std::time_t now = std::time(nullptr); - struct std::tm localTime; - -#ifdef _WIN32 - localtime_s(&localTime, &now); -#else - if (localtime_r(&now, &localTime) == nullptr) { - return "Date and time unknown"; - } -#endif - char mbstr[100]; - if (std::strftime(mbstr, sizeof(mbstr), "%a %b %d %H:%M:%S %Y", &localTime) - == 0) { - return "Could not format datetime string"; - } - return std::string(mbstr); -} - -/******************************************************************************* - * Parse a boolean value read from the input parameter file. - * - * Supports either "true" or "false" (case-insensitive) or "0" or "1" - * - * @param[in] str Input file value as string - * @param[out] value Input file value converted to bool - * @return Return code - ******************************************************************************/ -DrvrReturnCode ParseBoolean(const std::string &str, bool &value) { - try { - std::string str_lower = str; - StringToLower(str_lower); - if (str_lower == "0" || str_lower == "false") { - value = false; - } else if (str_lower == "1" || str_lower == "true") { - value = true; - } else { - return DRVRERR__PARSE; - } - } catch (...) { - return DRVRERR__PARSE; - } - return DRVR__SUCCESS; -} - -/******************************************************************************* - * Parse a double value read from the input parameter file - * - * @param[in] str Input file value as string - * @param[out] value Input file value converted to double - * @return Return code - ******************************************************************************/ -DrvrReturnCode ParseDouble(const std::string &str, double &value) { - try { - value = std::stod(str); - } catch (...) { - // error parsing the input string value - return DRVRERR__PARSE; - } - - return DRVR__SUCCESS; -} - -/******************************************************************************* - * Parse an integer value read from the input parameter file - * - * @param[in] str Input file value as string - * @param[out] value Input file value converted to int - * @return Return code - ******************************************************************************/ -DrvrReturnCode ParseInteger(const std::string &str, int &value) { - try { - std::size_t pos; - value = std::stoi(str, &pos, 10); - - // Verify the entire string was parsed - if (pos != str.size()) { - return DRVRERR__PARSE; - } - } catch (...) { - // error parsing the input string value - return DRVRERR__PARSE; - }; - - return DRVR__SUCCESS; -} - -/******************************************************************************* - * Helper function to standardize printing of text labels to file - * - * @param[in] os Output stream for writing - * @param[in] lbl Text message - ******************************************************************************/ -void PrintLabel(std::ostream &os, const std::string &lbl) { - os << "[" << lbl << "]"; -} - - -/****************************************************************************** - * Convert a string to lowercase. - * - * @param[in, out] str The string to convert - ******************************************************************************/ -void StringToLower(std::string &str) { - std::transform(str.begin(), str.end(), str.begin(), [](const char c) { - return static_cast(std::tolower(c)); - }); -} - -/******************************************************************************* - * Print version information to the specified output stream - * - * @param[in] os Output stream for writing; defaults to `std::cout` - ******************************************************************************/ -void Version(std::ostream &os) { - os << std::setfill('*') << std::setw(55) << "" << std::endl; - os << "Institute for Telecommunication Sciences - Boulder, CO" << std::endl; - os << "\tDriver Version: " << DRIVER_VERSION << std::endl; - os << "\t" << LIBRARY_NAME << " Version: " << LIBRARY_VERSION << std::endl; - os << "Time: " << GetDatetimeString() << std::endl; - os << std::setfill('*') << std::setw(55) << "" << std::endl; -} diff --git a/app/src/ReturnCodes.cpp b/app/src/ReturnCodes.cpp deleted file mode 100644 index 9f2792a..0000000 --- a/app/src/ReturnCodes.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/** @file ReturnCodes.cpp - * Maps status message strings to driver return codes. - */ - -#include "ReturnCodes.h" - -#include // for std::string -#include // for std::unordered_map - -/******************************************************************************* - * Get an error message string from a return code. - * - * @param[in] code Driver return code. - * @return A status message corresponding to the input code. - ******************************************************************************/ -std::string GetDrvrReturnStatusMsg(int code) { - static const std::unordered_map messages = { - {DRVR__SUCCESS, "Successful execution"}, - {DRVR__RETURN_SUCCESS, "Internal driver success"}, - {DRVRERR__MISSING_OPTION, "No value provided for given argument"}, - {DRVRERR__INVALID_OPTION, "Unknown option specified"}, - {DRVRERR__OPENING_INPUT_FILE, - "Failed to open the input file for reading"}, - {DRVRERR__OPENING_OUTPUT_FILE, - "Failed to open the output file for writing"}, - {DRVRERR__PARSE, "Failed parsing inputs; unknown parameter"}, - {DRVRERR__VALIDATION_IN_FILE, - "Option -i is required but was not provided"}, - {DRVRERR__VALIDATION_OUT_FILE, - "Option -o is required but was not provided"}, - }; - - // Construct status message - std::string msg = DRIVER_NAME; - msg += " v"; - msg += DRIVER_VERSION; - if (code == DRVR__SUCCESS) { - msg += " Status: "; - } else { - msg += " Error: "; - } - - auto it = messages.find(static_cast(code)); - if (it != messages.end()) { - msg += it->second; - } else { - msg += "Undefined return code"; - } - return msg; -} diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt deleted file mode 100644 index 8a9f4ef..0000000 --- a/app/tests/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -############################################ -## CONFIGURE COMMAND LINE DRIVER TESTS -############################################ -## TODO-TEMPLATE: Include source AND header files for tests here. -add_executable( - ${DRIVER_TEST_NAME} - "TempTextFile.cpp" - "TestDriver.cpp" - "TempTextFile.h" - "TestDriver.h" - "${DRIVER_HEADERS}/Driver.h" -) - -# Add the include directories -target_include_directories( - ${DRIVER_TEST_NAME} PUBLIC - "${DRIVER_HEADERS}" - "${PROJECT_SOURCE_DIR}/app/tests" -) - -# Link the library to the executable -target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME}) - -# Make driver executable location available to source -add_compile_definitions(DRIVER_LOCATION="$") - -########################################### -## SET UP AND DISCOVER TESTS -########################################### -include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) -target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME} GTest::gtest_main) -include(GoogleTest) -gtest_discover_tests(${DRIVER_TEST_NAME}) diff --git a/app/tests/TempTextFile.cpp b/app/tests/TempTextFile.cpp deleted file mode 100644 index f814e3d..0000000 --- a/app/tests/TempTextFile.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/** @file TempTextFile.cpp - * Implements a class to create and write to temporary text files - */ -#include "TempTextFile.h" - -#ifdef _WIN32 - // Ensure tmpnam_s is available on Windows - #ifndef __STDC_LIB_EXT1__ - #define __STDC_LIB_EXT1__ - #endif - #ifndef __STDC_WANT_LIB_EXT1__ - #define __STDC_WANT_LIB_EXT1__ 1 - #endif - #include // for L_tmpnam_s, tmpnam_s -#else // macOS and Linux - #include // for mkstemp - #include // for close -#endif - -#include // for std::remove -#include // for std::ofstream -#include // for std::cerr, std::ios::trunc -#include // for std::endl -#include // for std::runtime_error -#include // for std::string - -TempTextFile::TempTextFile(const std::string &content) { -#ifdef _WIN32 - // Generate and store a temporary file name - char tempFileName[L_tmpnam_s]; - if (tmpnam_s(tempFileName, sizeof(tempFileName)) != 0) { - throw std::runtime_error("Failed to create temporary file name."); - } -#else - // Safer implementation for POSIX platforms - char tempFileName[] = "/tmp/proplib-tempfile.XXXXXX"; - int fd = mkstemp(tempFileName); - if (fd == -1) { - throw std::runtime_error("Failed to create temporary file."); - } - close(fd); -#endif - filename = tempFileName; // Store generated filename - std::ofstream tempFile(tempFileName, std::ios::trunc); - if (!tempFile.is_open()) { - std::cerr << "Temp file name is: " << filename << std::endl; - throw std::runtime_error("Failed to open temporary file for writing."); - } - tempFile << content; - tempFile.close(); -} - -TempTextFile::~TempTextFile() { - // Delete the temporary file upon destruction - std::remove(filename.c_str()); -} - -std::string TempTextFile::getFileName() const { - // Return the name of the temporary file. - return filename; -} \ No newline at end of file diff --git a/app/tests/TempTextFile.h b/app/tests/TempTextFile.h deleted file mode 100644 index f70867d..0000000 --- a/app/tests/TempTextFile.h +++ /dev/null @@ -1,37 +0,0 @@ -/** @file TempTextFile.h - * Header for a class which manages temporary text files. - */ -#pragma once - -#include // for std::string - -/******************************************************************************* - * A class to manage a temporary text file. - * - * The TempTextFile class creates a temporary text file from a string that is - * automatically deleted when the object is destroyed. - ******************************************************************************/ -class TempTextFile { - public: - /*********************************************************************** - * Constructor that creates a temporary file and writes content to it. - * - * @param[in] content String content to write to the file. - * @throws std::runtime_error On failure to create or write to file. - **********************************************************************/ - TempTextFile(const std::string &content); - - /*********************************************************************** - * Destructor that closes (and deletes) the temporary file. - **********************************************************************/ - ~TempTextFile(); - - /*********************************************************************** - * Retrieve the name of the temporary file - * - * @return A string containing the name of the temporary file. - **********************************************************************/ - std::string getFileName() const; - private: - std::string filename; /**< Name of the temporary file */ -}; diff --git a/app/tests/TestDriver.cpp b/app/tests/TestDriver.cpp deleted file mode 100644 index abc9083..0000000 --- a/app/tests/TestDriver.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/** @file TestDriver.cpp - * General tests for the driver executable - */ -#include "TestDriver.h" - -#include // for std::string - -TEST_F(DriverTest, MissingOptionError1) { - // Test case: missing option between two provided flags - std::string cmd = executable + " -i -o out.txt"; - SuppressOutputs(cmd); - int rtn = RunCommand(cmd); - EXPECT_EQ(DRVRERR__MISSING_OPTION, rtn); -} - -TEST_F(DriverTest, MissingOptionError2) { - // Test case: missing option at the end of command - std::string cmd = executable + " -i"; - SuppressOutputs(cmd); - int rtn = RunCommand(cmd); - EXPECT_EQ(DRVRERR__MISSING_OPTION, rtn); -} - -TEST_F(DriverTest, InvalidOptionError) { - std::string cmd = executable + " -X"; - SuppressOutputs(cmd); - int rtn = RunCommand(cmd); - EXPECT_EQ(DRVRERR__INVALID_OPTION, rtn); -} - -TEST_F(DriverTest, OpeningInputFileError) { - // TODO-TEMPLATE: Update this call to RunDriver - params.in_file = "/invalid/path/input.xyz"; - params.DBG = false; - int rtn = RunDriver(params); - EXPECT_EQ(DRVRERR__OPENING_INPUT_FILE, rtn); -} - -TEST_F(DriverTest, OpeningOutputFileError) { - // TODO-TEMPLATE: Update this call to RunDriverWithInputFile - // Provide valid inputs but invalid output file path - std::string inputs = "template,0.0"; - params.DBG = true; - params.out_file = "/invalid/path/output.xyz"; - int rtn = RunDriverWithInputFile(inputs, params); - EXPECT_EQ(DRVRERR__OPENING_OUTPUT_FILE, rtn); -} - -TEST_F(DriverTest, ValidationInFileError) { - std::string cmd = executable + " -o out.txt"; - SuppressOutputs(cmd); - int rtn = RunCommand(cmd); - EXPECT_EQ(DRVRERR__VALIDATION_IN_FILE, rtn); -} - -TEST_F(DriverTest, ValidationOutFileError) { - // Input file does not need to exist here, just has to be specified - // TODO-TEMPLATE May need to update the command here - std::string cmd = executable + " -i in.txt"; - SuppressOutputs(cmd); - int rtn = RunCommand(cmd); - EXPECT_EQ(DRVRERR__VALIDATION_OUT_FILE, rtn); -} - -// TODO-TEMPLATE: Add tests for any additional validation errors - -// TODO-TEMPLATE: Add other general tests for the driver \ No newline at end of file diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h deleted file mode 100644 index b01796c..0000000 --- a/app/tests/TestDriver.h +++ /dev/null @@ -1,180 +0,0 @@ -/** @file TestDriver.h - * Primary header and test fixture for command line driver tests. - */ -#pragma once - -// clang-format off -// GoogleTest must be included first -#include // GoogleTest -// clang-format on - -#include "Driver.h" -#include "TempTextFile.h" - -#include // for std::remove, std::perror -#include // for std::system -#include // for std::cout -#include // for std::endl, std::flush -#include // for std::string - -#ifndef _WIN32 - #include // for WEXITSTATUS -#endif - -/******************************************************************************* - * @class DriverTest - * Test fixture for running the driver executable tests. - * - * This class extends the Google Test framework's Test class and provides - * utilities to set up, execute, and manage the output of the driver executable. - ******************************************************************************/ -class DriverTest: public ::testing::Test { - protected: - /*********************************************************************** - * Sets up the test environment. - **********************************************************************/ - void SetUp() override { - // TODO-TEMPLATE review and optionally adjust default params here - // Set the default driver params - params.DBG = false; - params.out_file = "tmp_out.txt"; - - // Get the name of the executable to test - executable = std::string(DRIVER_LOCATION); - } - - /*********************************************************************** - * Suppresses the output of the command. - * - * Appends redirection to suppress standard output and error based on - * the platform. - * - * @param[in, out] cmd The command string to modify - **********************************************************************/ - void SuppressOutputs(std::string &cmd) { -#ifdef _WIN32 - cmd += " > nul"; -#else - cmd += " > /dev/null"; -#endif - cmd += " 2>&1"; - } - - /*********************************************************************** - * Builds the command string to run the driver. - * - * Constructs a command string using the provided driver parameters - * struct. Optionally, the command can be written such that stdout and - * are suppressed. - * - * @param[in] dParams The driver parameters - * @param[in] suppressOutputs Whether to suppress outputs (default: true) - * @return The constructed command string - **********************************************************************/ - std::string BuildCommand( - const DrvrParams &dParams, const bool suppressOutputs = true - ) { - // TODO-TEMPLATE: Modify this function to correctly - // unpack the DrvrParams struct and build the command - - // Construct command from parameters - std::string command = executable; - command += " -i " + dParams.in_file; - if (dParams.DBG) { - command += " -DBG"; - } - command += " -o " + dParams.out_file; - - // Suppress text output of the driver, to avoid cluttering - // test outputs. - if (suppressOutputs) { - SuppressOutputs(command); - } - // Return the full command string - return command; - } - - /*********************************************************************** - * Runs the provided command (cross-platform) - * - * Note that on POSIX platforms the exit code of the command should be - * between 0 and 255. Exit codes outside this range will be shifted into - * this range and cannot be unambiguously compared to expectations. - * - * @param[in] cmd The command to run - * @return The exit code of the command. - **********************************************************************/ - int RunCommand(const std::string &cmd) { - std::cout << std::flush; - int rtn = std::system(cmd.c_str()); -#ifndef _WIN32 - rtn = WEXITSTATUS(rtn); // Get child process exit code on POSIX -#endif - return rtn; - } - - /*********************************************************************** - * Runs the driver executable. - * - * @param[in] dParams Parameters to parse as command line arguments - * @return Return code from the driver execution - **********************************************************************/ - int RunDriver(const DrvrParams &dParams) { - std::string cmd = BuildCommand(dParams); - return RunCommand(cmd); - } - - /*********************************************************************** - * Runs the driver using the specified input file contents. - * - * This method creates a temporary text file containing the contents - * of `inFileContents` and then runs the driver using the temporary - * file as the input file. The rest of the required driver parameters - * are provided in the `params` input; the `params.in_file` value is - * ignored and can be unset. If an output file was produced by the - * driver, it is deleted before this method returns. - * - * @param[in] inFileContents The contents to write to the input file - * @param[in] dParams A populated driver parameters struct (see above) - * @return Return code from the driver execution - **********************************************************************/ - int RunDriverWithInputFile( - const std::string &inFileContents, const DrvrParams &dParams - ) { - DrvrParams updated_params = dParams; - TempTextFile tempFile(inFileContents); - updated_params.in_file = tempFile.getFileName(); - int rtn = RunDriver(updated_params); - // Cleanup: delete output file if it was created - DeleteOutputFile(updated_params.out_file); - return rtn; - } - - /*********************************************************************** - * Deletes the specified output file if it exists. - * - * Checks if the file exists and attempts to delete it. Reports any - * errors encountered during deletion. - * - * @param[in] fileName The name of the file to delete. - **********************************************************************/ - void DeleteOutputFile(const std::string &fileName) { - bool fileExists = false; -#ifdef _WIN32 - fileExists = _access(fileName.c_str(), 0) == 0; -#else - fileExists = access(fileName.c_str(), F_OK) == 0; -#endif - if (fileExists) { - if (std::remove(fileName.c_str()) != 0) { - std::perror("Error deleting output file"); - } - } - } - - /** Platform-dependent string to call the executable */ - std::string executable; - - /** Driver parameters struct which may be used by tests */ - DrvrParams params; -}; diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt deleted file mode 100644 index aeb6125..0000000 --- a/docs/CMakeLists.txt +++ /dev/null @@ -1,68 +0,0 @@ -########################################### -## 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-400px.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( - "${LIB_NAME}Docs" - "${PROJECT_SOURCE_DIR}/app/src" - "${PROJECT_SOURCE_DIR}/app/include" - "${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 deleted file mode 100644 index d2ccfca..0000000 --- a/docs/doxy_custom.css +++ /dev/null @@ -1,44 +0,0 @@ -.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 deleted file mode 100644 index 6bb41ea..0000000 --- a/docs/doxy_footer.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - diff --git a/docs/doxy_header.html b/docs/doxy_header.html deleted file mode 100644 index ebea68f..0000000 --- a/docs/doxy_header.html +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - -$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 deleted file mode 100644 index 9084066..0000000 --- a/docs/doxy_mainpage.md +++ /dev/null @@ -1,33 +0,0 @@ -# Main Page - -This website is an information-oriented API reference document for the @libname -C++ library and associated command-line driver, 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 bindings for Python, MATLAB, and .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 deleted file mode 100644 index 45a7ba3fdc8a947949e4eb97376705b2779a64d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14748 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 diff --git a/docs/images/apple-touch-icon.png b/docs/images/apple-touch-icon.png deleted file mode 100644 index d6371ec98d7c7f24ddc8ac1570990a92336887dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5838 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 S^DO#(!)&Bys#~e!9R6SD2Sfz` diff --git a/docs/images/favicon-16x16.png b/docs/images/favicon-16x16.png deleted file mode 100644 index cc0687f5808893c9361d0c67cf5b5a374ae61185..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2197 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 diff --git a/extern/doxygen-awesome-css b/extern/doxygen-awesome-css deleted file mode 160000 index 568f56c..0000000 --- a/extern/doxygen-awesome-css +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 568f56cde6ac78b6dfcc14acd380b2e745c301ea diff --git a/extern/googletest b/extern/googletest deleted file mode 160000 index 58d77fa..0000000 --- a/extern/googletest +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 58d77fa8070e8cec2dc1ed015d66b454c8d78850 diff --git a/include/PropLibTemplate.h b/include/PropLibTemplate.h deleted file mode 100644 index 4a0dbaf..0000000 --- a/include/PropLibTemplate.h +++ /dev/null @@ -1,57 +0,0 @@ -/** @file PropLibTemplate.h - * Interface header for this library - * // TODO-TEMPLATE: Rename this file to your ${LIB_NAME} - */ -#pragma once - -#include // for std::string -#include // for std::unordered_map - -// TODO-TEMPLATE: This header should provide EVERYTHING needed to interface -// with the shared library, without needing to include other headers. - -namespace ITS { -// TODO-TEMPLATE: Use your library's namespace - -// Define cross-platform PROPLIB_API to export functions -#ifndef DOXYGEN_SHOULD_SKIP - #ifndef PROPLIB_API - #ifdef _WIN32 - #define PROPLIB_API extern "C" __declspec(dllexport) - #else - #define PROPLIB_API extern "C" - #endif - #endif -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Enums - -/******************************************************************************* - * Return Codes defined by this software (0-127) - ******************************************************************************/ -// clang-format off -enum ReturnCode { - SUCCESS = 0, /**< Successful execution */ - - // TODO-TEMPLATE add return codes for this software - // TODO-TEMPLATE: Add corresponding status messages in src/ReturnCodes.cpp -}; -// clang-format on - -//////////////////////////////////////////////////////////////////////////////// -// Constants -// TODO-TEMPLATE define any global constants here (use constexpr!) - -//////////////////////////////////////////////////////////////////////////////// -// Public Functions -// TODO-TEMPLATE: Add functions which should be exported in the DLL -PROPLIB_API char *GetReturnStatusCharArray(const int code); -PROPLIB_API void FreeReturnStatusCharArray(char *c_msg); - -//////////////////////////////////////////////////////////////////////////////// -// Private Functions -// TODO-TEMPLATE: Add other/internal functions here (no need for "PROPLIB_API") -std::string GetReturnStatus(const int code); - -} // namespace ITS diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 33976f3..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,55 +0,0 @@ -########################################### -## BUILD THE LIBRARY -########################################### -proplib_message("Configuring library ${LIB_NAME}") - -set(LIB_HEADERS "${PROJECT_SOURCE_DIR}/include") -set(LIB_FILES - "ReturnCodes.cpp" - "${LIB_HEADERS}/${LIB_NAME}.h" - ## TODO-TEMPLATE: Include source AND header files here. -) - -# By default, create shared library -if (NOT DEFINED BUILD_SHARED_LIBS) - message(STATUS "STATUS: BUILD_SHARED_LIBS is not defined to build the library: " ${LIB_NAME} ".") - add_library(${LIB_NAME} SHARED ${LIB_FILES}) -else () - message(STATUS "STATUS: BUILD_SHARED_LIBS is " ${BUILD_SHARED_LIBS} " to build the library: " ${LIB_NAME} ".") - add_library(${LIB_NAME} ${LIB_FILES}) -endif () - -# Add the include directory -target_include_directories(${LIB_NAME} PUBLIC "${LIB_HEADERS}") - -# link library to proplib_compiler_flags -# target_link_libraries(${LIB_NAME} PUBLIC proplib_compiler_flags) - -# Configure compiler options -configure_proplib_target(${LIB_NAME}) - -# Add definition to get the library name and version inside the library -add_compile_definitions( - LIBRARY_NAME="${LIB_NAME}" - LIBRARY_VERSION="${PROJECT_VERSION}" -) - -# Platform-specific configurations -if (WIN32) - set_target_properties(${LIB_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS true) -endif () -if (UNIX) - # avoid prefixing "lib" to the output file, for cross-platform consistency - set(CMAKE_SHARED_LIBRARY_PREFIX "") -endif () - -# Set some target metadata -set_target_properties( - ${LIB_NAME} PROPERTIES - OUTPUT_NAME ${LIB_NAME}-${PROJECT_VERSION}- - VERSION ${PROJECT_VERSION} - DEBUG_POSTFIX ${ARCH_SUFFIX} - RELEASE_POSTFIX ${ARCH_SUFFIX} -) - -proplib_message("Done configuring library ${LIB_NAME}") \ No newline at end of file diff --git a/src/ReturnCodes.cpp b/src/ReturnCodes.cpp deleted file mode 100644 index bee6333..0000000 --- a/src/ReturnCodes.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/** @file ReturnCodes.cpp - * Maps status messages to library return codes - */ - -// TODO-TEMPLATE include your primary library header -#include "PropLibTemplate.h" - -#ifdef _WIN32 - // Ensure strcpy_s is available on Windows - #ifndef __STDC_LIB_EXT1__ - #define __STDC_LIB_EXT1__ - #endif - #ifndef __STDC_WANT_LIB_EXT1__ - #define __STDC_WANT_LIB_EXT1__ 1 - #endif -#endif - -#include // for strcpy_s -#include // for std::string -#include // for std::unordered_map - -namespace ITS { -// TODO-TEMPLATE: put these functions in this software's namespace - -/******************************************************************************* - * Get an error message string from a return code. - * - * @param[in] code Integer return code. - * @return A status message corresponding to the input code. - ******************************************************************************/ -std::string GetReturnStatus(int code) { - static const std::unordered_map messages = { - {SUCCESS, "Successful execution"} - // TODO-TEMPLATE: Add messages corresponding to all return codes here - }; - // Construct status message - std::string msg = LIBRARY_NAME; - msg += " v"; - msg += LIBRARY_VERSION; - if (code == SUCCESS) { - msg += " Status: "; - } else { - msg += " Error: "; - } - - auto it = messages.find(static_cast(code)); - if (it != messages.end()) { - msg += it->second; - } else { - msg += "Undefined return code"; - } - return msg; -} - -/******************************************************************************* - * Get an error message string (as C-style string) from a return code. - * - * @param[in] code Integer return code. - * @return A status message corresponding to the input code. - ******************************************************************************/ -char *GetReturnStatusCharArray(const int code) { - const std::string msg = GetReturnStatus(code); - char *c_msg = new char[msg.size() + 1]; -#ifdef _WIN32 - strcpy_s(c_msg, msg.size() + 1, msg.c_str()); -#else - strcpy(c_msg, msg.c_str()); -#endif - return c_msg; -} - -/******************************************************************************* - * Free the memory allocated by GetReturnStatusCharArray - * - * @param[in] c_msg The status message C-style string to delete - ******************************************************************************/ -void FreeReturnStatusCharArray(char *c_msg) { - delete[] c_msg; -} - -} // namespace ITS \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt deleted file mode 100644 index 0fcd455..0000000 --- a/tests/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -############################################ -## CONFIGURE UNIT TESTS -############################################ -set(TEST_NAME "${LIB_NAME}Test") -proplib_message("Configuring library tests ${TEST_NAME}") - -## TODO-TEMPLATE: Include all source AND header files for tests here. -## Do not include the source and header files of the library under test. -add_executable( - ${TEST_NAME} - "TestUtils.cpp" - "TestUtils.h" -) - -########################################### -## SET UP AND DISCOVER TESTS -########################################### -include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) -target_link_libraries(${TEST_NAME} ${LIB_NAME} GTest::gtest_main) -include(GoogleTest) -gtest_discover_tests(${TEST_NAME}) - -proplib_message("Done configuring library tests ${TEST_NAME}") \ No newline at end of file diff --git a/tests/TestUtils.cpp b/tests/TestUtils.cpp deleted file mode 100644 index 4a62ae6..0000000 --- a/tests/TestUtils.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/** @file TestUtils.cpp - * Primary implementations for fixtures or common functions used by unit tests. - */ -#include "TestUtils.h" - -#include // for std::string - -// TODO-TEMPLATE: populate this file with common utilities for tests - -/******************************************************************************* - * Append a directory separator ('/' or '\') to a string, based on the - * current operating system. - * - * @param[in, out] str String to which the character will be appended. - *****************************************************************************/ -void AppendDirectorySep(std::string &str) { -#ifdef _WIN32 - str += "\\"; -#else - str += "/"; -#endif -} - -/****************************************************************************** - * Get the full path of the directory containing test data files. - * - * @return The path of the test data directory. - *****************************************************************************/ -std::string GetDataDirectory() { - std::string dataDir(__FILE__); - dataDir.resize(dataDir.find_last_of("/\\")); - dataDir.resize(dataDir.find_last_of("/\\")); - AppendDirectorySep(dataDir); - dataDir += "extern"; - AppendDirectorySep(dataDir); - dataDir - += "test-data"; // Name of data directory as cloned in the `extern` directory - AppendDirectorySep(dataDir); - return dataDir; -} - -// TODO-TEMPLATE: Remove this test and write your own in other files. -// This is included to verify test discovery is functional in the template. -TEST(TemplateTest, TestTemplate) { - EXPECT_EQ(1, 1); -} diff --git a/tests/TestUtils.h b/tests/TestUtils.h deleted file mode 100644 index adf443d..0000000 --- a/tests/TestUtils.h +++ /dev/null @@ -1,11 +0,0 @@ -/** @file TestUtils.h - * Primary header for fixtures or common functions used by unit tests. - */ -#pragma once - -// clang-format off -// GoogleTest must be included first -#include // GoogleTest -// clang-format on - -// TODO-TEMPLATE: define any common test fixtures here \ No newline at end of file From 9c6512288395ef85f734c1db74c8ac0463c8c2ce Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 2 Jan 2025 17:43:55 -0500 Subject: [PATCH 353/379] Revert "Empty branch for review" This reverts commit 44858826bec6a45a3baa8a2cf4212eb4e81d26ed. --- .clang-format | 103 ++++++++ .github/workflows/cff-validator.yml | 27 ++ .github/workflows/ctest.yml | 74 ++++++ .github/workflows/doxygen.yml | 72 ++++++ .github/workflows/release.yml | 86 +++++++ .gitignore | 73 ++++++ .gitmodules | 7 + .zenodo.json | 50 ++++ CITATION.cff | 32 +++ CMakeLists.txt | 186 ++++++++++++++ CMakePresets.json | 207 +++++++++++++++ CONTRIBUTING.md | 370 +++++++++++++++++++++++++++ GitHubRepoPublicReleaseApproval.md | 32 +++ LICENSE.md | 34 +++ README.md | 153 +++++++++++ app/CMakeLists.txt | 24 ++ app/include/CommaSeparatedIterator.h | 42 +++ app/include/Driver.h | 44 ++++ app/include/ReturnCodes.h | 32 +++ app/include/Structs.h | 17 ++ app/src/CMakeLists.txt | 40 +++ app/src/CommaSeparatedIterator.cpp | 78 ++++++ app/src/Driver.cpp | 188 ++++++++++++++ app/src/DriverUtils.cpp | 151 +++++++++++ app/src/ReturnCodes.cpp | 50 ++++ app/tests/CMakeLists.txt | 33 +++ app/tests/TempTextFile.cpp | 61 +++++ app/tests/TempTextFile.h | 37 +++ app/tests/TestDriver.cpp | 67 +++++ app/tests/TestDriver.h | 180 +++++++++++++ docs/CMakeLists.txt | 68 +++++ docs/doxy_custom.css | 44 ++++ docs/doxy_footer.html | 47 ++++ docs/doxy_header.html | 113 ++++++++ docs/doxy_mainpage.md | 33 +++ docs/images/ITSlogoOnly400.png | Bin 0 -> 14748 bytes docs/images/apple-touch-icon.png | Bin 0 -> 5838 bytes docs/images/favicon-16x16.png | Bin 0 -> 2197 bytes docs/images/favicon-32x32.png | Bin 0 -> 2450 bytes docs/images/ntia-logo-400px.png | Bin 0 -> 37216 bytes extern/doxygen-awesome-css | 1 + extern/googletest | 1 + include/PropLibTemplate.h | 57 +++++ src/CMakeLists.txt | 55 ++++ src/ReturnCodes.cpp | 81 ++++++ tests/CMakeLists.txt | 23 ++ tests/TestUtils.cpp | 46 ++++ tests/TestUtils.h | 11 + 48 files changed, 3130 insertions(+) create mode 100644 .clang-format create mode 100644 .github/workflows/cff-validator.yml create mode 100644 .github/workflows/ctest.yml create mode 100644 .github/workflows/doxygen.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .zenodo.json create mode 100644 CITATION.cff create mode 100644 CMakeLists.txt create mode 100644 CMakePresets.json create mode 100644 CONTRIBUTING.md create mode 100644 GitHubRepoPublicReleaseApproval.md create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 app/CMakeLists.txt create mode 100644 app/include/CommaSeparatedIterator.h create mode 100644 app/include/Driver.h create mode 100644 app/include/ReturnCodes.h create mode 100644 app/include/Structs.h create mode 100644 app/src/CMakeLists.txt create mode 100644 app/src/CommaSeparatedIterator.cpp create mode 100644 app/src/Driver.cpp create mode 100644 app/src/DriverUtils.cpp create mode 100644 app/src/ReturnCodes.cpp create mode 100644 app/tests/CMakeLists.txt create mode 100644 app/tests/TempTextFile.cpp create mode 100644 app/tests/TempTextFile.h create mode 100644 app/tests/TestDriver.cpp create mode 100644 app/tests/TestDriver.h create mode 100644 docs/CMakeLists.txt create mode 100644 docs/doxy_custom.css create mode 100644 docs/doxy_footer.html create mode 100644 docs/doxy_header.html create mode 100644 docs/doxy_mainpage.md create mode 100644 docs/images/ITSlogoOnly400.png create mode 100644 docs/images/apple-touch-icon.png create mode 100644 docs/images/favicon-16x16.png create mode 100644 docs/images/favicon-32x32.png create mode 100644 docs/images/ntia-logo-400px.png create mode 160000 extern/doxygen-awesome-css create mode 160000 extern/googletest create mode 100644 include/PropLibTemplate.h create mode 100644 src/CMakeLists.txt create mode 100644 src/ReturnCodes.cpp create mode 100644 tests/CMakeLists.txt create mode 100644 tests/TestUtils.cpp create mode 100644 tests/TestUtils.h 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/cff-validator.yml b/.github/workflows/cff-validator.yml new file mode 100644 index 0000000..6fce818 --- /dev/null +++ b/.github/workflows/cff-validator.yml @@ -0,0 +1,27 @@ +name: Validate CITATION.cff + +on: + push: + paths: + - 'CITATION.cff' + - '.github/workflows/cff-validator.yml' + pull_request: + paths: + - 'CITATION.cff' + - '.github/workflows/cff-validator.yml' + workflow_dispatch: + +jobs: + Validate-CITATION-cff: + runs-on: ubuntu-latest + name: Validate CITATION.cff + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Validate CITATION.cff + uses: dieghernan/cff-validator@v3 + with: + install-r: true diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml new file mode 100644 index 0000000..7e060b3 --- /dev/null +++ b/.github/workflows/ctest.yml @@ -0,0 +1,74 @@ +# This action compiles the library and driver and runs all unit tests using an OS and CMake matrix +name: Unit Tests + +on: + push: + branches: ["main", "dev"] + pull_request: + branches: ["main", "dev"] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + +# Define the matrix for different operating systems +jobs: + build-and-test: + name: ${{ matrix.os }} / ${{ matrix.architecture }} / CMake ${{ matrix.cmakeVersion }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: + - ubuntu-latest + - macos-latest # Apple + - macos-13 # Intel + - windows-latest + architecture: [arm64, x64, x86] + cmakeVersion: ["3.21", latest] # CMake >= 3.21 is required to use "--preset " and discover generators + exclude: + - os: macos-latest + architecture: x86 + - os: macos-latest + architecture: x64 + - os: macos-13 + architecture: x86 + - os: macos-13 + architecture: arm64 + - os: windows-latest + architecture: arm64 + - os: ubuntu-latest + architecture: arm64 + - os: ubuntu-latest + architecture: x86 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Clone required submodules + run: | + git submodule init extern/googletest + git submodule update + + - name: Install CMake + uses: lukka/get-cmake@latest + with: + cmakeVersion: ${{ matrix.cmakeVersion }} + + - name: "CMake: Build and Test (64-bit)" + if: matrix.architecture != 'x86' + uses: lukka/run-cmake@v10 + with: + configurePreset: release64 + configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF']" + buildPreset: release64 + testPreset: release64 + + - name: "CMake: Build and Test (32-bit)" + if: matrix.architecture == 'x86' + uses: lukka/run-cmake@v10 + with: + configurePreset: release32 + configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF']" + buildPreset: release32 + testPreset: release32 diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml new file mode 100644 index 0000000..a9287dd --- /dev/null +++ b/.github/workflows/doxygen.yml @@ -0,0 +1,72 @@ +# 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"] + push: + 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.12.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..209174b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,86 @@ +# 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: ${{ matrix.relName}}' + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: windows-latest + architecture: x64 + relName: Windows x64 + - os: windows-latest + architecture: x86 + relName: Windows x86 + - os: macos-latest + architecture: arm64 + relName: macOS Universal + - os: ubuntu-latest + architecture: x64 + relName: Linux x64 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install CMake # (latest stable version) + uses: lukka/get-cmake@latest + + - name: "CMake: Build (64-bit)" + if: matrix.architecture != 'x86' + uses: lukka/run-cmake@v10 + with: + configurePreset: release64 + configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF', '-DRUN_TESTS=OFF', '-DRUN_DRIVER_TESTS=OFF']" + buildPreset: release64 + + - name: "CMake: Build (32-bit)" + if: matrix.architecture == 'x86' + uses: lukka/run-cmake@v10 + with: + configurePreset: release32 + configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF', '-DRUN_TESTS=OFF', '-DRUN_DRIVER_TESTS=OFF', '-A', 'Win32']" + buildPreset: release32 + + - name: Upload release artifacts (binaries) + if: runner.os != 'Windows' + uses: actions/upload-artifact@v4 + with: + name: "${{ github.event.repository.name }}-release-${{ runner.os }}" + path: | + ${{ github.workspace }}/bin/*-universal.dylib + ${{ github.workspace }}/bin/*-x86_64.so + ${{ github.workspace }}/bin/*Driver*-*-Darwin-universal + ${{ github.workspace }}/bin/*Driver*-*-Linux-x86_64 + if-no-files-found: error + overwrite: true + + - name: Upload release artifact (Windows) + if: runner.os == 'Windows' + uses: actions/upload-artifact@v4 + with: + name: "${{ github.event.repository.name }}-release-${{ runner.os }}-${{ matrix.architecture }}" + path: | + ${{ github.workspace }}\bin\Release\*.dll + ${{ github.workspace }}\bin\Release\*Driver-*.exe + if-no-files-found: error + overwrite: true + + - name: Upload release artifact (C++ Header) # TODO-TEMPLATE: Specify the correct header below + if: runner.os == 'Linux' + uses: actions/upload-artifact@v4 + with: + name: "${{ github.event.repository.name }}-release-cpp-header" + path: ${{ github.workspace }}/include/PropLibTemplate.h + if-no-files-found: error + overwrite: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5fed29b --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +############ +## Windows +############ + +# Windows image file caches +Thumbs.db +*.o + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +# User-specific files +**/.vs +**/bin +**/Debug/ +**/Release/ +**/dotnet/packages +**/dotnet/nuget +*.tlog +*.obj +*.log +*.lastbuildstate +*.manifest +*.users +*.user +*.res +*.opendb +*.db +*.unsuccessfulbuild +*.ipch +*.pdb +*.exp +*.ilk +*.idb +*.opensdf +*.sdf +*.u2d +*.suo +*.aps +**/obj +**/x64 +**/x86 + +######### +## CMake +######### +CMakeLists.txt.user +CMakeCache.txt +CMakeUserPresets.json +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..2e325a1 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,7 @@ +[submodule "extern/googletest"] + path = extern/googletest + url = https://github.com/google/googletest + branch = v1.12.x +[submodule "extern/doxygen-awesome-css"] + path = extern/doxygen-awesome-css + url = https://github.com/jothepro/doxygen-awesome-css diff --git a/.zenodo.json b/.zenodo.json new file mode 100644 index 0000000..c3c6f74 --- /dev/null +++ b/.zenodo.json @@ -0,0 +1,50 @@ +{ + "upload_type": "software", + "publication_date": "TODO-TEMPLATE", + "title": "TODO-TEMPLATE", + "creators": [ + { + "name": "TODO-TEMPLATE", + "affiliation": "U.S. Department of Commerce, National Telecommunications and Information Administration, Institute for Telecommunication Sciences", + "orcid": "TODO-TEMPLATE" + } + ], + "description": "TODO-TEMPLATE. Make this the same as the abstract.", + "keywords": [ + "TODO-TEMPLATE", + "TODO-TEMPLATE" + ], + "related_identifiers": [ + { + "identifier": "https://github.com/NTIA/TODO-TEMPLATE-dotnet", + "relation": "isSupplementedBy", + "resource_type": "software" + }, + { + "identifier": "https://github.com/NTIA/TODO-TEMPLATE-matlab", + "relation": "isSupplementedBy", + "resource_type": "software" + }, + { + "identifier": "https://github.com/NTIA/TODO-TEMPLATE-python", + "relation": "isSupplementedBy", + "resource_type": "software" + }, + { + "identifier": "https://github.com/NTIA/TODO-TEMPLATE-test-data", + "relation": "isSupplementedBy", + "resource_type": "dataset" + }, + { + "identifier": "https://ntia.github.io/TODO-TEMPLATE/", + "relation": "isDocumentedBy", + "resource_type": "softwaredocumentation" + }, + { + "identifier": "https://ntia.github.io/propagation-library-wiki/models/TODO-TEMPLATE/", + "relation": "isDocumentedBy", + "resource_type": "softwaredocumentation" + } + ], + "version": "TODO-TEMPLATE" +} \ No newline at end of file diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..c5036b1 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,32 @@ +cff-version: 1.2.0 +title: >- + TODO-TEMPLATE +message: Please cite this software using these metadata. +type: software +authors: + - given-names: TODO-TEMPLATE + family-names: TODO-TEMPLATE + email: TODO-TEMPLATE@ntia.gov + affiliation: >- + U.S. Department of Commerce, National + Telecommunications and Information Administration, + Institute for Telecommunication Sciences + orcid: 'https://orcid.org/0000-0000-0000-0000' + - name: >- + U.S. Department of Commerce, National + Telecommunications and Information Administration, + Institute for Telecommunication Sciences + address: 325 Broadway + city: Boulder + country: US + post-code: '80305' + region: Colorado + alias: NTIA/ITS + email: code@ntia.gov + website: 'https://its.ntia.gov' +repository-code: 'https://github.com/NTIA/TODO-TEMPLATE' +url: 'https://ntia.github.io/propagation-library-wiki/models/TODO-TEMPLATE/' +keywords: + - propagation + - TODO-TEMPLATE +version: 1.0.0 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..cfc3851 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,186 @@ +############################################################ +## CMakeList.txt : Top-level CMake project file, do global +## configuration and include sub-projects here. +############################################################ + +# >=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.15 required for CMake features used in this project +cmake_minimum_required(VERSION 3.15 FATAL_ERROR) + +# Warn if '-A Win32' is provided but BUILD_32BIT has not been set. This may +# indicate accidental use of a 64-bit CMake preset while intending to build for 32-bit. +if (DEFINED ENV{CMAKE_GENERATOR_PLATFORM}) + if ($ENV{CMAKE_GENERATOR_PLATFORM} STREQUAL "Win32") + if (NOT BUILD_32BIT) + message(WARNING "Generator platform is Win32 but 32-bit preset is not being used.") + endif () + endif () +endif () + +# If on macOS, handle arm64/x86_64 architectures (must be done before project()) +# For other OS, identify the architecture and save it to the ARCH_SUFFIX variable. +if (APPLE) + # Get the current platform's native architecture + execute_process( + COMMAND uname -m + RESULT_VARIABLE result + OUTPUT_VARIABLE MACOS_NATIVE_ARCHITECTURE + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + # If running on Apple silicon, try a universal build. Otherwise, a native build. + if (${MACOS_NATIVE_ARCHITECTURE} STREQUAL "arm64") + set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE) + set(ARCH_SUFFIX "universal") + else () + set(ARCH_SUFFIX ${MACOS_NATIVE_ARCHITECTURE}) + endif () +elseif (WIN32) + if (BUILD_32BIT) + set(ARCH_SUFFIX "x86") + else () + set(ARCH_SUFFIX "x64") + endif () +elseif (UNIX) # Only non-Apple Linux evaluates as True here + set(ARCH_SUFFIX "x86_64") +endif () + +# Convenience function for printing visible status messages. +# Accepts arbitrary number of string inputs, each printed as a +# new line in the status message. +function(proplib_message) + string(REPLACE ";" "\n-- " all_args "${ARGN}") + message(STATUS + "==========[PROPLIB STATUS MESSAGE]==========\n" + "-- ${all_args}\n-- " + "============================================\n" + ) +endfunction() + +########################################### +## PROJECT METADATA +########################################### +# TODO-TEMPLATE: Add project metadata here. See CREATING-REPOSITORIES.md for an example. +set(LIB_NAME "PropLibTemplate") # Name of library/target +set(LIB_NAMESPACE "ITS") # Namespace for the named library +project( + "${LIB_NAMESPACE}.${LIB_NAME}" + VERSION 1.0 + DESCRIPTION "TODO-TEMPLATE: BRIEF DESCRIPTION OF YOUR SOFTWARE" + HOMEPAGE_URL "TODO-TEMPLATE: HOMEPAGE LINK, E.G. TO RELEVANT PAGE ON PROPLIB WIKI" + LANGUAGES "CXX" +) + +########################################### +## CMAKE OPTIONS AND DEFAULTS +########################################### +# Define options. Defaults to: compile 64-bit library and driver, build docs, run tests +option(BUILD_DOCS "Generate documentation site with Doxygen" ON) +option(BUILD_DRIVER "Build the command-line driver executable" ON) +option(RUN_DRIVER_TESTS "Test the command-line driver executable" ON) +option(DOCS_ONLY "Skip all steps except generating the documentation site" OFF) +option(RUN_TESTS "Run unit tests for the main library" ON) +option(BUILD_32BIT "Build project for x86/32-bit instead of x64/64-bit" OFF) + +########################################### +## SETUP +########################################### +# C++11 and some extensions (e.g., for `auto`) are the minimum required +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS ON) + +# Get 0/1 values indicating compiler type +set(gcc_like_cxx "$") +set(msvc_cxx "$") + +# Define a function to set compile options of a provided target. +function(configure_proplib_target proplib_target) + target_compile_options(${proplib_target} PRIVATE + # For GCC-like compilers in any configuration + "$<${gcc_like_cxx}:$>" + # For GCC-like compilers in Release configurations + "$<${gcc_like_cxx}:$:-O3;-DNDEBUG>>>" + # For GCC-like compilers in Debug configurations + "$<${gcc_like_cxx}:$:-g;-O0>>>" + # For GCC-like compilers in 32-bit configurations + "$<${gcc_like_cxx}:$:-m32>>>" + # For MSVC compiler in any configuration + "$<${msvc_cxx}:$>" + # For MSVC compiler in Release configurations + "$<${msvc_cxx}:$:/O2;/DNDEBUG;/MT>>>" + # For MSVC compiler in Debug configurations + "$<${msvc_cxx}:$:/Od;/Zi;/MTd>>>" + ) +endfunction() + +# 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 () + +# control where the static and shared libraries are built +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" CACHE STRING "Set the CMAKE Archive Output Directory") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" CACHE STRING "Set the CMAKE Library Output Directory") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" CACHE STRING "Set the CMAKE Runtime Output Directory") + +proplib_message( + "Initial Configuration Complete" + "Generator: ${CMAKE_GENERATOR}" + "Generator platform (for multi-config generators only): $ENV{CMAKE_GENERATOR_PLATFORM}" + "C++ Compiler: ${CMAKE_CXX_COMPILER}" + "Target architecture: ${ARCH_SUFFIX}" + "CMake Options:" + " BUILD_32BIT = ${BUILD_32BIT}" + " BUILD_DOCS = ${BUILD_DOCS}" + " BUILD_DRIVER = ${BUILD_DRIVER}" + " RUN_DRIVER_TESTS = ${RUN_DRIVER_TESTS}" + " DOCS_ONLY = ${DOCS_ONLY}" + " RUN_TESTS = ${RUN_TESTS}" +) + +########################################## +## BUILD/RUN +########################################## +if (NOT DOCS_ONLY) + add_subdirectory(src) # Configure the library + if (RUN_TESTS OR RUN_DRIVER_TESTS) + enable_testing() + include(CTest) + # Initialize GoogleTest if any tests will be configured + if (EXISTS "${PROJECT_SOURCE_DIR}/extern/googletest/CMakeLists.txt") + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + # Ensure GoogleTest is built as a static library + if (DEFINED BUILD_SHARED_LIBS AND BUILD_SHARED_LIBS) + set(BUILD_SHARED_LIBS_${LIB_NAME} ${BUILD_SHARED_LIBS}) + set(BUILD_SHARED_LIBS OFF) + endif () + add_subdirectory("${PROJECT_SOURCE_DIR}/extern/googletest" "extern/googletest" EXCLUDE_FROM_ALL) + include(GoogleTest) + # Restore initial value of BUILD_SHARED_LIBS + if (DEFINED BUILD_SHARED_LIBS_${LIB_NAME}) + set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_${LIB_NAME}}) + endif () + 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 () + if (RUN_TESTS) # Build and run unit tests + add_subdirectory(tests) + endif () + if (BUILD_DRIVER OR RUN_DRIVER_TESTS) + add_subdirectory(app) + 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..6973e97 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,207 @@ +{ + "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": { + "DOCS_ONLY": "OFF", + "RUN_TESTS": "ON", + "BUILD_32BIT": "OFF" + } + }, + { + "name": "proplib-config-release-base", + "hidden": true, + "inherits": "proplib-config-base", + "description": "Base 'Release' configuration preset for ITS PropLib libraries", + "cacheVariables": { + "CMAKE_BUILD_RPATH_USE_ORIGIN": "ON", + "BUILD_SHARED_LIBS": "ON", + "BUILD_DOCS": "ON", + "BUILD_DRIVER": "ON", + "RUN_DRIVER_TESTS": "ON" + }, + "environment": { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_CONFIGURATION_TYPES": "Release" + } + }, + { + "name": "proplib-config-debug-base", + "hidden": true, + "inherits": "proplib-config-base", + "description": "Base 'Debug' configuration preset for ITS PropLib libraries", + "cacheVariables": { + "CMAKE_BUILD_RPATH_USE_ORIGIN": "ON", + "BUILD_SHARED_LIBS": "ON", + "BUILD_DOCS": "OFF", + "BUILD_DRIVER": "OFF", + "RUN_DRIVER_TESTS": "OFF" + }, + "environment": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_CONFIGURATION_TYPES": "Debug" + } + }, + { + "name": "debug64", + "displayName": "Debug, 64-Bit", + "description": "Build library and tests with debug options. Skip building driver, driver tests, and docs.", + "inherits": "proplib-config-debug-base" + }, + { + "name": "release64", + "displayName": "Release, 64-Bit", + "description": "Build library, driver, docs and tests with release options.", + "inherits": "proplib-config-release-base" + }, + { + "name": "debug32", + "displayName": "Debug, 32-bit", + "description": "Build 32-bit library and tests with debug options. Skip building driver, driver tests, and docs.", + "inherits": "proplib-config-debug-base", + "cacheVariables": { + "BUILD_32BIT": "ON" + } + }, + { + "name": "release32", + "displayName": "Release, 32-bit", + "description": "Build 32-bit library, driver, docs and tests with release options.", + "inherits": "proplib-config-release-base", + "cacheVariables": { + "BUILD_32BIT": "ON" + } + }, + { + "name": "docsOnly", + "displayName": "Doxygen only", + "description": "Do not build or test the library or driver; only build Doxygen docs.", + "inherits": "proplib-config-base", + "cacheVariables": { + "BUILD_DOCS": "ON", + "DOCS_ONLY": "ON", + "RUN_TESTS": "OFF", + "BUILD_DRIVER": "OFF", + "RUN_DRIVER_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 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 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 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 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 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 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 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 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..169a31b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,370 @@ +# 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) +- [Testing Code](#testing-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 Branches + +Our repositories use the following approach to organize and keep track of branches. +The `main` branch typically represents the most recently released version of the software. +The `dev` branch stages changes before they are merged into `main` and a new release is created. +New features or bug fixes should be developed on individual "feature branches" with descriptive names. +When complete, features branches should merge into `dev`. + +### Git Submodules + +PropLib C++ repositories make use of Git submodules to reference certain development +dependencies, e.g. GoogleTest. Depending on the CMake preset or options used, submodules +may be required to successfully build and/or test the software. When cloning a repository, +submodules are not additionally cloned by default. Use the following commands to initialize +and clone any submodules in a repository: + +```cmd +git submodule init +git submodule update +``` + +### 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 (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 to determine +whether or not they should be merged. We may ask questions or request additional +changes which must be addressed. For example, we may request changes so that the code +meets structure, formatting, accuracy, or testing requirements. + +If your PR is approved and merged, your changes will be a part of the `dev` +branch of the repository, where they will stay until a new release is made. At that +point, `dev` will merge into `main` and a new release will be created. The maintainers +of a repository hold the authority on when a new release should be created. For example, +important bug fixes may take higher priority, while small improvements may stay on `dev` +for a while. Rest assured, even if a new release is not immediately made, your approved +changes will be always 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 (with +[GoogleTest](https://github.com/google/googletest) and +[CTest](https://cmake.org/cmake/help/latest/manual/ctest.1.html)), 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 +app/ # The command-line driver which can run the library + data/ # Example input and output files for use with the driver + include/ # Headers used by the command-line driver + src/ # Source code for the command-line driver + tests/ # Header and source files for testing the command-line driver + CMakeLists.txt # Configuration for the command-line driver and its tests + README.md # Usage information for the command-line driver +docs/ + CMakeLists.txt # Doxygen configuration + ... # Static files (images, HTML, CS, Markdown) used by Doxygen +extern/ + ... # External Git submodules/dependencies +include/ + .h # Library interface header file goes here, e.g. "ITM.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. Files containing tests must be included here. +CMakeLists.txt # Top-level CMakeLists.txt: project metadata and options +CMakePresets.json # Presets for CMake, e.g. "release64", "debug32", etc. +... +``` + +### CMake Options and CMake Presets + +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. The +top-level `CMakeLists.txt` stores the primary project configuration and includes +the lower-level configurations based on the preset or specified CMake options. + +The following CMake options are used for top-level project configuration: + +| Option | Default | Definition | +|--------------------|---------|------------------------------------------| +| `BUILD_DOCS` | `ON` | Generate documentation site with Doxygen | +| `BUILD_DRIVER` | `ON` | Build the command-line driver executable | +| `RUN_DRIVER_TESTS` | `ON` | Test the command-line driver executable | +| `DOCS_ONLY` | `OFF` | Skip all steps _except_ generating the documentation site | +| `RUN_TESTS` | `ON` | Run unit tests for the main library | + +[CMake Presets](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html) are +provided to support common build configurations. These are specified in the +`CMakePresets.json` file. The `release` preset will compile the library and driver +with optimizations, build the documentation site, and run all unit tests. The `debug` preset +will skip building the documentation site, driver, and driver tests, which can be useful for +rapid development and testing. Additionally, the Debug configuration will attempt to pass +debug flags to the compiler. Finally, the "docsOnly" preset skips all steps except for +generating the Doxygen documentation site. + +| Option | `release` preset | `debug` preset | `docsOnly` preset | +|--------------------|------------------|----------------|-------------------| +| `DOCS_ONLY` | `OFF` | `OFF` | `ON` | +| `RUN_TESTS` | `ON` | `ON` | `OFF` | +| `CMAKE_BUILD_TYPE` | `Release` | `Debug` | not set | +| `BUILD_DOCS` | `ON` | `OFF` | `ON` | +| `BUILD_DRIVER` | `ON` | `OFF` | `OFF` | +| `RUN_DRIVER_TESTS` | `ON` | `OFF` | `OFF` | + +Below are some examples of how CMake can be called to compile this software. + +```bash +# Configure and compile in 64-bit release configuration +cmake --preset release64 +cmake --build --preset release64 + +# Use the 64-bit release configuration but don't build Doxygen docs +cmake --preset release64 -DBUILD_DOCS=OFF +cmake --build --preset release64 + +# Configure and compile in 32-bit debug configuration +cmake --preset debug32 +cmake --build --preset debug32 + +# Use the 64-bit release configuration but don't run driver tests +cmake --preset release64 -DRUN_DRIVER_TESTS=OFF +cmake --build --preset release64 +``` + +### 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. Except for inline documentation, +use the JavaDoc banner style [described by Doxygen](https://www.doxygen.nl/manual/docblocks.html) + +```cpp +constexpr double = PI 3.1415; /**< Inline format, e.g. for constants */ + +/******************************************************************************* + * 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$. This whole documentation block + * is using the JavaDoc banner style! + * + * @param[in] x The input and its expected units + * @return The result @f$ y = 2x @f$ + ******************************************************************************/ +double doubleTheInput(double x) +{ + return 2 * x; +} +``` + +### Doxygen for C++ Libraries + +The base C++ libraries include Doxygen configurations which generate static +websites from code comments. These documentation sites are published as developer +reference documentation using GitHub Pages. When building the Doxygen site locally, +The site is generated in `docs/html/` and the main page can be accessed at `docs/html/index.html`. +When new releases are made, GitHub Actions workflows are triggered which build and deploy +the Doxygen site to GitHub Pages. + +### MATLAB Wrappers + +MATLAB® wrappers are implemented as toolboxes which interface with the shared library +compiled from C++ source code. The project structure is informed by the best practices +provided by MathWorks® in their [`toolboxdesign` repository](https://github.com/mathworks/toolboxdesign). +Here is an example of how a function may be documented in a MATLAB wrapper. Note the +documentation with code, where input and output arguments are provided for autocompletion. + +```matlab +function y = DoubleTheInput(x) +% DoubleTheInput - produces an output which is twice its input. +% +% Syntax: +% y = DoubleTheInput(x) +% +% Input Arguments: +% x (double) - A number which needs doubling +% +% Output Arguments: +% y (double) - The result, 2*x +% +% Description: +% Functions more complex than this one may warrant an additional, +% longer description. +arguments (Input) + x double +end +arguments (Output) + y double +end +... +``` + +### 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 +``` + +### .NET Wrappers + +PropLib .NET wrappers are written in C# and documentation comments are written in +[XML format](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/documentation-comments) +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; + } +} +``` + +## Testing Code + +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`. + +After compiling the library, you can run unit tests as follows. First, change your +working directory to the `build` directory, then run: + +```bash +ctest +``` diff --git a/GitHubRepoPublicReleaseApproval.md b/GitHubRepoPublicReleaseApproval.md new file mode 100644 index 0000000..d435f83 --- /dev/null +++ b/GitHubRepoPublicReleaseApproval.md @@ -0,0 +1,32 @@ +# GitHub Repository Public Release Approval + +**Project Name:** NTIA/OSM Research and Development + +**Software Name:** TODO-TEMPLATE + +The project identified above, which is contained within the repository this +document is stored in, has met the following criteria for public release: + +1. [ ] 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. + * [ ] Unit tests are available and the software has been tested against the unit tests. + * [ ] The software can be compiled and/or used on Windows, macOS, and Linux. + * [ ] The repository structure and contents follow from the ITS PropLib template, and + all template or placeholder code has been removed. + * [ ] The repository includes the appropriate `LICENSE.md` file +2. [ ] Any test data necessary for the code and its unit tests to function is included in this +GitHub repository, either directly or as a linked Git submodule. +3. [ ] The README.md file has passed editorial review by the ITS Publications Office. +4. [ ] 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. [ ] 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, and then create a pull request +for that branch. The following must log in to GitHub and approve that pull request +before the pull request can be merged and this repo made public: + +* Project Lead: TODO-TEMPLATE +* Supervising Division Chief or Release Authority: TODO-TEMPLATE diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..add8ca7 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,34 @@ +# SOFTWARE DISCLAIMER / RELEASE + +This software was developed by employees of the National Telecommunications and Information +Administration (NTIA), an agency of the Federal Government and is provided to you +as a public service. Pursuant to Title 15 United States Code Section 105, works +of NTIA employees are not subject to copyright protection within the United States. + +The software is provided by NTIA “AS IS.” NTIA MAKES NO WARRANTY OF ANY KIND, EXPRESS, +IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT AND DATA ACCURACY. NTIA does +not warrant or make any representations regarding the use of the software or the +results thereof, including but not limited to the correctness, accuracy, reliability +or usefulness of the software. + +To the extent that NTIA holds rights in countries other than the United States, +you are hereby granted the non-exclusive irrevocable and unconditional right to +print, publish, prepare derivative works and distribute the NTIA software, in any +medium, or authorize others to do so on your behalf, on a royalty-free basis throughout +the World. + +You may improve, modify, and create derivative works of the software or any portion +of the software, and you may copy and distribute such modifications or works. Modified +works should carry a notice stating that you changed the software and should note +the date and nature of any such change. + +You are solely responsible for determining the appropriateness of using and distributing +the software and you assume all risks associated with its use, including but not +limited to the risks and costs of program errors, compliance with applicable laws, +damage to or loss of data, programs or equipment, and the unavailability or interruption +of operation. This software is not intended to be used in any situation where a failure +could cause risk of injury or damage to property. + +Please provide appropriate acknowledgments of NTIA’s creation of the software in +any copies or derivative works of this software. diff --git a/README.md b/README.md new file mode 100644 index 0000000..affa347 --- /dev/null +++ b/README.md @@ -0,0 +1,153 @@ +# NTIA/ITS Propagation Library Template Project # + + +[![NTIA/ITS PropLib][proplib-badge]][proplib-link] + +[proplib-badge]: https://img.shields.io/badge/PropLib-badge?label=%F0%9F%87%BA%F0%9F%87%B8%20NTIA%2FITS&labelColor=162E51&color=D63E04 +[proplib-link]: https://ntia.github.io/propagation-library-wiki +[gh-actions-test-badge]: https://img.shields.io/github/actions/workflow/status/NTIA/proplib-template/ctest.yml?branch=main&logo=cmake&label=Build%2FTests&labelColor=162E51 +[gh-actions-test-link]: https://github.com/NTIA/proplib-template/actions/workflows/ctest.yml +[gh-actions-docs-badge]: https://img.shields.io/github/actions/workflow/status/NTIA/proplib-template/doxygen.yml?branch=main&logo=c%2B%2B&label=Docs&labelColor=162E51 +[gh-pages-docs-link]: https://ntia.github.io/proplib-template +[gh-releases-badge]: https://img.shields.io/github/v/release/NTIA/proplib-template?logo=github&label=Release&labelColor=162E51&color=D63E04 +[gh-releases-link]: https://github.com/NTIA/proplib-template/releases +[gh-issues-badge]: https://img.shields.io/github/issues/NTIA/proplib-template?logo=github&label=Issues&labelColor=162E51 +[gh-issues-link]: https://github.com/NTIA/proplib-template/issues +[doi-badge]: https://zenodo.org/badge/TODO-TEMPLATE.svg +[doi-link]: https://zenodo.org/badge/latestdoi/TODO-TEMPLATE + + +This code repository is a template repository for software in the NTIA/ITS +Propagation Library (PropLib). This template is intended for developers wishing +to develop a cross-platform C++ library as part of PropLib. Instructions on how +to use this repository are found in its [GitHub Wiki](https://github.com/NTIA/proplib-template/wiki). + +Additional template repositories exist for building .NET, MATLAB, and Python +wrappers for PropLib C++ libraries. Finally, another template is available as +an example of a submodule repository to provide common test data to all versions +of the software. See: + +- [NTIA/proplib-template-dotnet](https://github.com/NTIA/proplib-template-dotnet) +- [NTIA/proplib-template-matlab](https://github.com/NTIA/proplib-template-matlab) +- [NTIA/proplib-template-python](https://github.com/NTIA/proplib-template-python) +- [NTIA/proplib-template-test-data](https://github.com/NTIA/proplib-template-test-data) + +## Contact ## + +For questions about using this template repository, contact + + diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt new file mode 100644 index 0000000..388d4cc --- /dev/null +++ b/app/CMakeLists.txt @@ -0,0 +1,24 @@ +########################################### +## CONFIGURE THE COMMAND LINE DRIVER +########################################### +set(DRIVER_NAME "${LIB_NAME}Driver") +set(DRIVER_VERSION ${PROJECT_VERSION}.0) +set(DRIVER_HEADERS "${PROJECT_SOURCE_DIR}/app/include") +set(DRIVER_TEST_NAME "${DRIVER_NAME}Test") + +########################################### +## BUILD THE COMMAND LINE DRIVER +########################################### +proplib_message("Configuring command line driver ${DRIVER_NAME}") +add_subdirectory(src) +proplib_message("Done configuring command line driver ${DRIVER_NAME}") + +########################################### +## BUILD AND RUN THE DRIVER TESTS +########################################### +if (RUN_DRIVER_TESTS) + proplib_message("Configuring command line driver tests ${DRIVER_TEST_NAME}") + set_target_properties(${DRIVER_NAME} PROPERTIES ENABLE_EXPORTS ON) + add_subdirectory(tests) + proplib_message("Done configuring command line driver tests ${DRIVER_TEST_NAME}") +endif() diff --git a/app/include/CommaSeparatedIterator.h b/app/include/CommaSeparatedIterator.h new file mode 100644 index 0000000..a83a0ac --- /dev/null +++ b/app/include/CommaSeparatedIterator.h @@ -0,0 +1,42 @@ +/** @file CommaSeparatedIterator.h + * Iterator class for reading comma-delimited input text streams. + */ +#pragma once + +#include // for std::istream +#include // For std::string +#include // for std::pair + +/******************************************************************************* + * @class CommaSeparatedIterator + * An iterator that reads lines from an input stream, splitting each line + * into two strings based on a comma delimiter. + * + * This iterator can work with both `std::stringstream` and `std::ifstream`. + ******************************************************************************/ +class CommaSeparatedIterator { + public: + /** Type alias for the value returned by the iterator (pair of strings) */ + using value_type = std::pair; + + /*********************************************************************** + * Constructor method + * + * @param[in] stream The input stream which will be read + **********************************************************************/ + CommaSeparatedIterator(std::istream &stream); + + /** Pre-increment operator to advance the iterator to the next line */ + CommaSeparatedIterator &operator++(); + + /** Dereference operator to obtain the current pair of substrings */ + value_type operator*() const; + + /** Conversion to boolean to check if the iterator is valid */ + explicit operator bool() const; + private: + std::istream &stream_; /**< Reference to the input stream */ + std::string line_; /**< Current line read from the stream */ + std::string first_; /**< First string from the current line */ + std::string second_; /**< Second string from the current line */ +}; diff --git a/app/include/Driver.h b/app/include/Driver.h new file mode 100644 index 0000000..4f5645c --- /dev/null +++ b/app/include/Driver.h @@ -0,0 +1,44 @@ +/** @file Driver.h + * Interface header for this driver executable + */ +#pragma once + +#include "CommaSeparatedIterator.h" +#include "ReturnCodes.h" +#include "Structs.h" + +// TODO-TEMPLATE: Include your library's main interface header +#include "PropLibTemplate.h" + +#include // for std::left, std::setw +#include // for std::cout +#include // for std::endl, std::ostream +#include // for std::string + +///////////////////////////// +// Macros + +/** Shortcut for concise print-to-file statements in driver */ +#define PRINT << std::endl << std::left << std::setw(25) << +/** Shortcut for setting fixed whitespace padding in driver file output */ +#define SETW13 << std::setw(13) << + +////////////////////////////// +// Library Namespace +// TODO-TEMPLATE: use the namespace of your library +using namespace ITS; + +///////////////////////////// +// Functions +void Help(std::ostream &os = std::cout); +DrvrReturnCode ParseArguments(int argc, char **argv, DrvrParams ¶ms); +DrvrReturnCode ValidateInputs(const DrvrParams ¶ms); + +// Driver Utils +std::string GetDatetimeString(); +DrvrReturnCode ParseBoolean(const std::string &str, bool &value); +DrvrReturnCode ParseDouble(const std::string &str, double &value); +DrvrReturnCode ParseInteger(const std::string &str, int &value); +void PrintLabel(std::ostream &os, const std::string &lbl); +void StringToLower(std::string &str); +void Version(std::ostream &os = std::cout); \ No newline at end of file diff --git a/app/include/ReturnCodes.h b/app/include/ReturnCodes.h new file mode 100644 index 0000000..5866bba --- /dev/null +++ b/app/include/ReturnCodes.h @@ -0,0 +1,32 @@ +/** @file ReturnCodes.h + * Defines return codes for the driver + */ +#pragma once + +#include // for std::string + +// TODO-TEMPLATE: Add driver return codes here and corresponding entries in app/src/ReturnCodes.cpp + +/******************************************************************************* + * Return Codes defined by this driver software (128-255) + ******************************************************************************/ +// clang-format off +enum DrvrReturnCode { + // Primary Return Codes + DRVR__SUCCESS = 128, /**< Successful execution */ + DRVR__RETURN_SUCCESS, /**< Indicates driver should exit successfully */ + DRVRERR__MISSING_OPTION, /**< No value provided for given argument */ + DRVRERR__INVALID_OPTION, /**< Unknown option specified */ + DRVRERR__OPENING_INPUT_FILE, /**< Failed to open the input file for reading */ + DRVRERR__OPENING_OUTPUT_FILE, /**< Failed to open the output file for writing */ + + // Input File Parsing Errors + DRVRERR__PARSE = 160, /**< Failed parsing inputs; unknown parameter */ + + // Validation Errors + DRVRERR__VALIDATION_IN_FILE = 192, /**< Input file not specified */ + DRVRERR__VALIDATION_OUT_FILE, /**< Output file not specified */ +}; +// clang-format on + +std::string GetDrvrReturnStatusMsg(int code); diff --git a/app/include/Structs.h b/app/include/Structs.h new file mode 100644 index 0000000..81409a6 --- /dev/null +++ b/app/include/Structs.h @@ -0,0 +1,17 @@ +/** @file Structs.h + * Contains data structures and type macros used by this software +*/ +#pragma once + +#include // for std::string + +///////////////////////////// +// Data Structures + +// TODO-TEMPLATE: Basic struct provided to hold input/output file names and DBG flag +/** Parameters provided to the command line driver */ +struct DrvrParams { + std::string in_file = ""; /**< Input file */ + std::string out_file = ""; /**< Output file */ + bool DBG = false; /**< Dump intermediate values to file? */ +}; \ No newline at end of file diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt new file mode 100644 index 0000000..b839aa9 --- /dev/null +++ b/app/src/CMakeLists.txt @@ -0,0 +1,40 @@ +########################################### +## BUILD THE DRIVER +########################################### +## TODO-TEMPLATE: Include source AND header files here. Do not include +## source/header files already included in `add_library()` in `src/CMakeLists.txt` +add_executable( + ${DRIVER_NAME} + "CommaSeparatedIterator.cpp" + "Driver.cpp" + "DriverUtils.cpp" + "ReturnCodes.cpp" + "${DRIVER_HEADERS}/CommaSeparatedIterator.h" + "${DRIVER_HEADERS}/Driver.h" + "${DRIVER_HEADERS}/ReturnCodes.h" + "${DRIVER_HEADERS}/Structs.h" +) + +# Add the include directory +target_include_directories(${DRIVER_NAME} PUBLIC "${DRIVER_HEADERS}") + +# Link the library to the executable +target_link_libraries(${DRIVER_NAME} ${LIB_NAME}) + +configure_proplib_target(${DRIVER_NAME}) + +# Add definitions to enable version identification inside the driver +add_compile_definitions( + LIBRARY_VERSION="${PROJECT_VERSION}" + DRIVER_VERSION="${DRIVER_VERSION}" + LIBRARY_NAME="${LIB_NAME}" + DRIVER_NAME="${DRIVER_NAME}" +) + +# Set some target metadata +set_target_properties( + ${DRIVER_NAME} PROPERTIES + OUTPUT_NAME ${DRIVER_NAME}-${DRIVER_VERSION}-${CMAKE_SYSTEM_NAME}- + DEBUG_POSTFIX ${ARCH_SUFFIX} + RELEASE_POSTFIX ${ARCH_SUFFIX} +) \ No newline at end of file diff --git a/app/src/CommaSeparatedIterator.cpp b/app/src/CommaSeparatedIterator.cpp new file mode 100644 index 0000000..022a952 --- /dev/null +++ b/app/src/CommaSeparatedIterator.cpp @@ -0,0 +1,78 @@ +/** @file CommaSeparatedIterator.cpp + * Implementation of class to read comma-delimited input text streams. +*/ +#include "CommaSeparatedIterator.h" + +#include "Driver.h" + +#include // for std::size_t +#include // for std::istream +#include // for std::runtime_error +#include // for std::getline, std::string + +CommaSeparatedIterator::CommaSeparatedIterator(std::istream &stream): + stream_(stream) { + ++(*this); // Move to the first line +} + +/*********************************************************************** + * Pre-increment operator. + * + * Advances the iterator to the next line and splits it into two substrings. + * If the end of the stream is reached, both substrings will be empty. Both + * parsed substrings are converted to lowercase. + * + * @return A reference to the updated iterator. + **********************************************************************/ +CommaSeparatedIterator &CommaSeparatedIterator::operator++() { + if (std::getline(stream_, line_)) { + // Skip line if empty + if (line_.empty()) { + return ++(*this); + } + + // Parse line by comma delimiter + std::size_t pos = line_.find(','); + if (pos != std::string::npos) { + first_ = line_.substr(0, pos); + second_ = line_.substr(pos + 1); + } else { + first_ = line_; + second_ = ""; + } + + // Convert both substrings to lowercase + StringToLower(first_); + StringToLower(second_); + } else { + if (stream_.bad()) { + throw std::runtime_error("Error reading stream."); + } + // End of stream reached + first_ = second_ = ""; + } + + return *this; +} + +/*********************************************************************** + * Dereference operator. + * + * Returns the current pair of substrings (first and second). + * + * @return A pair containing the two substrings from the current line. + **********************************************************************/ +CommaSeparatedIterator::value_type CommaSeparatedIterator::operator*() const { + return {first_, second_}; +} + +/*********************************************************************** + * Conversion to boolean. + * + * Checks if the iterator is still valid (not at the end of the input). + * + * @return True if there are still lines to read, otherwise false. + **********************************************************************/ +CommaSeparatedIterator::operator bool() const { + return stream_.good() || !first_.empty() || !second_.empty(); +} \ No newline at end of file diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp new file mode 100644 index 0000000..e9162e8 --- /dev/null +++ b/app/src/Driver.cpp @@ -0,0 +1,188 @@ +/** @file Driver.cpp + * Implements the main function of the executable, and other high-level functions + */ +#include "Driver.h" + +#include // for std::find +#include // for std::ifstream, std::ofstream +#include // for std::setw +#include // for std::left +#include // for std::cerr +#include // for std::endl +#include // for std::string +#include // for std::vector + +/******************************************************************************* + * Main function of the driver executable + * + * @param[in] argc Number of arguments entered on the command line + * @param[in] argv Array containing the provided command-line arguments + * @return Return code + ******************************************************************************/ +int main(int argc, char **argv) { + int rtn; + DrvrParams params; + + // Parse command line arguments + rtn = ParseArguments(argc, argv, params); + if (rtn == DRVR__RETURN_SUCCESS) { + return SUCCESS; + } else if (rtn != DRVR__SUCCESS) { + Help(); + return rtn; + } + + // Ensure required options were provided + rtn = ValidateInputs(params); + if (rtn != DRVR__SUCCESS) { + Help(); + return rtn; + } + + // TODO-TEMPLATE: Add driver logic, e.g. validating inputs and calling the model + + // TODO-TEMPLATE this code block exists for a unit test to pass. Similar logic + // should be added to functions which parse input files. + std::ifstream file(params.in_file); + if (!file) { + std::cerr << "Failed to open file " << params.in_file << std::endl; + return DRVRERR__OPENING_INPUT_FILE; + } + + // Return driver error code if one was returned + if (rtn > DRVR__RETURN_SUCCESS) { + std::cerr << GetDrvrReturnStatusMsg(rtn) << std::endl; + return rtn; + } + + // Open output file for writing + std::ofstream fp(params.out_file); + if (!fp) { + std::cerr << "Error opening output file. Exiting." << std::endl; + return DRVRERR__OPENING_OUTPUT_FILE; + } + + // Print generator information to file + fp << std::left << std::setw(25) << "Model" << LIBRARY_NAME; + fp PRINT "Library Version" << "v" << LIBRARY_VERSION; + fp PRINT "Driver Version" << "v" << DRIVER_VERSION; + fp PRINT "Date Generated" << GetDatetimeString(); + fp PRINT "Input Arguments"; + for (int i = 1; i < argc; i++) { + fp << argv[i] << " "; + } + fp << std::endl << std::endl; + // TODO-TEMPLATE populate the rest of the output file + fp.close(); + return SUCCESS; +} + +/******************************************************************************* + * Parse the command line arguments + * + * @param[in] argc Number of arguments + * @param[in] argv Command line arguments + * @param[out] params Structure with user input params + * @return Return code + ******************************************************************************/ +DrvrReturnCode ParseArguments(int argc, char **argv, DrvrParams ¶ms) { + // TODO-TEMPLATE: Populate vector with all valid arguments + const std::vector validArgs + = {"-i", "-o", "-dbg", "-h", "--help", "-v", "--version"}; + + for (int i = 1; i < argc; i++) { + // Parse arg to lowercase string + std::string arg(argv[i]); + StringToLower(arg); + + // Check if provided flag is valid + if (std::find(validArgs.begin(), validArgs.end(), arg) + == validArgs.end()) { + // Invalid argument provided + std::cerr << "Unknown option: " << argv[i] << std::endl; + return DRVRERR__INVALID_OPTION; + } + + // Handle simple flags which don't have associated values (e.g., "-v", "-DBG") + if (arg == "-v" || arg == "--version") { + Version(); + return DRVR__RETURN_SUCCESS; + } else if (arg == "-h" || arg == "--help") { + Help(); + return DRVR__RETURN_SUCCESS; + // TODO-TEMPLATE handle any model input flags here + } else if (arg == "-dbg") { + params.DBG = true; + continue; + } + + // Check if end of arguments reached or next argument is another flag + if (i + 1 >= argc || argv[i + 1][0] == '-') { + std::cerr << "Error: no value given for " << arg << std::endl; + return DRVRERR__MISSING_OPTION; + } + + // TODO-TEMPLATE: Handle inputs which provide values (e.g. "-i in.txt"). + // Template code will set in_file and out_file in DrvrParams based on -i + // and -o options. It will also set DrvrParams.DBG based on a -dbg flag + if (arg == "-i") { + params.in_file = argv[i + 1]; + i++; + } else if (arg == "-o") { + params.out_file = argv[i + 1]; + i++; + } + } + + return DRVR__SUCCESS; +} + +/******************************************************************************* + * Print help instructions to the terminal + * + * @param[in] os Output stream for writing; defaults to `std::cout` + ******************************************************************************/ +void Help(std::ostream &os) { + // TODO-TEMPLATE: Update driver help message + os << std::endl << "Usage: .\\ [Options]" << std::endl; + os << "Options (not case sensitive)" << std::endl; + os << "\t-i :: Input file name" << std::endl; + os << "\t-t :: Terrain file name" << std::endl; + os << "\t-o :: Output file name" << std::endl; + os << "\t-dbg :: Dump intermediate values to output file [optional]" + << std::endl; + os << std::endl << "Examples:" << std::endl; + os << "\t[WINDOWS] " << DRIVER_NAME << ".exe -i in.txt -o out.txt" + << std::endl; + os << "\t[LINUX] .\\" << DRIVER_NAME << " -i in.txt -o out.txt" + << std::endl; + os << "Other Options (which don't run the model)" << std::endl; + os << "\t-h :: Display this help message" << std::endl; + os << "\t-v :: Display program version information" << std::endl; + os << std::endl; +}; + +/******************************************************************************* + * Validate that required inputs are present for the mode specified by the user. + * + * This function DOES NOT check the validity of the parameter values, only that + * required parameters have been specified by the user + * + * @param[in] params Structure with user input parameters + * @return Return code + ******************************************************************************/ +DrvrReturnCode ValidateInputs(const DrvrParams ¶ms) { + DrvrParams not_set; + DrvrReturnCode rtn = DRVR__SUCCESS; + // TODO-TEMPLATE: Check that required inputs were provided. + // This template code checks that input/output files were given with -i and -o + if (params.in_file == not_set.in_file) + rtn = DRVRERR__VALIDATION_IN_FILE; + if (params.out_file == not_set.out_file) + rtn = DRVRERR__VALIDATION_OUT_FILE; + + if (rtn != DRVR__SUCCESS) + std::cerr << GetDrvrReturnStatusMsg(rtn) << std::endl; + + return rtn; +} diff --git a/app/src/DriverUtils.cpp b/app/src/DriverUtils.cpp new file mode 100644 index 0000000..fe9f69b --- /dev/null +++ b/app/src/DriverUtils.cpp @@ -0,0 +1,151 @@ +/** @file DriverUtils.cpp + * Implements various model-agnostic utility functions for the driver + */ +#include "Driver.h" + +#ifdef _WIN32 + // Ensure localtime_s is available on Windows + #ifndef __STDC_LIB_EXT1__ + #define __STDC_LIB_EXT1__ + #endif + #ifndef __STDC_WANT_LIB_EXT1__ + #define __STDC_WANT_LIB_EXT1__ 1 + #endif +#endif + +#include // for std::transform +#include // for std::tolower +#include // for std::size_t +#include // for localtime_{s,r}, std::{time, time_t, tm, strftime} +#include // for std::setfill, std::setw +#include // for std::cerr, std::endl +#include // for std::ostream +#include // for std::stod, std::stoi, std::string + +/****************************************************************************** + * Get a string containing the current date and time information. + * + * @return A localized standard date and time string (locale dependent) + ******************************************************************************/ +std::string GetDatetimeString() { + std::time_t now = std::time(nullptr); + struct std::tm localTime; + +#ifdef _WIN32 + localtime_s(&localTime, &now); +#else + if (localtime_r(&now, &localTime) == nullptr) { + return "Date and time unknown"; + } +#endif + char mbstr[100]; + if (std::strftime(mbstr, sizeof(mbstr), "%a %b %d %H:%M:%S %Y", &localTime) + == 0) { + return "Could not format datetime string"; + } + return std::string(mbstr); +} + +/******************************************************************************* + * Parse a boolean value read from the input parameter file. + * + * Supports either "true" or "false" (case-insensitive) or "0" or "1" + * + * @param[in] str Input file value as string + * @param[out] value Input file value converted to bool + * @return Return code + ******************************************************************************/ +DrvrReturnCode ParseBoolean(const std::string &str, bool &value) { + try { + std::string str_lower = str; + StringToLower(str_lower); + if (str_lower == "0" || str_lower == "false") { + value = false; + } else if (str_lower == "1" || str_lower == "true") { + value = true; + } else { + return DRVRERR__PARSE; + } + } catch (...) { + return DRVRERR__PARSE; + } + return DRVR__SUCCESS; +} + +/******************************************************************************* + * Parse a double value read from the input parameter file + * + * @param[in] str Input file value as string + * @param[out] value Input file value converted to double + * @return Return code + ******************************************************************************/ +DrvrReturnCode ParseDouble(const std::string &str, double &value) { + try { + value = std::stod(str); + } catch (...) { + // error parsing the input string value + return DRVRERR__PARSE; + } + + return DRVR__SUCCESS; +} + +/******************************************************************************* + * Parse an integer value read from the input parameter file + * + * @param[in] str Input file value as string + * @param[out] value Input file value converted to int + * @return Return code + ******************************************************************************/ +DrvrReturnCode ParseInteger(const std::string &str, int &value) { + try { + std::size_t pos; + value = std::stoi(str, &pos, 10); + + // Verify the entire string was parsed + if (pos != str.size()) { + return DRVRERR__PARSE; + } + } catch (...) { + // error parsing the input string value + return DRVRERR__PARSE; + }; + + return DRVR__SUCCESS; +} + +/******************************************************************************* + * Helper function to standardize printing of text labels to file + * + * @param[in] os Output stream for writing + * @param[in] lbl Text message + ******************************************************************************/ +void PrintLabel(std::ostream &os, const std::string &lbl) { + os << "[" << lbl << "]"; +} + + +/****************************************************************************** + * Convert a string to lowercase. + * + * @param[in, out] str The string to convert + ******************************************************************************/ +void StringToLower(std::string &str) { + std::transform(str.begin(), str.end(), str.begin(), [](const char c) { + return static_cast(std::tolower(c)); + }); +} + +/******************************************************************************* + * Print version information to the specified output stream + * + * @param[in] os Output stream for writing; defaults to `std::cout` + ******************************************************************************/ +void Version(std::ostream &os) { + os << std::setfill('*') << std::setw(55) << "" << std::endl; + os << "Institute for Telecommunication Sciences - Boulder, CO" << std::endl; + os << "\tDriver Version: " << DRIVER_VERSION << std::endl; + os << "\t" << LIBRARY_NAME << " Version: " << LIBRARY_VERSION << std::endl; + os << "Time: " << GetDatetimeString() << std::endl; + os << std::setfill('*') << std::setw(55) << "" << std::endl; +} diff --git a/app/src/ReturnCodes.cpp b/app/src/ReturnCodes.cpp new file mode 100644 index 0000000..9f2792a --- /dev/null +++ b/app/src/ReturnCodes.cpp @@ -0,0 +1,50 @@ +/** @file ReturnCodes.cpp + * Maps status message strings to driver return codes. + */ + +#include "ReturnCodes.h" + +#include // for std::string +#include // for std::unordered_map + +/******************************************************************************* + * Get an error message string from a return code. + * + * @param[in] code Driver return code. + * @return A status message corresponding to the input code. + ******************************************************************************/ +std::string GetDrvrReturnStatusMsg(int code) { + static const std::unordered_map messages = { + {DRVR__SUCCESS, "Successful execution"}, + {DRVR__RETURN_SUCCESS, "Internal driver success"}, + {DRVRERR__MISSING_OPTION, "No value provided for given argument"}, + {DRVRERR__INVALID_OPTION, "Unknown option specified"}, + {DRVRERR__OPENING_INPUT_FILE, + "Failed to open the input file for reading"}, + {DRVRERR__OPENING_OUTPUT_FILE, + "Failed to open the output file for writing"}, + {DRVRERR__PARSE, "Failed parsing inputs; unknown parameter"}, + {DRVRERR__VALIDATION_IN_FILE, + "Option -i is required but was not provided"}, + {DRVRERR__VALIDATION_OUT_FILE, + "Option -o is required but was not provided"}, + }; + + // Construct status message + std::string msg = DRIVER_NAME; + msg += " v"; + msg += DRIVER_VERSION; + if (code == DRVR__SUCCESS) { + msg += " Status: "; + } else { + msg += " Error: "; + } + + auto it = messages.find(static_cast(code)); + if (it != messages.end()) { + msg += it->second; + } else { + msg += "Undefined return code"; + } + return msg; +} diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt new file mode 100644 index 0000000..8a9f4ef --- /dev/null +++ b/app/tests/CMakeLists.txt @@ -0,0 +1,33 @@ +############################################ +## CONFIGURE COMMAND LINE DRIVER TESTS +############################################ +## TODO-TEMPLATE: Include source AND header files for tests here. +add_executable( + ${DRIVER_TEST_NAME} + "TempTextFile.cpp" + "TestDriver.cpp" + "TempTextFile.h" + "TestDriver.h" + "${DRIVER_HEADERS}/Driver.h" +) + +# Add the include directories +target_include_directories( + ${DRIVER_TEST_NAME} PUBLIC + "${DRIVER_HEADERS}" + "${PROJECT_SOURCE_DIR}/app/tests" +) + +# Link the library to the executable +target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME}) + +# Make driver executable location available to source +add_compile_definitions(DRIVER_LOCATION="$") + +########################################### +## SET UP AND DISCOVER TESTS +########################################### +include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) +target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME} GTest::gtest_main) +include(GoogleTest) +gtest_discover_tests(${DRIVER_TEST_NAME}) diff --git a/app/tests/TempTextFile.cpp b/app/tests/TempTextFile.cpp new file mode 100644 index 0000000..f814e3d --- /dev/null +++ b/app/tests/TempTextFile.cpp @@ -0,0 +1,61 @@ +/** @file TempTextFile.cpp + * Implements a class to create and write to temporary text files + */ +#include "TempTextFile.h" + +#ifdef _WIN32 + // Ensure tmpnam_s is available on Windows + #ifndef __STDC_LIB_EXT1__ + #define __STDC_LIB_EXT1__ + #endif + #ifndef __STDC_WANT_LIB_EXT1__ + #define __STDC_WANT_LIB_EXT1__ 1 + #endif + #include // for L_tmpnam_s, tmpnam_s +#else // macOS and Linux + #include // for mkstemp + #include // for close +#endif + +#include // for std::remove +#include // for std::ofstream +#include // for std::cerr, std::ios::trunc +#include // for std::endl +#include // for std::runtime_error +#include // for std::string + +TempTextFile::TempTextFile(const std::string &content) { +#ifdef _WIN32 + // Generate and store a temporary file name + char tempFileName[L_tmpnam_s]; + if (tmpnam_s(tempFileName, sizeof(tempFileName)) != 0) { + throw std::runtime_error("Failed to create temporary file name."); + } +#else + // Safer implementation for POSIX platforms + char tempFileName[] = "/tmp/proplib-tempfile.XXXXXX"; + int fd = mkstemp(tempFileName); + if (fd == -1) { + throw std::runtime_error("Failed to create temporary file."); + } + close(fd); +#endif + filename = tempFileName; // Store generated filename + std::ofstream tempFile(tempFileName, std::ios::trunc); + if (!tempFile.is_open()) { + std::cerr << "Temp file name is: " << filename << std::endl; + throw std::runtime_error("Failed to open temporary file for writing."); + } + tempFile << content; + tempFile.close(); +} + +TempTextFile::~TempTextFile() { + // Delete the temporary file upon destruction + std::remove(filename.c_str()); +} + +std::string TempTextFile::getFileName() const { + // Return the name of the temporary file. + return filename; +} \ No newline at end of file diff --git a/app/tests/TempTextFile.h b/app/tests/TempTextFile.h new file mode 100644 index 0000000..f70867d --- /dev/null +++ b/app/tests/TempTextFile.h @@ -0,0 +1,37 @@ +/** @file TempTextFile.h + * Header for a class which manages temporary text files. + */ +#pragma once + +#include // for std::string + +/******************************************************************************* + * A class to manage a temporary text file. + * + * The TempTextFile class creates a temporary text file from a string that is + * automatically deleted when the object is destroyed. + ******************************************************************************/ +class TempTextFile { + public: + /*********************************************************************** + * Constructor that creates a temporary file and writes content to it. + * + * @param[in] content String content to write to the file. + * @throws std::runtime_error On failure to create or write to file. + **********************************************************************/ + TempTextFile(const std::string &content); + + /*********************************************************************** + * Destructor that closes (and deletes) the temporary file. + **********************************************************************/ + ~TempTextFile(); + + /*********************************************************************** + * Retrieve the name of the temporary file + * + * @return A string containing the name of the temporary file. + **********************************************************************/ + std::string getFileName() const; + private: + std::string filename; /**< Name of the temporary file */ +}; diff --git a/app/tests/TestDriver.cpp b/app/tests/TestDriver.cpp new file mode 100644 index 0000000..abc9083 --- /dev/null +++ b/app/tests/TestDriver.cpp @@ -0,0 +1,67 @@ +/** @file TestDriver.cpp + * General tests for the driver executable + */ +#include "TestDriver.h" + +#include // for std::string + +TEST_F(DriverTest, MissingOptionError1) { + // Test case: missing option between two provided flags + std::string cmd = executable + " -i -o out.txt"; + SuppressOutputs(cmd); + int rtn = RunCommand(cmd); + EXPECT_EQ(DRVRERR__MISSING_OPTION, rtn); +} + +TEST_F(DriverTest, MissingOptionError2) { + // Test case: missing option at the end of command + std::string cmd = executable + " -i"; + SuppressOutputs(cmd); + int rtn = RunCommand(cmd); + EXPECT_EQ(DRVRERR__MISSING_OPTION, rtn); +} + +TEST_F(DriverTest, InvalidOptionError) { + std::string cmd = executable + " -X"; + SuppressOutputs(cmd); + int rtn = RunCommand(cmd); + EXPECT_EQ(DRVRERR__INVALID_OPTION, rtn); +} + +TEST_F(DriverTest, OpeningInputFileError) { + // TODO-TEMPLATE: Update this call to RunDriver + params.in_file = "/invalid/path/input.xyz"; + params.DBG = false; + int rtn = RunDriver(params); + EXPECT_EQ(DRVRERR__OPENING_INPUT_FILE, rtn); +} + +TEST_F(DriverTest, OpeningOutputFileError) { + // TODO-TEMPLATE: Update this call to RunDriverWithInputFile + // Provide valid inputs but invalid output file path + std::string inputs = "template,0.0"; + params.DBG = true; + params.out_file = "/invalid/path/output.xyz"; + int rtn = RunDriverWithInputFile(inputs, params); + EXPECT_EQ(DRVRERR__OPENING_OUTPUT_FILE, rtn); +} + +TEST_F(DriverTest, ValidationInFileError) { + std::string cmd = executable + " -o out.txt"; + SuppressOutputs(cmd); + int rtn = RunCommand(cmd); + EXPECT_EQ(DRVRERR__VALIDATION_IN_FILE, rtn); +} + +TEST_F(DriverTest, ValidationOutFileError) { + // Input file does not need to exist here, just has to be specified + // TODO-TEMPLATE May need to update the command here + std::string cmd = executable + " -i in.txt"; + SuppressOutputs(cmd); + int rtn = RunCommand(cmd); + EXPECT_EQ(DRVRERR__VALIDATION_OUT_FILE, rtn); +} + +// TODO-TEMPLATE: Add tests for any additional validation errors + +// TODO-TEMPLATE: Add other general tests for the driver \ No newline at end of file diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h new file mode 100644 index 0000000..b01796c --- /dev/null +++ b/app/tests/TestDriver.h @@ -0,0 +1,180 @@ +/** @file TestDriver.h + * Primary header and test fixture for command line driver tests. + */ +#pragma once + +// clang-format off +// GoogleTest must be included first +#include // GoogleTest +// clang-format on + +#include "Driver.h" +#include "TempTextFile.h" + +#include // for std::remove, std::perror +#include // for std::system +#include // for std::cout +#include // for std::endl, std::flush +#include // for std::string + +#ifndef _WIN32 + #include // for WEXITSTATUS +#endif + +/******************************************************************************* + * @class DriverTest + * Test fixture for running the driver executable tests. + * + * This class extends the Google Test framework's Test class and provides + * utilities to set up, execute, and manage the output of the driver executable. + ******************************************************************************/ +class DriverTest: public ::testing::Test { + protected: + /*********************************************************************** + * Sets up the test environment. + **********************************************************************/ + void SetUp() override { + // TODO-TEMPLATE review and optionally adjust default params here + // Set the default driver params + params.DBG = false; + params.out_file = "tmp_out.txt"; + + // Get the name of the executable to test + executable = std::string(DRIVER_LOCATION); + } + + /*********************************************************************** + * Suppresses the output of the command. + * + * Appends redirection to suppress standard output and error based on + * the platform. + * + * @param[in, out] cmd The command string to modify + **********************************************************************/ + void SuppressOutputs(std::string &cmd) { +#ifdef _WIN32 + cmd += " > nul"; +#else + cmd += " > /dev/null"; +#endif + cmd += " 2>&1"; + } + + /*********************************************************************** + * Builds the command string to run the driver. + * + * Constructs a command string using the provided driver parameters + * struct. Optionally, the command can be written such that stdout and + * are suppressed. + * + * @param[in] dParams The driver parameters + * @param[in] suppressOutputs Whether to suppress outputs (default: true) + * @return The constructed command string + **********************************************************************/ + std::string BuildCommand( + const DrvrParams &dParams, const bool suppressOutputs = true + ) { + // TODO-TEMPLATE: Modify this function to correctly + // unpack the DrvrParams struct and build the command + + // Construct command from parameters + std::string command = executable; + command += " -i " + dParams.in_file; + if (dParams.DBG) { + command += " -DBG"; + } + command += " -o " + dParams.out_file; + + // Suppress text output of the driver, to avoid cluttering + // test outputs. + if (suppressOutputs) { + SuppressOutputs(command); + } + // Return the full command string + return command; + } + + /*********************************************************************** + * Runs the provided command (cross-platform) + * + * Note that on POSIX platforms the exit code of the command should be + * between 0 and 255. Exit codes outside this range will be shifted into + * this range and cannot be unambiguously compared to expectations. + * + * @param[in] cmd The command to run + * @return The exit code of the command. + **********************************************************************/ + int RunCommand(const std::string &cmd) { + std::cout << std::flush; + int rtn = std::system(cmd.c_str()); +#ifndef _WIN32 + rtn = WEXITSTATUS(rtn); // Get child process exit code on POSIX +#endif + return rtn; + } + + /*********************************************************************** + * Runs the driver executable. + * + * @param[in] dParams Parameters to parse as command line arguments + * @return Return code from the driver execution + **********************************************************************/ + int RunDriver(const DrvrParams &dParams) { + std::string cmd = BuildCommand(dParams); + return RunCommand(cmd); + } + + /*********************************************************************** + * Runs the driver using the specified input file contents. + * + * This method creates a temporary text file containing the contents + * of `inFileContents` and then runs the driver using the temporary + * file as the input file. The rest of the required driver parameters + * are provided in the `params` input; the `params.in_file` value is + * ignored and can be unset. If an output file was produced by the + * driver, it is deleted before this method returns. + * + * @param[in] inFileContents The contents to write to the input file + * @param[in] dParams A populated driver parameters struct (see above) + * @return Return code from the driver execution + **********************************************************************/ + int RunDriverWithInputFile( + const std::string &inFileContents, const DrvrParams &dParams + ) { + DrvrParams updated_params = dParams; + TempTextFile tempFile(inFileContents); + updated_params.in_file = tempFile.getFileName(); + int rtn = RunDriver(updated_params); + // Cleanup: delete output file if it was created + DeleteOutputFile(updated_params.out_file); + return rtn; + } + + /*********************************************************************** + * Deletes the specified output file if it exists. + * + * Checks if the file exists and attempts to delete it. Reports any + * errors encountered during deletion. + * + * @param[in] fileName The name of the file to delete. + **********************************************************************/ + void DeleteOutputFile(const std::string &fileName) { + bool fileExists = false; +#ifdef _WIN32 + fileExists = _access(fileName.c_str(), 0) == 0; +#else + fileExists = access(fileName.c_str(), F_OK) == 0; +#endif + if (fileExists) { + if (std::remove(fileName.c_str()) != 0) { + std::perror("Error deleting output file"); + } + } + } + + /** Platform-dependent string to call the executable */ + std::string executable; + + /** Driver parameters struct which may be used by tests */ + DrvrParams params; +}; diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt new file mode 100644 index 0000000..aeb6125 --- /dev/null +++ b/docs/CMakeLists.txt @@ -0,0 +1,68 @@ +########################################### +## 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-400px.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( + "${LIB_NAME}Docs" + "${PROJECT_SOURCE_DIR}/app/src" + "${PROJECT_SOURCE_DIR}/app/include" + "${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..ebea68f --- /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..9084066 --- /dev/null +++ b/docs/doxy_mainpage.md @@ -0,0 +1,33 @@ +# Main Page + +This website is an information-oriented API reference document for the @libname +C++ library and associated command-line driver, 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 bindings for Python, MATLAB, and .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 0000000000000000000000000000000000000000..45a7ba3fdc8a947949e4eb97376705b2779a64d4 GIT binary patch literal 14748 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 literal 0 HcmV?d00001 diff --git a/docs/images/apple-touch-icon.png b/docs/images/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d6371ec98d7c7f24ddc8ac1570990a92336887dc GIT binary patch literal 5838 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 S^DO#(!)&Bys#~e!9R6SD2Sfz` literal 0 HcmV?d00001 diff --git a/docs/images/favicon-16x16.png b/docs/images/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..cc0687f5808893c9361d0c67cf5b5a374ae61185 GIT binary patch literal 2197 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 literal 0 HcmV?d00001 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/PropLibTemplate.h b/include/PropLibTemplate.h new file mode 100644 index 0000000..4a0dbaf --- /dev/null +++ b/include/PropLibTemplate.h @@ -0,0 +1,57 @@ +/** @file PropLibTemplate.h + * Interface header for this library + * // TODO-TEMPLATE: Rename this file to your ${LIB_NAME} + */ +#pragma once + +#include // for std::string +#include // for std::unordered_map + +// TODO-TEMPLATE: This header should provide EVERYTHING needed to interface +// with the shared library, without needing to include other headers. + +namespace ITS { +// TODO-TEMPLATE: Use your library's namespace + +// Define cross-platform PROPLIB_API to export functions +#ifndef DOXYGEN_SHOULD_SKIP + #ifndef PROPLIB_API + #ifdef _WIN32 + #define PROPLIB_API extern "C" __declspec(dllexport) + #else + #define PROPLIB_API extern "C" + #endif + #endif +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Enums + +/******************************************************************************* + * Return Codes defined by this software (0-127) + ******************************************************************************/ +// clang-format off +enum ReturnCode { + SUCCESS = 0, /**< Successful execution */ + + // TODO-TEMPLATE add return codes for this software + // TODO-TEMPLATE: Add corresponding status messages in src/ReturnCodes.cpp +}; +// clang-format on + +//////////////////////////////////////////////////////////////////////////////// +// Constants +// TODO-TEMPLATE define any global constants here (use constexpr!) + +//////////////////////////////////////////////////////////////////////////////// +// Public Functions +// TODO-TEMPLATE: Add functions which should be exported in the DLL +PROPLIB_API char *GetReturnStatusCharArray(const int code); +PROPLIB_API void FreeReturnStatusCharArray(char *c_msg); + +//////////////////////////////////////////////////////////////////////////////// +// Private Functions +// TODO-TEMPLATE: Add other/internal functions here (no need for "PROPLIB_API") +std::string GetReturnStatus(const int code); + +} // namespace ITS diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..33976f3 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,55 @@ +########################################### +## BUILD THE LIBRARY +########################################### +proplib_message("Configuring library ${LIB_NAME}") + +set(LIB_HEADERS "${PROJECT_SOURCE_DIR}/include") +set(LIB_FILES + "ReturnCodes.cpp" + "${LIB_HEADERS}/${LIB_NAME}.h" + ## TODO-TEMPLATE: Include source AND header files here. +) + +# By default, create shared library +if (NOT DEFINED BUILD_SHARED_LIBS) + message(STATUS "STATUS: BUILD_SHARED_LIBS is not defined to build the library: " ${LIB_NAME} ".") + add_library(${LIB_NAME} SHARED ${LIB_FILES}) +else () + message(STATUS "STATUS: BUILD_SHARED_LIBS is " ${BUILD_SHARED_LIBS} " to build the library: " ${LIB_NAME} ".") + add_library(${LIB_NAME} ${LIB_FILES}) +endif () + +# Add the include directory +target_include_directories(${LIB_NAME} PUBLIC "${LIB_HEADERS}") + +# link library to proplib_compiler_flags +# target_link_libraries(${LIB_NAME} PUBLIC proplib_compiler_flags) + +# Configure compiler options +configure_proplib_target(${LIB_NAME}) + +# Add definition to get the library name and version inside the library +add_compile_definitions( + LIBRARY_NAME="${LIB_NAME}" + LIBRARY_VERSION="${PROJECT_VERSION}" +) + +# Platform-specific configurations +if (WIN32) + set_target_properties(${LIB_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS true) +endif () +if (UNIX) + # avoid prefixing "lib" to the output file, for cross-platform consistency + set(CMAKE_SHARED_LIBRARY_PREFIX "") +endif () + +# Set some target metadata +set_target_properties( + ${LIB_NAME} PROPERTIES + OUTPUT_NAME ${LIB_NAME}-${PROJECT_VERSION}- + VERSION ${PROJECT_VERSION} + DEBUG_POSTFIX ${ARCH_SUFFIX} + RELEASE_POSTFIX ${ARCH_SUFFIX} +) + +proplib_message("Done configuring library ${LIB_NAME}") \ No newline at end of file diff --git a/src/ReturnCodes.cpp b/src/ReturnCodes.cpp new file mode 100644 index 0000000..bee6333 --- /dev/null +++ b/src/ReturnCodes.cpp @@ -0,0 +1,81 @@ +/** @file ReturnCodes.cpp + * Maps status messages to library return codes + */ + +// TODO-TEMPLATE include your primary library header +#include "PropLibTemplate.h" + +#ifdef _WIN32 + // Ensure strcpy_s is available on Windows + #ifndef __STDC_LIB_EXT1__ + #define __STDC_LIB_EXT1__ + #endif + #ifndef __STDC_WANT_LIB_EXT1__ + #define __STDC_WANT_LIB_EXT1__ 1 + #endif +#endif + +#include // for strcpy_s +#include // for std::string +#include // for std::unordered_map + +namespace ITS { +// TODO-TEMPLATE: put these functions in this software's namespace + +/******************************************************************************* + * Get an error message string from a return code. + * + * @param[in] code Integer return code. + * @return A status message corresponding to the input code. + ******************************************************************************/ +std::string GetReturnStatus(int code) { + static const std::unordered_map messages = { + {SUCCESS, "Successful execution"} + // TODO-TEMPLATE: Add messages corresponding to all return codes here + }; + // Construct status message + std::string msg = LIBRARY_NAME; + msg += " v"; + msg += LIBRARY_VERSION; + if (code == SUCCESS) { + msg += " Status: "; + } else { + msg += " Error: "; + } + + auto it = messages.find(static_cast(code)); + if (it != messages.end()) { + msg += it->second; + } else { + msg += "Undefined return code"; + } + return msg; +} + +/******************************************************************************* + * Get an error message string (as C-style string) from a return code. + * + * @param[in] code Integer return code. + * @return A status message corresponding to the input code. + ******************************************************************************/ +char *GetReturnStatusCharArray(const int code) { + const std::string msg = GetReturnStatus(code); + char *c_msg = new char[msg.size() + 1]; +#ifdef _WIN32 + strcpy_s(c_msg, msg.size() + 1, msg.c_str()); +#else + strcpy(c_msg, msg.c_str()); +#endif + return c_msg; +} + +/******************************************************************************* + * Free the memory allocated by GetReturnStatusCharArray + * + * @param[in] c_msg The status message C-style string to delete + ******************************************************************************/ +void FreeReturnStatusCharArray(char *c_msg) { + delete[] c_msg; +} + +} // namespace ITS \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..0fcd455 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,23 @@ +############################################ +## CONFIGURE UNIT TESTS +############################################ +set(TEST_NAME "${LIB_NAME}Test") +proplib_message("Configuring library tests ${TEST_NAME}") + +## TODO-TEMPLATE: Include all source AND header files for tests here. +## Do not include the source and header files of the library under test. +add_executable( + ${TEST_NAME} + "TestUtils.cpp" + "TestUtils.h" +) + +########################################### +## SET UP AND DISCOVER TESTS +########################################### +include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) +target_link_libraries(${TEST_NAME} ${LIB_NAME} GTest::gtest_main) +include(GoogleTest) +gtest_discover_tests(${TEST_NAME}) + +proplib_message("Done configuring library tests ${TEST_NAME}") \ No newline at end of file diff --git a/tests/TestUtils.cpp b/tests/TestUtils.cpp new file mode 100644 index 0000000..4a62ae6 --- /dev/null +++ b/tests/TestUtils.cpp @@ -0,0 +1,46 @@ +/** @file TestUtils.cpp + * Primary implementations for fixtures or common functions used by unit tests. + */ +#include "TestUtils.h" + +#include // for std::string + +// TODO-TEMPLATE: populate this file with common utilities for tests + +/******************************************************************************* + * Append a directory separator ('/' or '\') to a string, based on the + * current operating system. + * + * @param[in, out] str String to which the character will be appended. + *****************************************************************************/ +void AppendDirectorySep(std::string &str) { +#ifdef _WIN32 + str += "\\"; +#else + str += "/"; +#endif +} + +/****************************************************************************** + * Get the full path of the directory containing test data files. + * + * @return The path of the test data directory. + *****************************************************************************/ +std::string GetDataDirectory() { + std::string dataDir(__FILE__); + dataDir.resize(dataDir.find_last_of("/\\")); + dataDir.resize(dataDir.find_last_of("/\\")); + AppendDirectorySep(dataDir); + dataDir += "extern"; + AppendDirectorySep(dataDir); + dataDir + += "test-data"; // Name of data directory as cloned in the `extern` directory + AppendDirectorySep(dataDir); + return dataDir; +} + +// TODO-TEMPLATE: Remove this test and write your own in other files. +// This is included to verify test discovery is functional in the template. +TEST(TemplateTest, TestTemplate) { + EXPECT_EQ(1, 1); +} diff --git a/tests/TestUtils.h b/tests/TestUtils.h new file mode 100644 index 0000000..adf443d --- /dev/null +++ b/tests/TestUtils.h @@ -0,0 +1,11 @@ +/** @file TestUtils.h + * Primary header for fixtures or common functions used by unit tests. + */ +#pragma once + +// clang-format off +// GoogleTest must be included first +#include // GoogleTest +// clang-format on + +// TODO-TEMPLATE: define any common test fixtures here \ No newline at end of file From 2691484c8f405ab6d38f459818b74a56a020f227 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 2 Jan 2025 17:51:51 -0500 Subject: [PATCH 354/379] Avoid duplicate workflow runs when creating a PR from dev to main --- .github/workflows/cff-validator.yml | 2 ++ .github/workflows/ctest.yml | 2 +- .github/workflows/doxygen.yml | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cff-validator.yml b/.github/workflows/cff-validator.yml index 6fce818..ccbcc68 100644 --- a/.github/workflows/cff-validator.yml +++ b/.github/workflows/cff-validator.yml @@ -2,10 +2,12 @@ name: Validate CITATION.cff on: push: + branches: ["main"] paths: - 'CITATION.cff' - '.github/workflows/cff-validator.yml' pull_request: + branches: ["main", "dev"] paths: - 'CITATION.cff' - '.github/workflows/cff-validator.yml' diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index 7e060b3..363c39c 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -3,7 +3,7 @@ name: Unit Tests on: push: - branches: ["main", "dev"] + branches: ["main"] pull_request: branches: ["main", "dev"] workflow_dispatch: diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index a9287dd..c5ee894 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -9,7 +9,7 @@ on: pull_request: branches: ["main", "dev"] push: - branches: ["main", "dev"] + branches: ["main"] workflow_dispatch: # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. From 9208c4663a1d298f6b4b3a3404d6c5f120822307 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 3 Jan 2025 14:49:53 -0500 Subject: [PATCH 355/379] Add test data submodule init to workflow --- .github/workflows/ctest.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index 363c39c..dee5d49 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -48,6 +48,7 @@ jobs: - name: Clone required submodules run: | git submodule init extern/googletest + git submodule init extern/test-data git submodule update - name: Install CMake From f94fdfd912f5bb4063fb2ec0bd487faf6120e73e Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 3 Jan 2025 14:50:07 -0500 Subject: [PATCH 356/379] Avoid warning about unhandled case --- app/src/Driver.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/app/src/Driver.cpp b/app/src/Driver.cpp index 89a8b45..d371e68 100644 --- a/app/src/Driver.cpp +++ b/app/src/Driver.cpp @@ -3,14 +3,14 @@ */ #include "Driver.h" -#include // for std::find -#include // for std::ifstream, std::ofstream -#include // for std::setw -#include // for std::left -#include // for std::cerr -#include // for std::endl -#include // for std::string -#include // for std::vector +#include // for std::find +#include // for std::ifstream, std::ofstream +#include // for std::setw +#include // for std::left +#include // for std::cerr +#include // for std::endl +#include // for std::string +#include // for std::vector /******************************************************************************* * Main function of the driver executable @@ -97,7 +97,9 @@ int main(int argc, char **argv) { case P2108Model::ASM: fp << "Aeronautical Statistical Model"; break; - // Validation above ensures one of these cases evaluates + // Validation above ensures one of the above cases evaluates + default: + break; } fp PRINT "Library Version" << "v" << LIBRARY_VERSION; fp PRINT "Driver Version" << "v" << DRIVER_VERSION; @@ -120,7 +122,9 @@ int main(int argc, char **argv) { case P2108Model::ASM: WriteASMInputs(fp, asm_params); break; - // Validation above ensures one of these cases evaluates + // Validation above ensures one of these cases evaluates + default: + break; } // Print results to file From 40b8b4b029470c594f1c0cbec13d22384702d3a9 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 3 Jan 2025 14:57:37 -0500 Subject: [PATCH 357/379] Avoid compile warning for unhandled case --- app/tests/TestDriver.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/tests/TestDriver.h b/app/tests/TestDriver.h index 994dc43..2d3fe64 100644 --- a/app/tests/TestDriver.h +++ b/app/tests/TestDriver.h @@ -85,6 +85,8 @@ class DriverTest: public ::testing::Test { case P2108Model::ASM: command += " -model ASM"; break; + default: // avoid compile-time warning. command will be invalid if this runs. + break; } command += " -o " + dParams.out_file; From 2fe1606ac06d5c54d88baa761c0a17adf1084c84 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 3 Jan 2025 15:30:14 -0500 Subject: [PATCH 358/379] test run create release artifacts action --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 209174b..4d66515 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,6 +5,7 @@ name: Create Release Artifacts on: push: tags: ['v[0-9]+.*'] + branches: ["inherit-from-template"] workflow_dispatch: permissions: From 3f8cdb9f5e49543cda9569f1673418a310dcf343 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 3 Jan 2025 15:38:16 -0500 Subject: [PATCH 359/379] Populate zenodo template --- .zenodo.json | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/.zenodo.json b/.zenodo.json index c3c6f74..a6e1a89 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -1,50 +1,58 @@ { "upload_type": "software", - "publication_date": "TODO-TEMPLATE", - "title": "TODO-TEMPLATE", + "publication_date": "2022-09-26", + "title": "Recommendation ITU-R P.2108-1, Release 1.0", "creators": [ { - "name": "TODO-TEMPLATE", + "name": "Kozma Jr, William", "affiliation": "U.S. Department of Commerce, National Telecommunications and Information Administration, Institute for Telecommunication Sciences", - "orcid": "TODO-TEMPLATE" + "orcid": "0000-0002-7417-4009" + }, + { + "name": "Romaniello, Anthony W.", + "affiliation": "U.S. Department of Commerce, National Telecommunications and Information Administration, Institute for Telecommunication Sciences", + "orcid": "0000-0001-8437-6504" } ], - "description": "TODO-TEMPLATE. Make this the same as the abstract.", + "description": "This repository contains the NTIA/ITS implementation of Recommendation ITU-R P.2108. This Recommendation contains three methods for the 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.", "keywords": [ - "TODO-TEMPLATE", - "TODO-TEMPLATE" + "Study Group 3", + "ITU-R", + "P2108", + "clutter", + "propagation" ], "related_identifiers": [ { - "identifier": "https://github.com/NTIA/TODO-TEMPLATE-dotnet", + "identifier": "https://github.com/NTIA/p2108-dotnet", "relation": "isSupplementedBy", "resource_type": "software" }, { - "identifier": "https://github.com/NTIA/TODO-TEMPLATE-matlab", + "identifier": "https://github.com/NTIA/p2108-matlab", "relation": "isSupplementedBy", "resource_type": "software" }, { - "identifier": "https://github.com/NTIA/TODO-TEMPLATE-python", + "identifier": "https://github.com/NTIA/p2108-python", "relation": "isSupplementedBy", "resource_type": "software" }, { - "identifier": "https://github.com/NTIA/TODO-TEMPLATE-test-data", + "identifier": "https://github.com/NTIA/-test-data", "relation": "isSupplementedBy", "resource_type": "dataset" }, { - "identifier": "https://ntia.github.io/TODO-TEMPLATE/", + "identifier": "https://ntia.github.io/P2108/", "relation": "isDocumentedBy", "resource_type": "softwaredocumentation" }, { - "identifier": "https://ntia.github.io/propagation-library-wiki/models/TODO-TEMPLATE/", + "identifier": "https://ntia.github.io/propagation-library-wiki/models/P2108/", "relation": "isDocumentedBy", "resource_type": "softwaredocumentation" } ], - "version": "TODO-TEMPLATE" + "version": "1.0" } \ No newline at end of file From 0e538ba1d45d11399d96de551858bd931fd489c6 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 3 Jan 2025 15:38:29 -0500 Subject: [PATCH 360/379] Update link to wiki page --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index feabea4..b64b94b 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ examples for all supported languages. An executable is also provided which can be used to run the functions provided by this library using plain text input and output files. Installation and usage details for the command-line driver are also provided on -[the wiki](https://ntia.github.io/propagation-library-wiki/models/TODO-TEMPLATE/driver). +[the wiki](https://ntia.github.io/propagation-library-wiki/models/P2108/driver). If you're a developer and would like to contribute to or extend this repository, you will find comprehensive documentation of this C++ code From 0a720afa7ea3fa1d94d3a0cc34fedf2df5100acd Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 3 Jan 2025 15:38:37 -0500 Subject: [PATCH 361/379] Populate template header file name --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4d66515..d27e6c9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -77,11 +77,11 @@ jobs: if-no-files-found: error overwrite: true - - name: Upload release artifact (C++ Header) # TODO-TEMPLATE: Specify the correct header below + - name: Upload release artifact (C++ Header) if: runner.os == 'Linux' uses: actions/upload-artifact@v4 with: name: "${{ github.event.repository.name }}-release-cpp-header" - path: ${{ github.workspace }}/include/PropLibTemplate.h + path: ${{ github.workspace }}/include/P2108.h if-no-files-found: error overwrite: true From bd1b8b77df0f7a67a71a4d9aea473e10a0738d8f Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 3 Jan 2025 15:44:32 -0500 Subject: [PATCH 362/379] Revert workflow trigger used for testing --- .github/workflows/release.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d27e6c9..19b1ec3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,7 +5,6 @@ name: Create Release Artifacts on: push: tags: ['v[0-9]+.*'] - branches: ["inherit-from-template"] workflow_dispatch: permissions: From 5518ad9d54dfdd346866629b730d328c9335b89b Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 10 Jan 2025 14:22:41 -0500 Subject: [PATCH 363/379] Remove example IO files for driver Replaced by examples provided in the PropLib wiki. unit tests separately generate their own test inputs --- app/data/i_asm.txt | 3 --- app/data/i_hgtcm.txt | 5 ----- app/data/i_tsm.txt | 3 --- app/data/o_asm.txt | 15 --------------- app/data/o_hgtcm.txt | 17 ----------------- app/data/o_tsm.txt | 15 --------------- 6 files changed, 58 deletions(-) delete mode 100644 app/data/i_asm.txt delete mode 100644 app/data/i_hgtcm.txt delete mode 100644 app/data/i_tsm.txt delete mode 100644 app/data/o_asm.txt delete mode 100644 app/data/o_hgtcm.txt delete mode 100644 app/data/o_tsm.txt diff --git a/app/data/i_asm.txt b/app/data/i_asm.txt deleted file mode 100644 index 72a72cd..0000000 --- a/app/data/i_asm.txt +++ /dev/null @@ -1,3 +0,0 @@ -f__ghz,10 -theta__deg,10.5 -p,45 \ No newline at end of file diff --git a/app/data/i_hgtcm.txt b/app/data/i_hgtcm.txt deleted file mode 100644 index 3bfb3ea..0000000 --- a/app/data/i_hgtcm.txt +++ /dev/null @@ -1,5 +0,0 @@ -f__ghz,1.5 -h__meter,2 -w_s__meter,27 -R__meter,15 -clutter_type,4 \ No newline at end of file diff --git a/app/data/i_tsm.txt b/app/data/i_tsm.txt deleted file mode 100644 index d8dbf87..0000000 --- a/app/data/i_tsm.txt +++ /dev/null @@ -1,3 +0,0 @@ -f__ghz,26.6 -d__km,15.8 -p,45 \ No newline at end of file diff --git a/app/data/o_asm.txt b/app/data/o_asm.txt deleted file mode 100644 index f93be43..0000000 --- a/app/data/o_asm.txt +++ /dev/null @@ -1,15 +0,0 @@ -Model P2108 -Model Variant Aeronautical Statistical Model -Library Version v1.0 -Driver Version v1.0.0 -Date Generated Thu Oct 31 17:30:41 2024 -Input Arguments -i .\i_asm.txt -model ASM -o .\o_asm.txt - -Inputs -f__ghz 10 (gigahertz) -theta__deg 10.5 (degrees) -p 45 (%) - -Results -Return Code 0 [P2108 Status: Successful execution] -Clutter loss 12.4 (dB) \ No newline at end of file diff --git a/app/data/o_hgtcm.txt b/app/data/o_hgtcm.txt deleted file mode 100644 index 70f3fe7..0000000 --- a/app/data/o_hgtcm.txt +++ /dev/null @@ -1,17 +0,0 @@ -Model P2108 -Model Variant Height Gain Terminal Correction Model -Library Version v1.0 -Driver Version v1.0.0 -Date Generated Thu Oct 31 17:31:08 2024 -Input Arguments -i .\i_hgtcm.txt -model HGTCM -o .\o_hgtcm.txt - -Inputs -f__ghz 1.5 (gigahertz) -h__meter 2 (meters) -w_s__meter 27 (meters) -r__meter 15 (meters) -clutter_type 4 [Urban clutter type] - -Results -Return Code 0 [P2108 Status: Successful execution] -Clutter loss 24.5 (dB) \ No newline at end of file diff --git a/app/data/o_tsm.txt b/app/data/o_tsm.txt deleted file mode 100644 index 8fe530c..0000000 --- a/app/data/o_tsm.txt +++ /dev/null @@ -1,15 +0,0 @@ -Model P2108 -Model Variant Terrestrial Statistical Model -Library Version v1.0 -Driver Version v1.0.0 -Date Generated Thu Oct 31 17:30:55 2024 -Input Arguments -i .\i_tsm.txt -model TSM -o .\o_tsm.txt - -Inputs -f__ghz 26.6 (gigahertz) -d__km 15.8 (kilometers) -p 45 (%) - -Results -Return Code 0 [P2108 Status: Successful execution] -Clutter loss 32.5 (dB) \ No newline at end of file From 027e603b062f19f30b5cd3efcbcad94401ac23e4 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 10 Jan 2025 16:14:07 -0500 Subject: [PATCH 364/379] Add license and disclaimer text --- README.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index affa347..40b36d9 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ For questions about using this template repository, contact . +## Disclaimer ## + +Certain commercial equipment, instruments, or materials are identified in this project were used for the convenience of the developers. In no case does such identification imply recommendation or endorsement by the National Telecommunications and Information Administration, nor does it imply that the material or equipment identified is necessarily the best available for the purpose. + --> From 21c1f1fd682c181f0abfea49e9c7d5f6d27e8253 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Fri, 10 Jan 2025 16:14:43 -0500 Subject: [PATCH 365/379] Remove dead code --- src/CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 33976f3..dae5fa1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,9 +22,6 @@ endif () # Add the include directory target_include_directories(${LIB_NAME} PUBLIC "${LIB_HEADERS}") -# link library to proplib_compiler_flags -# target_link_libraries(${LIB_NAME} PUBLIC proplib_compiler_flags) - # Configure compiler options configure_proplib_target(${LIB_NAME}) From 39ee7de29ac950ecdc1df7928c3af4a2ebff9dc0 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 13 Jan 2025 11:26:07 -0500 Subject: [PATCH 366/379] Update project name in release form --- GitHubRepoPublicReleaseApproval.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GitHubRepoPublicReleaseApproval.md b/GitHubRepoPublicReleaseApproval.md index d435f83..cedd557 100644 --- a/GitHubRepoPublicReleaseApproval.md +++ b/GitHubRepoPublicReleaseApproval.md @@ -1,6 +1,6 @@ # GitHub Repository Public Release Approval -**Project Name:** NTIA/OSM Research and Development +**Project Name:** NTIA/OSM Research and Development - Propagation Library **Software Name:** TODO-TEMPLATE From 0aa45b9060a1e94413ce3379d62552abf27523e1 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 13 Jan 2025 11:31:40 -0500 Subject: [PATCH 367/379] Correct PL name --- GitHubRepoPublicReleaseApproval.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GitHubRepoPublicReleaseApproval.md b/GitHubRepoPublicReleaseApproval.md index 28bbd29..c1532e3 100644 --- a/GitHubRepoPublicReleaseApproval.md +++ b/GitHubRepoPublicReleaseApproval.md @@ -28,5 +28,5 @@ your version of this Markdown document to that branch, and then create a pull re for that branch. The following must log in to GitHub and approve that pull request before the pull request can be merged and this repo made public: -* Project Lead: Brian Lain +* Project Lead: William Kozma, Jr. * Supervising Division Chief or Release Authority: Chris Anderson From f350b14bd5c80c8b03b3451c8bc88d322c57372c Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 13 Jan 2025 12:41:25 -0500 Subject: [PATCH 368/379] Fix CMake preset name in example command --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 40b36d9..b2515d2 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,7 @@ If you've configured tests when building the project, for example by using one o the "Release" or "Debug" CMake presets, you can run the included unit tests as follows: ```cmd -ctest --preset release +ctest --preset release64 ``` ## References ## From 2b42143e1cf55fdd38aa27aa3f2c7c56d2b3f62f Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Mon, 13 Jan 2025 13:49:05 -0500 Subject: [PATCH 369/379] Update test data expected location in example --- CONTRIBUTING.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 169a31b..e425fa1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -128,25 +128,22 @@ repository. For details about wrapper repositories, refer to their own README fi ```bash app/ # The command-line driver which can run the library - data/ # Example input and output files for use with the driver include/ # Headers used by the command-line driver src/ # Source code for the command-line driver tests/ # Header and source files for testing the command-line driver CMakeLists.txt # Configuration for the command-line driver and its tests - README.md # Usage information for the command-line driver docs/ CMakeLists.txt # Doxygen configuration ... # Static files (images, HTML, CS, Markdown) used by Doxygen extern/ - ... # External Git submodules/dependencies + test-data/ # Git submodule containing test data files shared with wrappers + ... # Other external Git submodules/dependencies include/ .h # Library interface header file goes here, e.g. "ITM.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. Files containing tests must be included here. From 969a3652788bd7734960b13c02fafec6e232c719 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:49:40 -0500 Subject: [PATCH 370/379] Clone extern/test-data submodule by default --- .github/workflows/ctest.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index 363c39c..af5d14b 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -45,9 +45,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Clone required submodules + - name: Clone required submodules # TODO-TEMPLATE: Check required submodules and update if needed run: | git submodule init extern/googletest + git submodule init extern/test-data git submodule update - name: Install CMake From 57c11b43cf37df697af28c84b78de58d582bd3e2 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:50:47 -0500 Subject: [PATCH 371/379] Remove test-data submodule from template, keep instructions --- .github/workflows/ctest.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index af5d14b..5701642 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -45,10 +45,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Clone required submodules # TODO-TEMPLATE: Check required submodules and update if needed + - name: Clone required submodules # TODO-TEMPLATE: Add test-data submodule here, if needed run: | git submodule init extern/googletest - git submodule init extern/test-data git submodule update - name: Install CMake From b801cb3ea3693162f45e81518050c97184cd1c09 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:54:46 -0500 Subject: [PATCH 372/379] Remove template text --- .github/workflows/ctest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index ae98d2f..dee5d49 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -45,7 +45,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Clone required submodules # TODO-TEMPLATE: Add test-data submodule here, if needed + - name: Clone required submodules run: | git submodule init extern/googletest git submodule init extern/test-data From 628c106d9c484b609eda936569774c0191ce93f1 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 16 Jan 2025 15:50:25 -0500 Subject: [PATCH 373/379] Run driver tests and use debug preset for CI tests (temporary) --- .github/workflows/ctest.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index dee5d49..c2c2e7e 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -60,16 +60,16 @@ jobs: if: matrix.architecture != 'x86' uses: lukka/run-cmake@v10 with: - configurePreset: release64 - configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF']" - buildPreset: release64 - testPreset: release64 + configurePreset: debug64 + configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF','-DRUN_DRIVER_TESTS=ON']" + buildPreset: debug64 + testPreset: debug64 - name: "CMake: Build and Test (32-bit)" if: matrix.architecture == 'x86' uses: lukka/run-cmake@v10 with: - configurePreset: release32 - configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF']" - buildPreset: release32 - testPreset: release32 + configurePreset: debug32 + configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF,'-DRUN_DRIVER_TESTS=ON'']" + buildPreset: debug32 + testPreset: debug32 From 5d10a5ca9a55ef5e9106eafa50d02c2ed08cde62 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 16 Jan 2025 15:57:12 -0500 Subject: [PATCH 374/379] fix typo, edit preset options --- .github/workflows/ctest.yml | 2 -- CMakePresets.json | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index c2c2e7e..856c587 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -61,7 +61,6 @@ jobs: uses: lukka/run-cmake@v10 with: configurePreset: debug64 - configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF','-DRUN_DRIVER_TESTS=ON']" buildPreset: debug64 testPreset: debug64 @@ -70,6 +69,5 @@ jobs: uses: lukka/run-cmake@v10 with: configurePreset: debug32 - configurePresetAdditionalArgs: "['-DBUILD_DOCS=OFF,'-DRUN_DRIVER_TESTS=ON'']" buildPreset: debug32 testPreset: debug32 diff --git a/CMakePresets.json b/CMakePresets.json index 6973e97..ec7c65d 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -43,8 +43,8 @@ "CMAKE_BUILD_RPATH_USE_ORIGIN": "ON", "BUILD_SHARED_LIBS": "ON", "BUILD_DOCS": "OFF", - "BUILD_DRIVER": "OFF", - "RUN_DRIVER_TESTS": "OFF" + "BUILD_DRIVER": "ON", + "RUN_DRIVER_TESTS": "ON" }, "environment": { "CMAKE_BUILD_TYPE": "Debug", From b5fe908e9d6f5d1553f7277f82c6a1509edcd181 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 16 Jan 2025 17:12:27 -0500 Subject: [PATCH 375/379] Build and test driver when using debug presets --- CMakePresets.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index 6973e97..c68dd72 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -43,8 +43,8 @@ "CMAKE_BUILD_RPATH_USE_ORIGIN": "ON", "BUILD_SHARED_LIBS": "ON", "BUILD_DOCS": "OFF", - "BUILD_DRIVER": "OFF", - "RUN_DRIVER_TESTS": "OFF" + "BUILD_DRIVER": "ON", + "RUN_DRIVER_TESTS": "ON" }, "environment": { "CMAKE_BUILD_TYPE": "Debug", @@ -54,7 +54,7 @@ { "name": "debug64", "displayName": "Debug, 64-Bit", - "description": "Build library and tests with debug options. Skip building driver, driver tests, and docs.", + "description": "Build library, driver and tests with debug options. Skip building docs.", "inherits": "proplib-config-debug-base" }, { @@ -66,7 +66,7 @@ { "name": "debug32", "displayName": "Debug, 32-bit", - "description": "Build 32-bit library and tests with debug options. Skip building driver, driver tests, and docs.", + "description": "Build 32-bit library, driver and tests with debug options. Skip building docs.", "inherits": "proplib-config-debug-base", "cacheVariables": { "BUILD_32BIT": "ON" @@ -121,28 +121,28 @@ "name": "debug64", "inherits": "proplib-build-debug-base", "displayName": "Build Debug, 64-Bit", - "description": "Build library and tests with debug options, skip building docs", + "description": "Build library, driver and tests with debug options, skip building docs", "configurePreset": "debug64" }, { "name": "release64", "inherits": "proplib-build-release-base", "displayName": "Build Release, 64-Bit", - "description": "Build library and tests with release options, and build docs", + "description": "Build library, driver, docs and tests with release options", "configurePreset": "release64" }, { "name": "debug32", "inherits": "proplib-build-debug-base", "displayName": "Build Debug, 32-Bit", - "description": "Build library and tests with debug options, skip building docs", + "description": "Build library, driver and tests with debug options, skip building docs", "configurePreset": "debug32" }, { "name": "release32", "inherits": "proplib-build-release-base", "displayName": "Build Release, 32-Bit", - "description": "Build library and tests with release options, and build docs", + "description": "Build library, driver, docs and tests with release options", "configurePreset": "release32" }, { @@ -179,28 +179,28 @@ "name": "debug64", "inherits": "proplib-test-debug-base", "displayName": "Test Debug, 64-bit", - "description": "Build library and tests with debug options, skip building docs", + "description": "Build library, driver and tests with debug options, skip building docs", "configurePreset": "debug64" }, { "name": "release64", "inherits": "proplib-test-release-base", "displayName": "Test Release, 64-Bit", - "description": "Build library and tests with release options, and build docs", + "description": "Build library, driver and tests with release options, and build docs", "configurePreset": "release64" }, { "name": "debug32", "inherits": "proplib-test-debug-base", "displayName": "Test Debug, 32-Bit", - "description": "Build library and tests with debug options, skip building docs", + "description": "Build library, driver and tests with debug options, skip building docs", "configurePreset": "debug32" }, { "name": "release32", "inherits": "proplib-test-release-base", "displayName": "Test Release, 32-Bit", - "description": "Build library and tests with release options, and build docs", + "description": "Build library, driver and tests with release options, and build docs", "configurePreset": "release32" } ] From 12e73a5d129f2a6d50b56f5217499c0c9c942c69 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 16 Jan 2025 17:13:26 -0500 Subject: [PATCH 376/379] Change debug warning level to W4 for MSVC --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cfc3851..314d634 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,11 +108,11 @@ function(configure_proplib_target proplib_target) # For GCC-like compilers in 32-bit configurations "$<${gcc_like_cxx}:$:-m32>>>" # For MSVC compiler in any configuration - "$<${msvc_cxx}:$>" + "$<${msvc_cxx}:$>" # For MSVC compiler in Release configurations - "$<${msvc_cxx}:$:/O2;/DNDEBUG;/MT>>>" + "$<${msvc_cxx}:$:/W3;/O2;/DNDEBUG;/MT>>>" # For MSVC compiler in Debug configurations - "$<${msvc_cxx}:$:/Od;/Zi;/MTd>>>" + "$<${msvc_cxx}:$:/W4;/Od;/Zi;/MTd>>>" ) endfunction() From a252c1fe0603e6e4dc0fcccf440093768f90d356 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 16 Jan 2025 17:14:11 -0500 Subject: [PATCH 377/379] Remove /MT and /MTd compiler flags for MSVC Caused errors by default when running driver tests using MSVC. This provides a working default configuration and preset behavior --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 314d634..98ab8d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,9 +110,9 @@ function(configure_proplib_target proplib_target) # For MSVC compiler in any configuration "$<${msvc_cxx}:$>" # For MSVC compiler in Release configurations - "$<${msvc_cxx}:$:/W3;/O2;/DNDEBUG;/MT>>>" + "$<${msvc_cxx}:$:/W3;/O2;/DNDEBUG>>>" # For MSVC compiler in Debug configurations - "$<${msvc_cxx}:$:/W4;/Od;/Zi;/MTd>>>" + "$<${msvc_cxx}:$:/W4;/Od;/Zi>>>" ) endfunction() From 6d372d5fe1a049c9957705b62040dc8983a99205 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 16 Jan 2025 17:19:13 -0500 Subject: [PATCH 378/379] Add PropLib compiler option configuration to tests --- app/src/CMakeLists.txt | 1 + app/tests/CMakeLists.txt | 3 +++ src/CMakeLists.txt | 2 +- tests/CMakeLists.txt | 3 +++ 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/CMakeLists.txt b/app/src/CMakeLists.txt index b839aa9..8c033d8 100644 --- a/app/src/CMakeLists.txt +++ b/app/src/CMakeLists.txt @@ -21,6 +21,7 @@ target_include_directories(${DRIVER_NAME} PUBLIC "${DRIVER_HEADERS}") # Link the library to the executable target_link_libraries(${DRIVER_NAME} ${LIB_NAME}) +# Set PropLib compiler option defaults configure_proplib_target(${DRIVER_NAME}) # Add definitions to enable version identification inside the driver diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index 8a9f4ef..f6033c0 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -21,6 +21,9 @@ target_include_directories( # Link the library to the executable target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME}) +# Set PropLib compiler option defaults +configure_proplib_target(${TEST_NAME}) + # Make driver executable location available to source add_compile_definitions(DRIVER_LOCATION="$") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dae5fa1..d39d591 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,7 +22,7 @@ endif () # Add the include directory target_include_directories(${LIB_NAME} PUBLIC "${LIB_HEADERS}") -# Configure compiler options +# Set PropLib compiler option defaults configure_proplib_target(${LIB_NAME}) # Add definition to get the library name and version inside the library diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0fcd455..d38d8cc 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -12,6 +12,9 @@ add_executable( "TestUtils.h" ) +# Set PropLib compiler option defaults +configure_proplib_target(${TEST_NAME}) + ########################################### ## SET UP AND DISCOVER TESTS ########################################### From 7d932a53ba891162f389a417aae2f32048703bad Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <66272872+aromanielloNTIA@users.noreply.github.com> Date: Thu, 16 Jan 2025 17:24:03 -0500 Subject: [PATCH 379/379] Fix wrong target provided to function --- app/tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index f6033c0..3ec1941 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -22,7 +22,7 @@ target_include_directories( target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME}) # Set PropLib compiler option defaults -configure_proplib_target(${TEST_NAME}) +configure_proplib_target(${DRIVER_TEST_NAME}) # Make driver executable location available to source add_compile_definitions(DRIVER_LOCATION="$")