feat: initial
This commit is contained in:
1 parent
d1fede526a
commit
8c0160dd77
16 files changed
+1307
-3
No files matched your search
@@ -0,0 +1,3 @@
|
||||
.DS_Store
|
||||
heat_exchander_airflow_pressure_drop.dSYM
|
||||
commit.md
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": []
|
||||
}
|
||||
Vendored
+49
@@ -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"
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
idf_component_register(SRCS "main.c" INCLUDE_DIRS "include")
|
||||
@@ -1,3 +1,26 @@
|
||||
# esp_component_template
|
||||
# Heat Exchanger Airflow Pressure Drop Calculation
|
||||
|
||||
esp_component_template
|
||||
## 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
|
||||
```
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
Executable
BIN
Binary file not shown.
@@ -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;
|
||||
}
|
||||
Executable
BIN
Binary file not shown.
Whitespace-only changes.
@@ -0,0 +1 @@
|
||||
1.0.0
|
||||
Reference in new issue
Block a user