diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5a0ed1f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +heat_exchander_airflow_pressure_drop.dSYM +commit.md \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..cd4d5eb --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,4 @@ +{ + "version": "0.2.0", + "configurations": [] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..cb24e3f --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,49 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: gcc build MAC", + "command": "/usr/bin/gcc", + "args": [ + "-fdiagnostics-color=always", + "-g", + "-I${fileDirname}", + "${workspaceFolder}/*.c", + "${workspaceFolder}/ats0002/*.c", + "-o", + "${fileDirname}/${fileBasenameNoExtension}" + ], + "options": { + "cwd": "${fileDirname}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "C/C++: gcc build WIN", + "type": "shell", + "command": "x86_64-w64-mingw32-gcc", + "args": [ + "-g", + "-Wall", + "-o", + "${workspaceFolder}/heat_exchander_airflow_pressure_drop.exe", + "${workspaceFolder}/*.c", + "${workspaceFolder}/ats0002/*.c" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$gcc" + ] + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 9ab5c51..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -idf_component_register(SRCS "main.c" INCLUDE_DIRS "include") \ No newline at end of file diff --git a/README.md b/README.md index 3df3e4d..0f39e15 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,26 @@ -# esp_component_template +# Heat Exchanger Airflow Pressure Drop Calculation -esp_component_template \ No newline at end of file +## Features + +1. Calculation of pressure drop across heat exchangers. +2. Support for three heat exchanger models: + - Honeywell Heat Exchanger CMM 21-50-14 + - Liebherr Reheater CMM 21-53-89 + - Liebherr Condenser CMM 21-53-88 +3. Multiple part number selection for each heat exchanger. +4. Air passage configuration selection. +5. Airflow and static inlet pressure measurement input. +6. Automatic pass/fail evaluation against max allowed pressure drop limit. +7. Recalculation support based on barometric pressure and airflow temperature. +8. Logarithmic interpolation of pressure drop limits from anchor points. +9. Support for KPA and PSIG pressure units. + +## Using + +Clone and build the project: + +```text +git clone https://github.com/aZholtikov/ats0002 +cd ats0002 +build +``` diff --git a/ats0002/21_50_14.h b/ats0002/21_50_14.h new file mode 100644 index 0000000..dcf0184 --- /dev/null +++ b/ats0002/21_50_14.h @@ -0,0 +1,274 @@ +#pragma once + +#include "ats0002.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @file 21_50_14.h + * + * @brief Honeywell Heat Exchanger CMM 21-50-14 configuration data. + * + * This module provides static configuration data for Honeywell Heat Exchanger + * CMM 21-50-14, including pressure-to-airflow anchor points for + * cooling and hot sides, air passage definitions, and heat exchanger type + * mappings for part numbers 182820-1 through 182820-4. + * + * Key features: + * - Cooling side performance data (Figures 4002 and 4003) + * - Hot side performance data (Figures 4005 and 4006) + * - Complete air passage configuration with operational limits + * + * @note All data arrays are static and const-qualified for read-only access. + */ + + /** + * @brief Honeywell Heat Exchanger CMM 21-50-14, Figure 4002 (P/N 182820-1). + * + * Cooling side dependence of ΔPS (KPA) on air flow rate (LB/MIN), UPPER LIMIT. + */ + static const airflow_to_pressure_anchors_data_t _cooling_side_182820_1[] = { + {30.0, 0.14}, + {40.0, 0.23}, + {50.0, 0.33}, + {60.0, 0.45}, + {70.0, 0.57}, + {80.0, 0.72}, + {90.0, 0.87}, + {100.0, 1.07}, + {200.0, 3.48}, + {300.0, 6.72}}; + + /** + * @brief Honeywell Heat Exchanger CMM 21-50-14, Figure 4002 (P/N 182820-2). + * + * Cooling side dependence of ΔPS (KPA) on air flow rate (LB/MIN), UPPER LIMIT. + */ + static const airflow_to_pressure_anchors_data_t _cooling_side_182820_2[] = { + {30.0, 0.14}, + {40.0, 0.23}, + {50.0, 0.33}, + {60.0, 0.45}, + {70.0, 0.57}, + {80.0, 0.72}, + {90.0, 0.87}, + {100.0, 1.07}, + {200.0, 3.48}, + {300.0, 6.72}}; + + /** + * @brief Honeywell Heat Exchanger CMM 21-50-14, Figure 4003 (P/N 182820-3). + * + * Cooling side dependence of ΔPS (KPA) on air flow rate (LB/MIN), UPPER LIMIT. + */ + static const airflow_to_pressure_anchors_data_t _cooling_side_182820_3[] = { + {30.0, 0.12}, + {40.0, 0.19}, + {50.0, 0.29}, + {60.0, 0.39}, + {70.0, 0.47}, + {80.0, 0.60}, + {90.0, 0.72}, + {100.0, 0.85}, + {200.0, 2.59}, + {300.0, 4.85}, + {400.0, 7.96}}; + + /** + * @brief Honeywell Heat Exchanger CMM 21-50-14, Figure 4003 (P/N 182820-4). + * + * Cooling side dependence of ΔPS (KPA) on air flow rate (LB/MIN), UPPER LIMIT. + */ + static const airflow_to_pressure_anchors_data_t _cooling_side_182820_4[] = { + {30.0, 0.12}, + {40.0, 0.19}, + {50.0, 0.29}, + {60.0, 0.39}, + {70.0, 0.47}, + {80.0, 0.60}, + {90.0, 0.72}, + {100.0, 0.85}, + {200.0, 2.59}, + {300.0, 4.85}, + {400.0, 7.96}}; + + /** + * @brief Honeywell Heat Exchanger CMM 21-50-14, Figure 4005 (P/N 182820-1). + * + * Hot side dependence of ΔPS (PSIG) on air flow rate (LB/MIN), UPPER LIMIT. + */ + static const airflow_to_pressure_anchors_data_t _hot_side_182820_1[] = { + {30.0, 1.18}, + {40.0, 1.96}, + {50.0, 3.05}, + {60.0, 4.13}, + {70.0, 5.65}, + {80.0, 7.02}, + {90.0, 8.94}, + {100.0, 10.81}, + {200.0, 43.22}}; + + /** + * @brief Honeywell Heat Exchanger CMM 21-50-14, Figure 4005 (P/N 182820-2). + * + * Hot side dependence of ΔPS (PSIG) on air flow rate (LB/MIN), UPPER LIMIT. + */ + static const airflow_to_pressure_anchors_data_t _hot_side_182820_2[] = { + {30.0, 1.18}, + {40.0, 1.96}, + {50.0, 3.05}, + {60.0, 4.13}, + {70.0, 5.65}, + {80.0, 7.02}, + {90.0, 8.94}, + {100.0, 10.81}, + {200.0, 43.22}}; + + /** + * @brief Honeywell Heat Exchanger CMM 21-50-14, Figure 4006 (P/N 182820-3). + * + * Hot side dependence of ΔPS (PSIG) on air flow rate (LB/MIN), UPPER LIMIT. + */ + static const airflow_to_pressure_anchors_data_t _hot_side_182820_3[] = { + {30.0, 1.67}, + {40.0, 2.65}, + {50.0, 3.93}, + {60.0, 5.60}, + {70.0, 7.17}, + {80.0, 9.33}, + {90.0, 11.30}, + {100.0, 13.75}, + {200.0, 44.20}}; + + /** + * @brief Honeywell Heat Exchanger CMM 21-50-14, Figure 4006 (P/N 182820-4). + * + * Hot side dependence of ΔPS (PSIG) on air flow rate (LB/MIN), UPPER LIMIT. + */ + static const airflow_to_pressure_anchors_data_t _hot_side_182820_4[] = { + {30.0, 1.67}, + {40.0, 2.65}, + {50.0, 3.93}, + {60.0, 5.60}, + {70.0, 7.17}, + {80.0, 9.33}, + {90.0, 11.30}, + {100.0, 13.75}, + {200.0, 44.20}}; + + static const air_passage_data_t _air_passage_182820_1_data[] = { + {.air_passage_description = "Cooling Air Side", + .air_passage_anchors = _cooling_side_182820_1, + .anchors_data_size = ARRAY_SIZE(_cooling_side_182820_1), + .recalculated_required = true, + .airflow_pressure_unit = KPA, + .min_barometric_pressure = 840.0, + .max_barometric_pressure = 1070.0, + .min_airflow_temperature = 15.0, + .max_airflow_temperature = 35.0, + .min_airflow_pressure = 0.01, + .max_airflow_pressure = 2.0}, + {.air_passage_description = "Bleed Air Side", + .air_passage_anchors = _hot_side_182820_1, + .anchors_data_size = ARRAY_SIZE(_hot_side_182820_1), + .recalculated_required = true, + .airflow_pressure_unit = PSIG, + .min_barometric_pressure = 840.0, + .max_barometric_pressure = 1070.0, + .min_airflow_temperature = 15.0, + .max_airflow_temperature = 35.0, + .min_airflow_pressure = 0.01, + .max_airflow_pressure = 15.0}}; + + static const air_passage_data_t _air_passage_182820_2_data[] = { + {.air_passage_description = "Cooling Air Side", + .air_passage_anchors = _cooling_side_182820_2, + .anchors_data_size = ARRAY_SIZE(_cooling_side_182820_2), + .recalculated_required = true, + .airflow_pressure_unit = KPA, + .min_barometric_pressure = 840.0, + .max_barometric_pressure = 1070.0, + .min_airflow_temperature = 15.0, + .max_airflow_temperature = 35.0, + .min_airflow_pressure = 0.01, + .max_airflow_pressure = 2.0}, + {.air_passage_description = "Bleed Air Side", + .air_passage_anchors = _hot_side_182820_2, + .anchors_data_size = ARRAY_SIZE(_hot_side_182820_2), + .recalculated_required = true, + .airflow_pressure_unit = PSIG, + .min_barometric_pressure = 840.0, + .max_barometric_pressure = 1070.0, + .min_airflow_temperature = 15.0, + .max_airflow_temperature = 35.0, + .min_airflow_pressure = 0.01, + .max_airflow_pressure = 15.0}}; + + static const air_passage_data_t _air_passage_182820_3_data[] = { + {.air_passage_description = "Cooling Air Side", + .air_passage_anchors = _cooling_side_182820_3, + .anchors_data_size = ARRAY_SIZE(_cooling_side_182820_3), + .recalculated_required = true, + .airflow_pressure_unit = KPA, + .min_barometric_pressure = 840.0, + .max_barometric_pressure = 1070.0, + .min_airflow_temperature = 15.0, + .max_airflow_temperature = 35.0, + .min_airflow_pressure = 0.01, + .max_airflow_pressure = 2.0}, + {.air_passage_description = "Bleed Air Side", + .air_passage_anchors = _hot_side_182820_3, + .anchors_data_size = ARRAY_SIZE(_hot_side_182820_3), + .recalculated_required = true, + .airflow_pressure_unit = PSIG, + .min_barometric_pressure = 840.0, + .max_barometric_pressure = 1070.0, + .min_airflow_temperature = 15.0, + .max_airflow_temperature = 35.0, + .min_airflow_pressure = 0.01, + .max_airflow_pressure = 15.0}}; + + static const air_passage_data_t _air_passage_182820_4_data[] = { + {.air_passage_description = "Cooling Air Side", + .air_passage_anchors = _cooling_side_182820_4, + .anchors_data_size = ARRAY_SIZE(_cooling_side_182820_4), + .recalculated_required = true, + .airflow_pressure_unit = KPA, + .min_barometric_pressure = 840.0, + .max_barometric_pressure = 1070.0, + .min_airflow_temperature = 15.0, + .max_airflow_temperature = 35.0, + .min_airflow_pressure = 0.01, + .max_airflow_pressure = 2.0}, + {.air_passage_description = "Bleed Air Side", + .air_passage_anchors = _hot_side_182820_4, + .anchors_data_size = ARRAY_SIZE(_hot_side_182820_4), + .recalculated_required = true, + .airflow_pressure_unit = PSIG, + .min_barometric_pressure = 840.0, + .max_barometric_pressure = 1070.0, + .min_airflow_temperature = 15.0, + .max_airflow_temperature = 35.0, + .min_airflow_pressure = 0.01, + .max_airflow_pressure = 15.0}}; + + static const heat_exchanger_type_data_t heat_exchanger_21_50_14_data[] = { + {.part_number_description = "182820-1", + .air_passages_data = _air_passage_182820_1_data, + .air_passages_data_size = ARRAY_SIZE(_air_passage_182820_1_data)}, + {.part_number_description = "182820-2", + .air_passages_data = _air_passage_182820_2_data, + .air_passages_data_size = ARRAY_SIZE(_air_passage_182820_2_data)}, + {.part_number_description = "182820-3", + .air_passages_data = _air_passage_182820_3_data, + .air_passages_data_size = ARRAY_SIZE(_air_passage_182820_3_data)}, + {.part_number_description = "182820-4", + .air_passages_data = _air_passage_182820_4_data, + .air_passages_data_size = ARRAY_SIZE(_air_passage_182820_4_data)}}; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/ats0002/21_53_88.h b/ats0002/21_53_88.h new file mode 100644 index 0000000..75afca0 --- /dev/null +++ b/ats0002/21_53_88.h @@ -0,0 +1,154 @@ +#pragma once + +#include "ats0002.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @file 21_53_88.h + * + * @brief Liebherr Condenser CMM 21-53-88 configuration data. + * + * This module provides static configuration data for Liebherr Condenser CMM + * 21-53-88 heat exchanger, including pressure-to-airflow anchor points for + * cooling and hot sides, air passage definitions, and heat exchanger type + * mappings for part numbers 969A0000-01 and 969A0000-02. + * + * Key features: + * - Cooling side performance data (Figure 4002) + * - Hot side performance data (Figures 4003 and 4004) + * - Complete air passage configuration with operational limits + * + * @note All data arrays are static and const-qualified for read-only access. + */ + + /** + * @brief Liebherr Condenser CMM 21-53-88, Figure 4002 (P/N 969A0000-01). + * + * Cooling side dependence of ΔPS (KPA) on air flow rate (LB/MIN), UPPER line (+20%). + */ + static const airflow_to_pressure_anchors_data_t _cooling_side_969A0000_01[] = { + {25.0, 0.26}, + {30.0, 0.36}, + {40.0, 0.61}, + {50.0, 0.91}, + {60.0, 1.26}, + {70.0, 1.75}, + {80.0, 2.33}, + {90.0, 2.80}, + {100.0, 3.50}, + {200.0, 13.20}}; + + /** + * @brief Liebherr Condenser CMM 21-53-88, Figure 4002 (P/N 969A0000-02). + * + * Cooling side dependence of ΔPS (KPA) on air flow rate (LB/MIN), UPPER line (+20%). + */ + static const airflow_to_pressure_anchors_data_t _cooling_side_969A0000_02[] = { + {25.0, 0.26}, + {30.0, 0.36}, + {40.0, 0.61}, + {50.0, 0.91}, + {60.0, 1.26}, + {70.0, 1.75}, + {80.0, 2.33}, + {90.0, 2.80}, + {100.0, 3.50}, + {200.0, 13.20}}; + + /** + * @brief Liebherr Condenser CMM 21-53-88, Figure 4003 (P/N 969A0000-01). + * + * Hot side dependence of ΔPS (PSIG) on air flow rate (LB/MIN), UPPER line (+20%). + */ + static const airflow_to_pressure_anchors_data_t _hot_side_969A0000_01[] = { + {20.0, 0.25}, + {30.0, 0.48}, + {40.0, 0.80}, + {50.0, 1.25}, + {60.0, 1.67}, + {70.0, 2.25}, + {80.0, 2.75}, + {90.0, 3.57}, + {100.0, 4.70}, + {120.0, 11.00}}; + + /** + * @brief Liebherr Condenser CMM 21-53-88, Figure 4004 (P/N 969A0000-02). + * + * Hot side dependence of ΔPS (PSIG) on air flow rate (LB/MIN), UPPER line (+20%). + */ + static const airflow_to_pressure_anchors_data_t _hot_side_969A0000_02[] = { + {20.0, 0.17}, + {30.0, 0.34}, + {40.0, 0.57}, + {50.0, 0.83}, + {60.0, 1.18}, + {70.0, 1.56}, + {80.0, 1.93}, + {90.0, 2.55}, + {100.0, 3.42}, + {120.0, 9.51}}; + + static const air_passage_data_t _air_passage_969A0000_01_data[] = { + {.air_passage_description = "Cold Air Side", + .air_passage_anchors = _cooling_side_969A0000_01, + .anchors_data_size = ARRAY_SIZE(_cooling_side_969A0000_01), + .recalculated_required = false, + .airflow_pressure_unit = KPA, + .min_barometric_pressure = 840.0, + .max_barometric_pressure = 1070.0, + .min_airflow_temperature = 15.0, + .max_airflow_temperature = 33.0, + .min_airflow_pressure = 0.01, + .max_airflow_pressure = 2.0}, + {.air_passage_description = "Hot Air Side", + .air_passage_anchors = _hot_side_969A0000_01, + .anchors_data_size = ARRAY_SIZE(_hot_side_969A0000_01), + .recalculated_required = false, + .airflow_pressure_unit = PSIG, + .min_barometric_pressure = 840.0, + .max_barometric_pressure = 1070.0, + .min_airflow_temperature = 15.0, + .max_airflow_temperature = 33.0, + .min_airflow_pressure = 0.01, + .max_airflow_pressure = 15.0}}; + + static const air_passage_data_t _air_passage_969A0000_02_data[] = { + {.air_passage_description = "Cold Air Side", + .air_passage_anchors = _cooling_side_969A0000_02, + .anchors_data_size = ARRAY_SIZE(_cooling_side_969A0000_02), + .recalculated_required = false, + .airflow_pressure_unit = KPA, + .min_barometric_pressure = 840.0, + .max_barometric_pressure = 1070.0, + .min_airflow_temperature = 15.0, + .max_airflow_temperature = 33.0, + .min_airflow_pressure = 0.01, + .max_airflow_pressure = 2.0}, + {.air_passage_description = "Hot Air Side", + .air_passage_anchors = _hot_side_969A0000_02, + .anchors_data_size = ARRAY_SIZE(_hot_side_969A0000_02), + .recalculated_required = false, + .airflow_pressure_unit = PSIG, + .min_barometric_pressure = 840.0, + .max_barometric_pressure = 1070.0, + .min_airflow_temperature = 15.0, + .max_airflow_temperature = 33.0, + .min_airflow_pressure = 0.01, + .max_airflow_pressure = 15.0}}; + + static const heat_exchanger_type_data_t heat_exchanger_21_53_88_data[] = { + {.part_number_description = "969A0000-01", + .air_passages_data = _air_passage_969A0000_01_data, + .air_passages_data_size = ARRAY_SIZE(_air_passage_969A0000_01_data)}, + {.part_number_description = "969A0000-02", + .air_passages_data = _air_passage_969A0000_02_data, + .air_passages_data_size = ARRAY_SIZE(_air_passage_969A0000_02_data)}}; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/ats0002/21_53_89.h b/ats0002/21_53_89.h new file mode 100644 index 0000000..292383f --- /dev/null +++ b/ats0002/21_53_89.h @@ -0,0 +1,154 @@ +#pragma once + +#include "ats0002.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @file 21_53_89.h + * + * @brief Liebherr Reheater CMM 21-53-89 configuration data. + * + * This module provides static configuration data for Liebherr Reheater CMM + * 21-53-89 heat exchanger, including pressure-to-airflow anchor points for + * cooling and hot sides, air passage definitions, and heat exchanger type + * mappings for part numbers 968B0000-01 and 968A0000-03. + * + * Key features: + * - Cooling side performance data (Figures 4002 and 4003) + * - Hot side performance data (Figures 4004 and 4005) + * - Complete air passage configuration with operational limits + * + * @note All data arrays are static and const-qualified for read-only access. + */ + + /** + * @brief Liebherr Reheater CMM 21-53-89, Figure 4002 (P/N 968B0000-01). + * + * Cooling side dependence of ΔPS (PSIG) on air flow rate (LB/MIN), UPPER line (+20%). + */ + static const airflow_to_pressure_anchors_data_t _cooling_side_968B0000_01[] = { + {20.0, 0.11}, + {30.0, 0.22}, + {40.0, 0.37}, + {50.0, 0.55}, + {60.0, 0.78}, + {70.0, 1.00}, + {80.0, 1.40}, + {90.0, 1.70}, + {100.0, 2.00}, + {200.0, 7.00}}; + + /** + * @brief Liebherr Reheater CMM 21-53-89, Figure 4003 (P/N 968A0000-03). + * + * Cooling side dependence of ΔPS (PSIG) on air flow rate (LB/MIN), UPPER line (+20%). + */ + static const airflow_to_pressure_anchors_data_t _cooling_side_968A0000_03[] = { + {20.0, 0.09}, + {30.0, 0.19}, + {40.0, 0.32}, + {50.0, 0.47}, + {60.0, 0.65}, + {70.0, 0.85}, + {80.0, 1.10}, + {90.0, 1.40}, + {100.0, 1.70}, + {200.0, 5.60}}; + + /** + * @brief Liebherr Reheater CMM 21-53-89, Figure 4004 (P/N 968B0000-01). + * + * Hot side dependence of ΔPS (PSIG) on air flow rate (LB/MIN), UPPER line (+20%). + */ + static const airflow_to_pressure_anchors_data_t _hot_side_968B0000_01[] = { + {20.0, 0.09}, + {30.0, 0.19}, + {40.0, 0.32}, + {50.0, 0.47}, + {60.0, 0.65}, + {70.0, 0.85}, + {80.0, 1.10}, + {90.0, 1.40}, + {100.0, 1.70}, + {200.0, 5.60}}; + + /** + * @brief Liebherr Reheater CMM 21-53-89, Figure 4005 (P/N 968A0000-03). + * + * Hot side dependence of ΔPS (PSIG) on air flow rate (LB/MIN), UPPER line (+20%). + */ + static const airflow_to_pressure_anchors_data_t _hot_side_968A0000_03[] = { + {20.0, 0.08}, + {30.0, 0.17}, + {40.0, 0.27}, + {50.0, 0.40}, + {60.0, 0.55}, + {70.0, 0.72}, + {80.0, 0.91}, + {90.0, 1.25}, + {100.0, 1.45}, + {200.0, 4.70}}; + + static const air_passage_data_t _air_passage_968B0000_01_data[] = { + {.air_passage_description = "Cold Air Side", + .air_passage_anchors = _cooling_side_968B0000_01, + .anchors_data_size = ARRAY_SIZE(_cooling_side_968B0000_01), + .recalculated_required = false, + .airflow_pressure_unit = PSIG, + .min_barometric_pressure = 840.0, + .max_barometric_pressure = 1070.0, + .min_airflow_temperature = 15.0, + .max_airflow_temperature = 35.0, + .min_airflow_pressure = 0.01, + .max_airflow_pressure = 15.0}, + {.air_passage_description = "Hot Air Side", + .air_passage_anchors = _hot_side_968B0000_01, + .anchors_data_size = ARRAY_SIZE(_hot_side_968B0000_01), + .recalculated_required = false, + .airflow_pressure_unit = PSIG, + .min_barometric_pressure = 840.0, + .max_barometric_pressure = 1070.0, + .min_airflow_temperature = 15.0, + .max_airflow_temperature = 35.0, + .min_airflow_pressure = 0.01, + .max_airflow_pressure = 15.0}}; + + static const air_passage_data_t _air_passage_968A0000_03_data[] = { + {.air_passage_description = "Cold Air Side", + .air_passage_anchors = _cooling_side_968A0000_03, + .anchors_data_size = ARRAY_SIZE(_cooling_side_968A0000_03), + .recalculated_required = false, + .airflow_pressure_unit = PSIG, + .min_barometric_pressure = 840.0, + .max_barometric_pressure = 1070.0, + .min_airflow_temperature = 15.0, + .max_airflow_temperature = 35.0, + .min_airflow_pressure = 0.01, + .max_airflow_pressure = 15.0}, + {.air_passage_description = "Hot Air Side", + .air_passage_anchors = _hot_side_968A0000_03, + .anchors_data_size = ARRAY_SIZE(_hot_side_968A0000_03), + .recalculated_required = false, + .airflow_pressure_unit = PSIG, + .min_barometric_pressure = 840.0, + .max_barometric_pressure = 1070.0, + .min_airflow_temperature = 15.0, + .max_airflow_temperature = 35.0, + .min_airflow_pressure = 0.01, + .max_airflow_pressure = 15.0}}; + + static const heat_exchanger_type_data_t heat_exchanger_21_53_89_data[] = { + {.part_number_description = "968B0000-01", + .air_passages_data = _air_passage_968B0000_01_data, + .air_passages_data_size = ARRAY_SIZE(_air_passage_968B0000_01_data)}, + {.part_number_description = "968A0000-03", + .air_passages_data = _air_passage_968A0000_03_data, + .air_passages_data_size = ARRAY_SIZE(_air_passage_968A0000_03_data)}}; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/ats0002/ats0002.c b/ats0002/ats0002.c new file mode 100644 index 0000000..b2281b4 --- /dev/null +++ b/ats0002/ats0002.c @@ -0,0 +1,333 @@ +#include "ats0002.h" + +/** + * @brief Normalize the decimal separator in a string from comma to dot. + * + * Replaces all comma characters with dots in-place, supporting locales + * that use comma as the decimal separator. + * + * @param str Pointer to the string to normalize (must not be NULL) + */ +static void _normalize_decimal_separator(char *str); + +/** + * @brief Get a numeric value or special commands from user input. + * + * Reads a line from standard input, handles EOF and special commands + * ('q'/'Q' for EXIT, 'r'/'R' for REPEAT), normalizes the decimal + * separator, and converts the input to a double value. + * + * @param data Pointer to store the parsed numeric value or EXIT/REPEAT + */ +static void _get_user_input(double *data); + +/** + * @brief Get a menu selection from user input with validation. + * + * Prompts the user for a selection, validates that the value is within + * the specified range, and handles EXIT and REPEAT commands. + * + * @param result Pointer to store the selected value (must not be NULL) + * @param size Number of available options + * @param prompt Prompt message to display to the user + * @param error_msg Error message to display for invalid input + */ +static void _get_selection(uint16_t *result, uint8_t size, const char *prompt, const char *error_msg); + +/** + * @brief Get a double value from user input within a specified range. + * + * Prompts the user for a value, validates it is within the given range, + * and handles EXIT and REPEAT commands. Supports REPEAT for re-prompting + * without terminating. + * + * @param result Pointer to store the entered value (must not be NULL) + * @param min Minimum allowed value + * @param max Maximum allowed value + * @param prompt Prompt message to display to the user + * @param error_msg Error message to display for invalid input + */ +static void _get_double_in_range(double *result, double min, double max, const char *prompt, const char *error_msg); + +/** + * @brief Get a log-interpolated pressure drop limit based on airflow. + * + * Reads a pressure drop limit from the user and calculates the interpolated + * value using logarithmic interpolation on the anchor points from the + * provided air passage data. + * + * @param limit Pointer to store the calculated pressure drop limit + * @param airflow Pointer to the current airflow value + * @param data Pointer to air passage data containing anchor points (must not be NULL) + */ +static void _get_log_interpolated_airflow_pressure_drop_limit(double *limit, const double *airflow, const air_passage_data_t *data); + +/** + * @brief Calculate density-corrected static inlet pressure. + * + * Applies barometric pressure and temperature compensation to the raw + * airflow pressure value, producing a density-corrected result in HPA + * (hectopascal) units. + * + * @param corrected_airflow_pressure Pointer to store the corrected pressure + * @param airflow_pressure Pointer to the raw static inlet pressure value + * @param barometric_pressure Pointer to the current barometric pressure + * @param airflow_temperature Pointer to the current air temperature in Celsius + */ +static void _calculate_density_corrected_pressure(double *corrected_airflow_pressure, const double *airflow_pressure, const double *barometric_pressure, const double *airflow_temperature); + +void get_user_command(uint16_t *user_command) +{ + printf("Enter 'q' to quit or 'r' to REPEAT:\n"); + for (;;) + { + double input = 0.0; + _get_user_input(&input); + if (input == (double)EXIT) + { + *user_command = EXIT; + return; + } + if (input == (double)REPEAT) + { + *user_command = REPEAT; + return; + } + printf("Incorrect command. Try again.\n"); + } +} + +static void _get_selection(uint16_t *result, uint8_t size, const char *prompt, const char *error_msg) +{ + printf("%s (or 'q' to quit):\n", prompt); + for (;;) + { + double input = 0.0; + _get_user_input(&input); + + if (input == (double)EXIT) + { + *result = EXIT; + return; + } + if (input >= 1.0 && input <= (double)size) + { + uint16_t val = (uint16_t)input; + if ((double)val == input) + { + *result = val - 1; + return; + } + } + printf("%s\n", error_msg); + } +} + +void get_heat_exchanger_number(uint16_t *num, uint8_t size) +{ + _get_selection(num, size, "Enter the heat exchanger number", "Incorrect heat exchanger number. Try again."); +} + +void get_heat_exchanger_part_number(uint16_t *num, uint8_t size) +{ + _get_selection(num, size, "Enter the heat exchanger part number", "Incorrect heat exchanger part number. Try again."); +} + +void get_heat_exchanger_air_passage_number(uint16_t *num, uint8_t size) +{ + _get_selection(num, size, "Enter the heat exchanger air passage number", "Incorrect heat exchanger air passage number. Try again."); +} + +static void _get_double_in_range(double *result, double min, double max, const char *prompt, const char *error_msg) +{ + printf("%s (or 'q' to quit):\n", prompt); + for (;;) + { + double input = 0.0; + _get_user_input(&input); + if (input == (double)EXIT) + { + *result = (double)EXIT; + return; + } + if (input == (double)REPEAT) + { + printf("%s\n", error_msg); + continue; + } + if (input >= min && input <= max) + { + *result = input; + return; + } + printf("%s\n", error_msg); + } +} + +void get_airflow(double *airflow, const air_passage_data_t *data) +{ + double min = data->air_passage_anchors[0].airflow; + double max = data->air_passage_anchors[data->anchors_data_size - 1].airflow; + _get_double_in_range(airflow, min, max, "Enter airflow in LB/MIN", "Incorrect airflow. Try again."); +} + +void get_airflow_pressure(double *airflow_pressure, const air_passage_data_t *data) +{ + double min = data->min_airflow_pressure; + double max = data->max_airflow_pressure; + const char *prompt = (data->airflow_pressure_unit == KPA) ? "Enter static inlet pressure in KPA" : "Enter static inlet pressure in PSIG"; + _get_double_in_range(airflow_pressure, min, max, prompt, "Incorrect static inlet pressure. Try again."); +} + +void get_barometric_pressure(double *barometric_pressure, const air_passage_data_t *data) +{ + double min = data->min_barometric_pressure; + double max = data->max_barometric_pressure; + _get_double_in_range(barometric_pressure, min, max, "Enter barometric pressure in MBAR", "Incorrect barometric pressure. Try again."); +} + +void get_airflow_temperature(double *airflow_temperature, const air_passage_data_t *data) +{ + double min = data->min_airflow_temperature; + double max = data->max_airflow_temperature; + _get_double_in_range(airflow_temperature, min, max, "Enter air temperature in C", "Incorrect air temperature. Try again."); +} + +void get_pressure_drop(double *pressure_drop_limit, const double *airflow, const double *airflow_pressure, double *corrected_airflow_pressure, const double *barometric_pressure, const double *airflow_temperature, const air_passage_data_t *data) +{ + if (data->recalculated_required == true) + { + double hpa_pressure = 0.0; + double hpa_corrected_pressure = 0.0; + switch (data->airflow_pressure_unit) + { + case KPA: + hpa_pressure = *airflow_pressure * 10.0; + _calculate_density_corrected_pressure(&hpa_corrected_pressure, &hpa_pressure, barometric_pressure, airflow_temperature); + *corrected_airflow_pressure = hpa_corrected_pressure / 10.0; + break; + case PSIG: + hpa_pressure = *airflow_pressure * 68.9476; + _calculate_density_corrected_pressure(&hpa_corrected_pressure, &hpa_pressure, barometric_pressure, airflow_temperature); + *corrected_airflow_pressure = hpa_corrected_pressure * 0.0145038; + break; + default: + break; + } + } + _get_log_interpolated_airflow_pressure_drop_limit(pressure_drop_limit, airflow, data); +} + +static void _normalize_decimal_separator(char *str) +{ + char *src = str; + char *dst = str; + while (*src != '\0') + { + if (*src == ',') + { + *dst++ = '.'; + src++; + } + else + { + *dst++ = *src++; + } + } + *dst = '\0'; +} + +static void _get_user_input(double *data) +{ + char input[64]; + for (;;) + { + printf("> "); + fflush(stdout); + if (fgets(input, sizeof(input), stdin) == NULL) + { + if (feof(stdin)) + { + *data = EXIT; + return; + } + printf("Input error. Try again.\n"); + continue; + } + if (strchr(input, '\n') == NULL) + { + int c; + while ((c = getchar()) != '\n' && c != EOF) + { + } + } + input[strcspn(input, "\n")] = '\0'; + if (strcmp(input, "q") == 0 || strcmp(input, "Q") == 0) + { + *data = EXIT; + break; + } + if (strcmp(input, "r") == 0 || strcmp(input, "R") == 0) + { + *data = REPEAT; + break; + } + _normalize_decimal_separator(input); + char *endptr = NULL; + *data = strtod(input, &endptr); + if (endptr == input) + { + printf("Error: Not a valid number. Try again.\n"); + continue; + } + while (*endptr == ' ') + { + endptr++; + } + if (*endptr != '\0') + { + printf("Error: Unexpected characters after number. Try again.\n"); + continue; + } + break; + } +} + +static void _get_log_interpolated_airflow_pressure_drop_limit(double *limit, const double *airflow, const air_passage_data_t *data) +{ + if (*airflow <= data->air_passage_anchors[0].airflow) + { + *limit = data->air_passage_anchors[0].pressure; + return; + } + if (*airflow >= data->air_passage_anchors[data->anchors_data_size - 1].airflow) + { + *limit = data->air_passage_anchors[data->anchors_data_size - 1].pressure; + return; + } + for (uint8_t i = 0; i < data->anchors_data_size - 1; ++i) + { + if (*airflow >= data->air_passage_anchors[i].airflow && + *airflow <= data->air_passage_anchors[i + 1].airflow) + { + double log_x = log(*airflow); + double log_x1 = log(data->air_passage_anchors[i].airflow); + double log_x2 = log(data->air_passage_anchors[i + 1].airflow); + double log_y1 = log(data->air_passage_anchors[i].pressure); + double log_y2 = log(data->air_passage_anchors[i + 1].pressure); + double log_y = log_y1 + (log_x - log_x1) * (log_y2 - log_y1) / (log_x2 - log_x1); + *limit = exp(log_y); + return; + } + } + *limit = -1.0; +} + +static void _calculate_density_corrected_pressure(double *corrected_airflow_pressure, const double *airflow_pressure, const double *barometric_pressure, const double *airflow_temperature) +{ + double value_4 = *airflow_pressure * 0.5; + double value_5 = *barometric_pressure + value_4; + double value_6 = *airflow_temperature + 273.2; + double value_7 = (0.28443 * value_5) / value_6; + *corrected_airflow_pressure = *airflow_pressure * value_7; +} \ No newline at end of file diff --git a/ats0002/ats0002.h b/ats0002/ats0002.h new file mode 100644 index 0000000..cde4e90 --- /dev/null +++ b/ats0002/ats0002.h @@ -0,0 +1,207 @@ +#pragma once + +/** + * @file ats0002.h + * + * @brief Heat exchanger data management and user input handling for ATS0002. + * + * This module provides data structures and functions for managing heat + * exchanger configuration data including air passage descriptions, pressure + * to airflow anchor points, and user interaction functions for selecting + * heat exchanger models and retrieving operational parameters. + * + * Key features: + * - Hierarchical data structures for heat exchanger, type, and air passage configuration + * - User input functions for command selection and parameter retrieval + * - Pressure drop calculation support with temperature and barometric compensation + * + * @note All data structures use const pointers to read-only configuration data. + */ + +#include "stdint.h" +#include "stdio.h" +#include "stdbool.h" +#include "stdlib.h" +#include "string.h" +#include "math.h" +#include "locale.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +#define EXIT 0xFFFF +#define REPEAT 0xFFEE + +#define EXIT_CHECK(cond) \ + if (cond == EXIT) \ + { \ + printf("Goodbye.\n"); \ + return 0; \ + } + +#define ASK_CHOICE(var, getter, count) \ + (var) = 0; \ + getter(&(var), (count)); \ + EXIT_CHECK(var); + +typedef enum +{ + KPA, + PSIG +} airflow_pressure_unit_t; + +/** + * @brief Airflow to pressure anchor point data structure. + * + * Represents a pair of airflow and corresponding pressure values used as + * interpolation anchor points for heat exchanger performance curves. + */ +typedef struct +{ + double airflow; /*!< Airflow rate value (LB/MIN) */ + double pressure; /*!< Corresponding pressure drop value (KPA or PSIG) */ +} airflow_to_pressure_anchors_data_t; + +/** + * @brief Air passage configuration data structure. + * + * Contains description, pressure-to-airflow anchor points, and operational + * parameter limits for a single air passage within a heat exchanger. + */ +typedef struct +{ + const char *air_passage_description; /*!< Human-readable description of the air passage */ + const airflow_to_pressure_anchors_data_t *air_passage_anchors; /*!< Pointer to anchor point array */ + const uint8_t anchors_data_size; /*!< Number of anchor points in the array */ + const bool recalculated_required; /*!< Flag indicating if recalculation is required */ + const airflow_pressure_unit_t airflow_pressure_unit; + const double min_barometric_pressure; /*!< Minimum allowed barometric pressure (Pa) */ + const double max_barometric_pressure; /*!< Maximum allowed barometric pressure (Pa) */ + const double min_airflow_temperature; /*!< Minimum allowed air temperature (C) */ + const double max_airflow_temperature; /*!< Maximum allowed air temperature (C) */ + const double min_airflow_pressure; /*!< Minimum allowed air pressure (KPA or PSIG) */ + const double max_airflow_pressure; /*!< Maximum allowed air pressure (KPA or PSIG) */ +} air_passage_data_t; + +/** + * @brief Heat exchanger type configuration data structure. + * + * Contains description and an array of air passage configurations for a + * specific heat exchanger part number. + */ +typedef struct +{ + const char *part_number_description; /*!< Human-readable part number description */ + const air_passage_data_t *air_passages_data; /*!< Pointer to air passage configuration array */ + const uint8_t air_passages_data_size; /*!< Number of air passages in the array */ +} heat_exchanger_type_data_t; + +/** + * @brief Heat exchanger configuration data structure. + * + * Contains description and an array of heat exchanger type configurations + * representing all supported part numbers for a given heat exchanger model. + */ +typedef struct +{ + const char *heat_exchanger_description; /*!< Human-readable heat exchanger model description */ + const heat_exchanger_type_data_t *heat_exchanger_type_data; /*!< Pointer to heat exchanger type array */ + const uint8_t heat_exchanger_type_data_size; /*!< Number of heat exchanger types in the array */ +} heat_exchanger_data_t; + +/** + * @brief Retrieve a user command from standard input. + * + * Reads a command selection from the user. The user can select a numeric + * command or enter EXIT to terminate the program. + * + * @param[out] user_command Pointer to store the selected command value + */ +void get_user_command(uint16_t *user_command); + +/** + * @brief Retrieve a heat exchanger model number from user input. + * + * Prompts the user to select a heat exchanger model from the available options. + * + * @param[out] heat_exchanger_number Pointer to store the selected model number + * @param[in] structure_size Number of available heat exchanger models + */ +void get_heat_exchanger_number(uint16_t *heat_exchanger_number, uint8_t structure_size); + +/** + * @brief Retrieve a heat exchanger part number from user input. + * + * Prompts the user to select a part number for the selected heat exchanger model. + * + * @param[out] heat_exchanger_part_number Pointer to store the selected part number + * @param[in] structure_size Number of available part numbers + */ +void get_heat_exchanger_part_number(uint16_t *heat_exchanger_part_number, uint8_t structure_size); + +/** + * @brief Retrieve an air passage number from user input. + * + * Prompts the user to select an air passage for the selected heat exchanger part. + * + * @param[out] heat_exchanger_air_passage_number Pointer to store the selected passage number + * @param[in] structure_size Number of available air passages + */ +void get_heat_exchanger_air_passage_number(uint16_t *heat_exchanger_air_passage_number, uint8_t structure_size); + +/** + * @brief Retrieve an airflow value from user input. + * + * Validates the entered airflow against the operational limits defined in the + * provided air passage data. + * + * @param[out] airflow Pointer to store the entered airflow value + * @param[in] data Pointer to air passage data containing operational limits (must not be NULL) + */ +void get_airflow(double *airflow, const air_passage_data_t *data); + +/** + * @brief Retrieve an airflow pressure value from user input. + * + * Validates the entered pressure against the operational limits defined in the + * provided air passage data. + * + * @param[out] airflow_pressure Pointer to store the entered pressure value + * @param[in] data Pointer to air passage data containing operational limits (must not be NULL) + */ +void get_airflow_pressure(double *airflow_pressure, const air_passage_data_t *data); + +/** + * @brief Retrieve a barometric pressure value from user input. + * + * Validates the entered barometric pressure against the operational limits + * defined in the provided air passage data. + * + * @param[out] barometric_pressure Pointer to store the entered pressure value + * @param[in] data Pointer to air passage data containing operational limits (must not be NULL) + */ +void get_barometric_pressure(double *barometric_pressure, const air_passage_data_t *data); + +/** + * @brief Retrieve an airflow temperature value from user input. + * + * Validates the entered temperature against the operational limits defined in + * the provided air passage data. + * + * @param[out] airflow_temperature Pointer to store the entered temperature value + * @param[in] data Pointer to air passage data containing operational limits (must not be NULL) + */ +void get_airflow_temperature(double *airflow_temperature, const air_passage_data_t *data); + +/** + * @brief Retrieve a pressure drop limit value from user input. + * + * Reads a pressure drop limit from the user and validates it against the + * operational limits defined in the provided air passage data. + * + * @param[out] pressure_drop_limit Pointer to store the entered pressure drop limit + * @param[in] airflow Pointer to store the current airflow value + * @param[in] barometric_pressure Pointer to store the current barometric pressure value + * @param[in] airflow_temperature Pointer to store the current airflow temperature value + * @param[in] data Pointer to air passage data containing operational limits (must not be NULL) + */ +void get_pressure_drop(double *pressure_drop_limit, const double *airflow, const double *airflow_pressure, double *corrected_airflow_pressure, const double *barometric_pressure, const double *airflow_temperature, const air_passage_data_t *data); \ No newline at end of file diff --git a/heat_exchander_airflow_pressure_drop b/heat_exchander_airflow_pressure_drop new file mode 100755 index 0000000..05f1171 Binary files /dev/null and b/heat_exchander_airflow_pressure_drop differ diff --git a/heat_exchander_airflow_pressure_drop.c b/heat_exchander_airflow_pressure_drop.c new file mode 100644 index 0000000..bfe7a43 --- /dev/null +++ b/heat_exchander_airflow_pressure_drop.c @@ -0,0 +1,103 @@ +#include "ats0002/ats0002.h" +#include "ats0002/21_53_89.h" +#include "ats0002/21_53_88.h" +#include "ats0002/21_50_14.h" + +static const heat_exchanger_data_t heat_exchanger_data[] = { + {"Honeywell Heat Exchanger CMM 21-50-14", heat_exchanger_21_50_14_data, ARRAY_SIZE(heat_exchanger_21_50_14_data)}, + {"Liebherr Reheater CMM 21-53-89", heat_exchanger_21_53_89_data, ARRAY_SIZE(heat_exchanger_21_53_89_data)}, + {"Liebherr Condenser CMM 21-53-88", heat_exchanger_21_53_88_data, ARRAY_SIZE(heat_exchanger_21_53_88_data)}}; + +int main(void) +{ + setlocale(LC_NUMERIC, "C"); + for (;;) + { +#ifdef _WIN32 + system("cls"); +#else + system("clear"); +#endif + printf("************************************************************\n" + "* *\n" + "* Heat Exchanger Airflow Pressure Drop Calculation Program *\n" + "* *\n" + "* Developed by @rick_professor *\n" + "* Participation by @cabanleo *\n" + "* Property of Aerotech LLC *\n" + "* *\n" + "************************************************************\n" + "\n"); + for (uint8_t i = 0; i < ARRAY_SIZE(heat_exchanger_data); ++i) + { + printf("%d. %s\n", i + 1, heat_exchanger_data[i].heat_exchanger_description); + } + printf("\n"); + uint16_t heat_exchanger_number = 0; + ASK_CHOICE(heat_exchanger_number, get_heat_exchanger_number, ARRAY_SIZE(heat_exchanger_data)); + printf("\n"); + const heat_exchanger_data_t *he = &heat_exchanger_data[heat_exchanger_number]; + for (uint8_t i = 0; i < he->heat_exchanger_type_data_size; ++i) + { + printf("%d. %s\n", i + 1, he->heat_exchanger_type_data[i].part_number_description); + } + printf("\n"); + uint16_t part_number = 0; + ASK_CHOICE(part_number, get_heat_exchanger_part_number, he->heat_exchanger_type_data_size); + printf("\n"); + const heat_exchanger_type_data_t *hetd = &he->heat_exchanger_type_data[part_number]; + for (uint8_t i = 0; i < hetd->air_passages_data_size; ++i) + { + printf("%d. %s\n", i + 1, hetd->air_passages_data[i].air_passage_description); + } + printf("\n"); + uint16_t air_passage_number = 0; + ASK_CHOICE(air_passage_number, get_heat_exchanger_air_passage_number, hetd->air_passages_data_size); + printf("\n"); + const air_passage_data_t *apd = &hetd->air_passages_data[air_passage_number]; + double airflow = 0.0; + get_airflow(&airflow, apd); + EXIT_CHECK(airflow); + printf("\n"); + double airflow_pressure = 0.0; + get_airflow_pressure(&airflow_pressure, apd); + EXIT_CHECK(airflow_pressure); + printf("\n"); + double barometric_pressure = 0.0; + double airflow_temperature = 0.0; + if (apd->recalculated_required == true) + { + get_barometric_pressure(&barometric_pressure, apd); + EXIT_CHECK(barometric_pressure); + printf("\n"); + get_airflow_temperature(&airflow_temperature, apd); + EXIT_CHECK(airflow_temperature); + printf("\n"); + } + double pressure_drop_limit = 0.0; + double corrected_airflow_pressure = 0.0; + get_pressure_drop(&pressure_drop_limit, &airflow, &airflow_pressure, &corrected_airflow_pressure, &barometric_pressure, &airflow_temperature, apd); + const char *unit = apd->airflow_pressure_unit == KPA ? "KPA" : "PSIG"; + printf("****************** Test summary *******************\n"); + printf("Name: %s\n", he->heat_exchanger_description); + printf("P/N: %s\n", he->heat_exchanger_type_data[part_number].part_number_description); + printf("Side: %s\n", hetd->air_passages_data[air_passage_number].air_passage_description); + printf("Airflow: %0.2f LB/MIN\n", airflow); + printf("Static inlet pressure: %0.2f %s\n", airflow_pressure, unit); + if (apd->recalculated_required == true) + { + printf("Barometric pressure: %0.2f MBAR\n", barometric_pressure); + printf("Airflow temperature: %0.2f C\n", airflow_temperature); + printf("Corrected static inlet pressure: %0.2f %s\n", corrected_airflow_pressure, unit); + } + printf("Max allowed pressure drop: %0.2f %s\n", pressure_drop_limit, unit); + double test_pressure = apd->recalculated_required == true ? corrected_airflow_pressure : airflow_pressure; + printf("Test result: %s\n", test_pressure <= pressure_drop_limit ? "PASSED" : "FAILED"); + printf("***************************************************\n"); + printf("\n"); + uint16_t command = 0; + get_user_command(&command); + EXIT_CHECK(command); + } + return 0; +} \ No newline at end of file diff --git a/heat_exchander_airflow_pressure_drop.exe b/heat_exchander_airflow_pressure_drop.exe new file mode 100755 index 0000000..7db0a55 Binary files /dev/null and b/heat_exchander_airflow_pressure_drop.exe differ diff --git a/include/main.h b/include/main.h deleted file mode 100644 index e69de29..0000000 diff --git a/main.c b/main.c deleted file mode 100644 index e69de29..0000000 diff --git a/version.txt b/version.txt index e69de29..afaf360 100644 --- a/version.txt +++ b/version.txt @@ -0,0 +1 @@ +1.0.0 \ No newline at end of file