From 051f7fb91fe8ae5bb8e080b15403fdd39ec0a1bf Mon Sep 17 00:00:00 2001 From: Elin O'Neill Date: Fri, 27 Jun 2025 16:31:50 -0400 Subject: [PATCH 01/14] Implemented 'to_altitude()' and corresponding tests (nonstratosphere data) --- endaq/calc/utils.py | 101 ++++++++++++++++++++++- tests/calc/csv_to_df/default_sea_lvl.csv | 18 ++++ tests/calc/test_utils.py | 68 +++++++++++++++ 3 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 tests/calc/csv_to_df/default_sea_lvl.csv diff --git a/endaq/calc/utils.py b/endaq/calc/utils.py index 2ba7eb9..1735380 100644 --- a/endaq/calc/utils.py +++ b/endaq/calc/utils.py @@ -3,7 +3,7 @@ from __future__ import annotations import typing -from typing import Optional, Union +from typing import Optional, Union, Literal import warnings import numpy as np @@ -161,9 +161,9 @@ def resample(df: pd.DataFrame, sample_rate: Optional[float] = None) -> pd.DataFr index=resampled_time.astype(df.index.dtype), columns=df.columns, ) - + resampled_df.index.name = df.index.name - + return resampled_df @@ -343,3 +343,98 @@ def convert_units( for i, c in enumerate(df.columns): converted_df[c] = vals[:, i] return converted_df + + +def to_altitude(df: pd.DataFrame, + base_altitude: float = 0, + base_press: Optional[float] = 101325, + base_temp: Optional[float] = 15, + units: Literal['m', 'ft'] = 'm') -> pd.DataFrame: + """ + Converts pressure (Pascals) to altitude (feet or meters). + + :param df: pandas DataFrame of one of the temperature/pressure channels. + The pressure column (named "Pressure (Pa)") should be present, if not + raise an error listing the expected column name that is absent + :param base_altitude: H_b; Height at the bottom of atmospheric layer in + meters [m] + :param base_press: P_b; static pressure (pressure at sea level) in Pascals + [Pa]. If set to None, use the first pressure measurement + :param base_temp: T_b standard temperature (temperature at sea level) in + Celsius [C]. If set to None and a Temperature column (named + "Temperature (C)") exists, use the first temperature value. If set to + None and no temperature column exists, error out + :param units: determines if altitude is represented in meters ('m') or feet + ('ft') + :returns: a pandas DataFrame with the same Index values as the + input, and a column of “Altitude (m)” or “Altitude (ft)” data. + """ + # Initial Check for Necessary Data + if "Pressure (Pa)" not in df.columns: + raise TypeError("'Pressure' column does not exist.") + + # Conversion Constants + C_K = 273.15 # Celsius to Kelvin Constant (C + 273.15 = K) + ft_m = 0.3048 # Feet to Meters Constant (ft * 0.3048 = m) & (m / 0.3048 = ft) + + # Constants + h_s = 11000 # Height at the Start of the Stratosphere (meters) + L_b = -0.0065 # Standard Temperature Lapse Rate [K/m] + g_0 = 9.80665 # Gravitational Acceleration Constant [m/s^2] + R = 8.31432 # Universal Gas Constant [N*m/mol*K] + M = 0.0289644 # Molar Mass of Earth's Air [kg/mol] + + # Constants from Parameters + press_col_index = df.columns.get_loc("Pressure (Pa)") + + if base_temp == None: + if "Temperature (C)" not in df.columns: + raise TypeError("Temp set to None with no existing temperature column.") + else: + temp_col_index = df.columns.get_loc("Temperature (C)") + T_b = df.iloc[0, temp_col_index] + C_K # Standard Temperature in Kelvin + else: + T_b = base_temp + C_K # Standard Temperature in Kelvin + + if base_press == None: + # Set to first pressure recording in the dataframe + P_b = df.iloc[0, press_col_index] # Static Pressure in Pascals + else: + P_b = base_press # Static Pressure in Pascals + + if units == "ft": + h_b = base_altitude * ft_m # Height at Bottom of Atmospheric Layer in Meters + else: + h_b = base_altitude # Height at Bottom of Atmospheric Layer in Meters + + # List of Altitudes to be added to Dataframe + altitude_column = [] + + # Pressure at base of stratosphere + stratosphere_pressure = (P_b * ((L_b * ((T_b / L_b) - h_b + h_s) / T_b) ** + ((-g_0 * M) / (R * L_b)))) + + # Calculate Altitude for the DataFrame + for index, row in df.iterrows(): + P = row["Pressure (Pa)"] + if P > stratosphere_pressure: + h = h_b + (T_b / L_b) * (((P / P_b) ** ((-R * L_b) / (g_0 * M))) - 1) + altitude_column.append(h) + else: + h = h_b + ((R * T_b * np.log(P / P_b)) / (-g_0 * M)) + altitude_column.append(h) + + # Convert Altitude back to feet if specified + if units == 'ft': + altitude_column = [x / ft_m for x in altitude_column] + # Add "Altitude (ft)" Column to Copy of Original Dataframe + alt_df = df.copy() + alt_df["Altitude (ft)"] = altitude_column + + # Add "Altitude (m)" Column to Copy of Original Dataframe + if units == 'm': + alt_df = df.copy() + alt_df["Altitude (m)"] = altitude_column + + # Return DataFrame with New Altitude Column + return alt_df diff --git a/tests/calc/csv_to_df/default_sea_lvl.csv b/tests/calc/csv_to_df/default_sea_lvl.csv new file mode 100644 index 0000000..75cb987 --- /dev/null +++ b/tests/calc/csv_to_df/default_sea_lvl.csv @@ -0,0 +1,18 @@ +Pressure (Pa),Temperature (C) +101325,15 +100000, +95000, +90000, +85000, +80000, +75000, +70000, +65000, +60000, +55000, +50000, +45000, +40000, +35000, +30000, +25000, diff --git a/tests/calc/test_utils.py b/tests/calc/test_utils.py index b012c89..effb44c 100644 --- a/tests/calc/test_utils.py +++ b/tests/calc/test_utils.py @@ -5,6 +5,10 @@ import numpy as np import pandas as pd +import sys +import os +sys.path.insert(0, os.path.realpath(os.path.join(__file__, '..', '..', '..'))) + from endaq.calc import utils @@ -107,3 +111,67 @@ def test_convert_units(): df = pd.DataFrame({'Val': [-40, 0, 10]}) np.testing.assert_allclose(utils.convert_units('degC', 'degF', df).Val[0], -40) np.testing.assert_allclose(utils.convert_units('degC', 'degF', df).Val[1], 32) + + +def test_to_altitude(): + """ + Tests the accuracy of the to_altitude function, which converts air pressure + to altitude. These tests only cover measurements BELOW the stratosphere. + """ + # Pressure Data CSV File --> DataFrame + df = pd.read_csv("./csv_to_df/default_sea_lvl.csv") + + # Meters + # DataFrame 1; Default settings: + def_key = [0.00, 110.88, 540.34, 988.50, 1457.30, 1948.99, 2466.23, + 3012.18, 3590.69, 4206.43, 4865.22, 5574.44, 6343.62, 7185.44, + 8117.27, 9163.96, 10362.95] + default_df = utils.to_altitude(df=df) + altitude_list_default = default_df['Altitude (m)'].tolist() + altitude_list_default = [round(num, 2) for num in altitude_list_default] + assert (altitude_list_default == def_key + ), "Equation is not accurate with default settings." + + + # DataFrame 2; Different base temperature: + temp_key = [0.00, 116.66, 568.47, 1039.96, 1533.16, 2050.45, 2594.61, + 3168.99, 3777.61, 4425.40, 5118.48, 5864.62, 6673.85, 7559.48, + 8539.82, 9641.00, 10902.40] + diff_temp_df = utils.to_altitude(df=df, base_temp=30) + altitude_list_diff_temp = diff_temp_df['Altitude (m)'].tolist() + altitude_list_diff_temp = [round(num, 2) for num in altitude_list_diff_temp] + assert (altitude_list_diff_temp == temp_key + ), "Equation is not accurate with non-default base temperature." + + # DataFrame 3: Different base pressure: + press_key = [-111.16, 0, 430.53, 879.82, 1349.79, 1842.71, 2361.25, 2908.57, + 3488.53, 4105.81, 4766.26, 5477.25, 6248.37, 7092.29, 8026.46, + 9075.77, 10277.77] + diff_press_df = utils.to_altitude(df=df, base_press=100000) + altitude_list_diff_press = diff_press_df['Altitude (m)'].tolist() + altitude_list_diff_press = [round(num, 2) for num in altitude_list_diff_press] + assert (altitude_list_diff_press == press_key + ), "Equation is not accurate with non-default base pressure." + + # DataFrame 4: Different base temperature and pressure: + temp_press_key = [-116.95, 0.00, 452.94, 925.62, 1420.06, 1938.64, 2484.17, + 3059.98, 3670.13, 4319.54, 5014.37, 5762.38, 6573.63, + 7461.49, 8444.29, 9548.22, 10812.79] + diff_temp_and_press_df = utils.to_altitude(df=df, base_temp=30, + base_press=100000) + altitude_list_diff_temp_press = diff_temp_and_press_df['Altitude (m)'].tolist() + altitude_list_diff_temp_press = [round(num, 2) for num in + altitude_list_diff_temp_press] + assert (altitude_list_diff_temp_press == temp_press_key + ), "Equation is not accurate with non-default base temperature and pressure." + + # Feet --> Meters --> Feet + # DataFrame 1; Default settings; Units = Feet: + def_key = [0.00, 363.79, 1772.76, 3243.11, 4781.17, 6394.32, 8091.29, + 9882.49, 11780.47, 13800.61, 15962.0, 18288.84, 20812.4, + 23574.27, 26631.46, 30065.48, 33999.16] + default_df = utils.to_altitude(df=df, units='ft') + altitude_list_default = default_df['Altitude (ft)'].tolist() + altitude_list_default = [round(num, 2) for num in altitude_list_default] + assert (altitude_list_default == def_key + ), "Equation is not accurate for units='ft'." From dc1777f6587179dddc8ff9cadaabe82b6063b950 Mon Sep 17 00:00:00 2001 From: Elin O'Neill Date: Mon, 30 Jun 2025 08:39:20 -0400 Subject: [PATCH 02/14] Minor file path fix. --- tests/calc/test_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/calc/test_utils.py b/tests/calc/test_utils.py index effb44c..a71e716 100644 --- a/tests/calc/test_utils.py +++ b/tests/calc/test_utils.py @@ -119,7 +119,7 @@ def test_to_altitude(): to altitude. These tests only cover measurements BELOW the stratosphere. """ # Pressure Data CSV File --> DataFrame - df = pd.read_csv("./csv_to_df/default_sea_lvl.csv") + df = pd.read_csv("tests/calc/csv_to_df/default_sea_lvl.csv") # Meters # DataFrame 1; Default settings: From dc3c3c9b0a4ad9d171f60db4f89a6dacf305bfc9 Mon Sep 17 00:00:00 2001 From: Elin O'Neill Date: Mon, 30 Jun 2025 09:09:13 -0400 Subject: [PATCH 03/14] Equation fix. --- endaq/calc/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/endaq/calc/utils.py b/endaq/calc/utils.py index 1735380..51efb33 100644 --- a/endaq/calc/utils.py +++ b/endaq/calc/utils.py @@ -411,8 +411,8 @@ def to_altitude(df: pd.DataFrame, altitude_column = [] # Pressure at base of stratosphere - stratosphere_pressure = (P_b * ((L_b * ((T_b / L_b) - h_b + h_s) / T_b) ** - ((-g_0 * M) / (R * L_b)))) + stratosphere_pressure = (P_b * (1 + (L_b / T_b) * (h_s - h_b)) ** + ((-g_0 * M) / (R * L_b))) # Calculate Altitude for the DataFrame for index, row in df.iterrows(): From 0c75e7bf8668768c2ebdbd9f568739c9782a3a3b Mon Sep 17 00:00:00 2001 From: Elin O'Neill Date: Mon, 30 Jun 2025 09:43:43 -0400 Subject: [PATCH 04/14] Added edge-case tests --- tests/calc/csv_to_df/press_error.csv | 2 ++ tests/calc/csv_to_df/temp_error.csv | 18 ++++++++++++++++++ tests/calc/test_utils.py | 21 +++++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 tests/calc/csv_to_df/press_error.csv create mode 100644 tests/calc/csv_to_df/temp_error.csv diff --git a/tests/calc/csv_to_df/press_error.csv b/tests/calc/csv_to_df/press_error.csv new file mode 100644 index 0000000..6e4fcb5 --- /dev/null +++ b/tests/calc/csv_to_df/press_error.csv @@ -0,0 +1,2 @@ +Temperature (C) +15 \ No newline at end of file diff --git a/tests/calc/csv_to_df/temp_error.csv b/tests/calc/csv_to_df/temp_error.csv new file mode 100644 index 0000000..1e3223a --- /dev/null +++ b/tests/calc/csv_to_df/temp_error.csv @@ -0,0 +1,18 @@ +Pressure (Pa), +101325, +100000, +95000, +90000, +85000, +80000, +75000, +70000, +65000, +60000, +55000, +50000, +45000, +40000, +35000, +30000, +25000, diff --git a/tests/calc/test_utils.py b/tests/calc/test_utils.py index a71e716..da86c55 100644 --- a/tests/calc/test_utils.py +++ b/tests/calc/test_utils.py @@ -175,3 +175,24 @@ def test_to_altitude(): altitude_list_default = [round(num, 2) for num in altitude_list_default] assert (altitude_list_default == def_key ), "Equation is not accurate for units='ft'." + + # Base Values = None + base_none_df = utils.to_altitude(df=df, base_press=None, base_temp=None) + altitude_list_none = base_none_df['Altitude (m)'].tolist() + altitude_list_none = [round(num, 2) for num in altitude_list_default] + assert (altitude_list_none == def_key + ), "Equation is not accurate with base settings = None." + + # Errors + temp_df = pd.read_csv("tests/calc/csv_to_df/temp_error.csv") + press_df = df = pd.read_csv("tests/calc/csv_to_df/press_error.csv") + + # No temperature column + with pytest.raises(TypeError) as exc_info: + test_to_altitude(df=temp_df, base_temp=None) + assert (exc_info.type == TypeError), "Error not raised for missing temperature column." + + # No pressure column + with pytest.raises(TypeError) as exc_info: + test_to_altitude(df=press_df, base_press=None) + assert (exc_info.type == TypeError), "Error not raised for missing pressure column." From a53f2a4a4465ca5f5e999e77438b1b3c22d3fb99 Mon Sep 17 00:00:00 2001 From: Elin O'Neill Date: Mon, 30 Jun 2025 10:56:46 -0400 Subject: [PATCH 05/14] Limited NumPy version requirement. --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c6f9a4a..0d929e7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ backports.cached-property; python_version<'3.8' ebmlite>=3.2.0 idelib>=3.2.8 jinja2 -numpy>=1.19.5 +numpy>=1.19.5, <2.3.0 pandas>=1.3 plotly>=5.3.1 pynmeagps From b2817e388959d42220449e9ce741b717f458c89c Mon Sep 17 00:00:00 2001 From: Elin O'Neill Date: Mon, 30 Jun 2025 11:09:54 -0400 Subject: [PATCH 06/14] Limiting NumPy version again. --- requirements.txt | 2 +- setup.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0d929e7..ac124b1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ backports.cached-property; python_version<'3.8' ebmlite>=3.2.0 idelib>=3.2.8 jinja2 -numpy>=1.19.5, <2.3.0 +numpy>=1.19.5,<2.3.0 pandas>=1.3 plotly>=5.3.1 pynmeagps diff --git a/setup.py b/setup.py index 48bda63..3e61e26 100644 --- a/setup.py +++ b/setup.py @@ -42,6 +42,7 @@ def get_version(rel_path): "pytest-cov", "pytest-xdist[psutil]", "sympy", + "numpy<=2.3.0" ] DOCS_REQUIRES = [ From 6ce2a49afdeac6ec24e5ec672f36e540ba297111 Mon Sep 17 00:00:00 2001 From: Elin O'Neill Date: Mon, 30 Jun 2025 11:25:35 -0400 Subject: [PATCH 07/14] Revision to NumPy limit. --- setup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 3e61e26..e5965cb 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ def get_version(rel_path): "ebmlite>=3.2.0", "idelib>=3.2.8", "jinja2", - "numpy>=1.19.5", + "numpy>1.19.5,<=2.3.0", "pandas>=1.3", "plotly>=5.3.1", "pynmeagps", @@ -42,7 +42,6 @@ def get_version(rel_path): "pytest-cov", "pytest-xdist[psutil]", "sympy", - "numpy<=2.3.0" ] DOCS_REQUIRES = [ From 97ed61d2f6f934dc49ab7097cd12e4f1901c7711 Mon Sep 17 00:00:00 2001 From: Elin O'Neill Date: Mon, 30 Jun 2025 11:31:52 -0400 Subject: [PATCH 08/14] Revision to NumPy limit 2. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e5965cb..812fece 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ def get_version(rel_path): "ebmlite>=3.2.0", "idelib>=3.2.8", "jinja2", - "numpy>1.19.5,<=2.3.0", + "numpy>1.19.5,<2.3.0", "pandas>=1.3", "plotly>=5.3.1", "pynmeagps", From 153091f27762c1462e6be308961490cb380f9b5f Mon Sep 17 00:00:00 2001 From: Elin O'Neill Date: Mon, 30 Jun 2025 11:40:21 -0400 Subject: [PATCH 09/14] Revision to SciPy version limit. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 812fece..f5d686e 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,7 @@ def get_version(rel_path): "pynmeagps", "python-dotenv>=0.18.0", "requests>=2.25.1", - "scipy>=1.7.1", + "scipy>=1.7.1,<1.16.0", "pint>=0.18" ] From a9ba3291c65069fe70ec67a2c30055cd19f075c5 Mon Sep 17 00:00:00 2001 From: Elin O'Neill Date: Mon, 30 Jun 2025 12:09:26 -0400 Subject: [PATCH 10/14] Reverse NumPy changes. --- requirements.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index ac124b1..c6f9a4a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ backports.cached-property; python_version<'3.8' ebmlite>=3.2.0 idelib>=3.2.8 jinja2 -numpy>=1.19.5,<2.3.0 +numpy>=1.19.5 pandas>=1.3 plotly>=5.3.1 pynmeagps diff --git a/setup.py b/setup.py index f5d686e..79243c5 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ def get_version(rel_path): "ebmlite>=3.2.0", "idelib>=3.2.8", "jinja2", - "numpy>1.19.5,<2.3.0", + "numpy>1.19.5", "pandas>=1.3", "plotly>=5.3.1", "pynmeagps", From c71ebb051d6f119808f508eb10a63487ecb5c5d0 Mon Sep 17 00:00:00 2001 From: Elin O'Neill Date: Mon, 30 Jun 2025 15:05:51 -0400 Subject: [PATCH 11/14] Stratosphere equation and initial tests implemented. --- endaq/calc/utils.py | 33 ++++++++++++-------- tests/calc/csv_to_df/beyond_stratosphere.csv | 2 ++ tests/calc/csv_to_df/stratosphere.csv | 12 +++++++ tests/calc/test_utils.py | 28 +++++++++++++---- 4 files changed, 56 insertions(+), 19 deletions(-) create mode 100644 tests/calc/csv_to_df/beyond_stratosphere.csv create mode 100644 tests/calc/csv_to_df/stratosphere.csv diff --git a/endaq/calc/utils.py b/endaq/calc/utils.py index 51efb33..04a4e68 100644 --- a/endaq/calc/utils.py +++ b/endaq/calc/utils.py @@ -351,17 +351,17 @@ def to_altitude(df: pd.DataFrame, base_temp: Optional[float] = 15, units: Literal['m', 'ft'] = 'm') -> pd.DataFrame: """ - Converts pressure (Pascals) to altitude (feet or meters). + Converts pressure (Pascals) to altitude (feet or meters) up to 50km. :param df: pandas DataFrame of one of the temperature/pressure channels. The pressure column (named "Pressure (Pa)") should be present, if not raise an error listing the expected column name that is absent - :param base_altitude: H_b; Height at the bottom of atmospheric layer in - meters [m] + :param base_altitude: h_b; Height at the bottom of atmospheric layer in + meters (m) :param base_press: P_b; static pressure (pressure at sea level) in Pascals - [Pa]. If set to None, use the first pressure measurement + (Pa). If set to None, use the first pressure measurement :param base_temp: T_b standard temperature (temperature at sea level) in - Celsius [C]. If set to None and a Temperature column (named + Celsius (C). If set to None and a Temperature column (named "Temperature (C)") exists, use the first temperature value. If set to None and no temperature column exists, error out :param units: determines if altitude is represented in meters ('m') or feet @@ -378,11 +378,13 @@ def to_altitude(df: pd.DataFrame, ft_m = 0.3048 # Feet to Meters Constant (ft * 0.3048 = m) & (m / 0.3048 = ft) # Constants - h_s = 11000 # Height at the Start of the Stratosphere (meters) + h_sb = 11000 # Height at the Base of the Stratosphere (meters) + h_st = 50000 # Height at the Top of the Stratosphere (meters) L_b = -0.0065 # Standard Temperature Lapse Rate [K/m] g_0 = 9.80665 # Gravitational Acceleration Constant [m/s^2] R = 8.31432 # Universal Gas Constant [N*m/mol*K] M = 0.0289644 # Molar Mass of Earth's Air [kg/mol] + top_stratosphere_pressure = 100 # Air Pressure at Stratopause (1mb)=[100 Pa] # Constants from Parameters press_col_index = df.columns.get_loc("Pressure (Pa)") @@ -393,8 +395,10 @@ def to_altitude(df: pd.DataFrame, else: temp_col_index = df.columns.get_loc("Temperature (C)") T_b = df.iloc[0, temp_col_index] + C_K # Standard Temperature in Kelvin + T_bS = T_b -71.5 # Temperature at start of Stratosphere else: T_b = base_temp + C_K # Standard Temperature in Kelvin + T_bS = T_b -71.5 # Temperature at start of Stratosphere if base_press == None: # Set to first pressure recording in the dataframe @@ -407,22 +411,25 @@ def to_altitude(df: pd.DataFrame, else: h_b = base_altitude # Height at Bottom of Atmospheric Layer in Meters + # Pressure at base of Stratosphere + base_stratosphere_pressure = (P_b * (1 + (L_b / T_b) * (h_sb - h_b)) ** + ((-g_0 * M) / (R * L_b))) + # List of Altitudes to be added to Dataframe altitude_column = [] - # Pressure at base of stratosphere - stratosphere_pressure = (P_b * (1 + (L_b / T_b) * (h_s - h_b)) ** - ((-g_0 * M) / (R * L_b))) - # Calculate Altitude for the DataFrame for index, row in df.iterrows(): P = row["Pressure (Pa)"] - if P > stratosphere_pressure: + if P > base_stratosphere_pressure: h = h_b + (T_b / L_b) * (((P / P_b) ** ((-R * L_b) / (g_0 * M))) - 1) altitude_column.append(h) - else: - h = h_b + ((R * T_b * np.log(P / P_b)) / (-g_0 * M)) + elif top_stratosphere_pressure < P < base_stratosphere_pressure: + h = h_sb + ((R * T_bS * np.log(P / base_stratosphere_pressure)) / + (-g_0 * M)) altitude_column.append(h) + else: + raise ValueError("Altitudes above stratosphere not supported.") # Convert Altitude back to feet if specified if units == 'ft': diff --git a/tests/calc/csv_to_df/beyond_stratosphere.csv b/tests/calc/csv_to_df/beyond_stratosphere.csv new file mode 100644 index 0000000..53731f0 --- /dev/null +++ b/tests/calc/csv_to_df/beyond_stratosphere.csv @@ -0,0 +1,2 @@ +Pressure (Pa),Temperature (C) +25,15 diff --git a/tests/calc/csv_to_df/stratosphere.csv b/tests/calc/csv_to_df/stratosphere.csv new file mode 100644 index 0000000..55600fb --- /dev/null +++ b/tests/calc/csv_to_df/stratosphere.csv @@ -0,0 +1,12 @@ +Pressure (Pa),Temperature (C) +20000,-55 +18000 +16000 +14000 +12000 +10000 +8000 +6000 +4000 +2000 +500 \ No newline at end of file diff --git a/tests/calc/test_utils.py b/tests/calc/test_utils.py index da86c55..6d801a4 100644 --- a/tests/calc/test_utils.py +++ b/tests/calc/test_utils.py @@ -167,32 +167,48 @@ def test_to_altitude(): # Feet --> Meters --> Feet # DataFrame 1; Default settings; Units = Feet: - def_key = [0.00, 363.79, 1772.76, 3243.11, 4781.17, 6394.32, 8091.29, + def_ft_key = [0.00, 363.79, 1772.76, 3243.11, 4781.17, 6394.32, 8091.29, 9882.49, 11780.47, 13800.61, 15962.0, 18288.84, 20812.4, 23574.27, 26631.46, 30065.48, 33999.16] default_df = utils.to_altitude(df=df, units='ft') altitude_list_default = default_df['Altitude (ft)'].tolist() altitude_list_default = [round(num, 2) for num in altitude_list_default] - assert (altitude_list_default == def_key + assert (altitude_list_default == def_ft_key ), "Equation is not accurate for units='ft'." # Base Values = None base_none_df = utils.to_altitude(df=df, base_press=None, base_temp=None) altitude_list_none = base_none_df['Altitude (m)'].tolist() - altitude_list_none = [round(num, 2) for num in altitude_list_default] + altitude_list_none = [round(num, 2) for num in altitude_list_none] assert (altitude_list_none == def_key ), "Equation is not accurate with base settings = None." - # Errors + # Error test dfs temp_df = pd.read_csv("tests/calc/csv_to_df/temp_error.csv") press_df = df = pd.read_csv("tests/calc/csv_to_df/press_error.csv") + beyond_strat_df = pd.read_csv("tests/calc/csv_to_df/beyond_stratosphere.csv") # No temperature column with pytest.raises(TypeError) as exc_info: - test_to_altitude(df=temp_df, base_temp=None) + utils.to_altitude(df=temp_df, base_temp=None) assert (exc_info.type == TypeError), "Error not raised for missing temperature column." # No pressure column with pytest.raises(TypeError) as exc_info: - test_to_altitude(df=press_df, base_press=None) + utils.to_altitude(df=press_df, base_press=None) assert (exc_info.type == TypeError), "Error not raised for missing pressure column." + + # Beyond stratosphere (50km) + with pytest.raises(ValueError) as exc_info: + utils.to_altitude(df=beyond_strat_df) + assert(exc_info.type == ValueError), "Error not raised when above stratosphere." + + # Stratosphere Tests + df_2 = pd.read_csv("tests/calc/csv_to_df/stratosphere.csv") + strat_key = [11784.05, 12452.21, 13199.14, 14045.95, 15023.51, 16179.72, + 17594.82, 19419.19, 21990.49, 26386.17, 35177.52] + strat_df = utils.to_altitude(df=df_2) + altitude_list_strat = strat_df['Altitude (m)'].tolist() + altitude_list_strat = [round(num, 2) for num in altitude_list_strat] + assert (altitude_list_strat == strat_key + ), "Equation is not accurate in the Stratosphere." From e09a3e386a9f539d5a5ddf37c26e109706519b87 Mon Sep 17 00:00:00 2001 From: Elin O'Neill Date: Mon, 7 Jul 2025 09:56:16 -0400 Subject: [PATCH 12/14] Resolve SciPy version issues --- setup.py | 2 +- tests/batch/test_core.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 79243c5..bdf00d5 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,7 @@ def get_version(rel_path): "pynmeagps", "python-dotenv>=0.18.0", "requests>=2.25.1", - "scipy>=1.7.1,<1.16.0", + "scipy>=1.7.1", "pint>=0.18" ] diff --git a/tests/batch/test_core.py b/tests/batch/test_core.py index 7a1a136..f7561c3 100644 --- a/tests/batch/test_core.py +++ b/tests/batch/test_core.py @@ -292,7 +292,7 @@ def assert_output_is_valid(output: endaq.batch.core.OutputStruct): @pytest.mark.filterwarnings("ignore:no acceleration channel in:UserWarning") @pytest.mark.filterwarnings( "ignore" - ":nperseg .* is greater than input length .*, using nperseg .*" + ":.*nperseg.* is greater than (signal|input) length.*, using nperseg .*" ":UserWarning" ) def test_aggregate_data(getdata_builder): From 3f06b8b4f64592e4156ee9fd0f9a8913337a509b Mon Sep 17 00:00:00 2001 From: Elin O'Neill Date: Tue, 22 Jul 2025 11:19:30 -0400 Subject: [PATCH 13/14] Implemented suggestions for to_altitude --- endaq/calc/utils.py | 74 ++++++++++++++++++++++++---------------- tests/calc/test_utils.py | 10 +++--- 2 files changed, 49 insertions(+), 35 deletions(-) diff --git a/endaq/calc/utils.py b/endaq/calc/utils.py index 04a4e68..c3b16ce 100644 --- a/endaq/calc/utils.py +++ b/endaq/calc/utils.py @@ -346,38 +346,42 @@ def convert_units( def to_altitude(df: pd.DataFrame, - base_altitude: float = 0, base_press: Optional[float] = 101325, base_temp: Optional[float] = 15, + temp_col_index: Optional[int] = None, + press_col_index: Optional[int] = None, units: Literal['m', 'ft'] = 'm') -> pd.DataFrame: """ Converts pressure (Pascals) to altitude (feet or meters) up to 50km. :param df: pandas DataFrame of one of the temperature/pressure channels. - The pressure column (named "Pressure (Pa)") should be present, if not - raise an error listing the expected column name that is absent - :param base_altitude: h_b; Height at the bottom of atmospheric layer in - meters (m) - :param base_press: P_b; static pressure (pressure at sea level) in Pascals - (Pa). If set to None, use the first pressure measurement - :param base_temp: T_b standard temperature (temperature at sea level) in - Celsius (C). If set to None and a Temperature column (named - "Temperature (C)") exists, use the first temperature value. If set to - None and no temperature column exists, error out + A pressure column should be present, if not, raise an error + :param base_press: P_b; reference pressure (pressure at sea level) in + Pascals (Pa). If set to None, use the first pressure measurement + included in the dataframe (df) + :param base_temp: T_b reference temperature (temperature at sea level) in + Celsius (C). If set to None and a Temperature column exists in the + dataframe (df), use the first listed temperature value. If set to None + and no temperature column exists, error out + :param temp_col_index: the index (starting at 0) in the dataframe (df) of + the temperature column to pull data from. + :param press_col_index: the index (starting at 0) in the dataframe (df) of + the pressure column to pull data from :param units: determines if altitude is represented in meters ('m') or feet - ('ft') + ('ft') in both the input and output dataframes :returns: a pandas DataFrame with the same Index values as the - input, and a column of “Altitude (m)” or “Altitude (ft)” data. + input, and an added column of “Altitude (m)” or “Altitude (ft)” data """ - # Initial Check for Necessary Data - if "Pressure (Pa)" not in df.columns: - raise TypeError("'Pressure' column does not exist.") + # Column Name Placeholders + temp_col = None + press_col = None # Conversion Constants C_K = 273.15 # Celsius to Kelvin Constant (C + 273.15 = K) ft_m = 0.3048 # Feet to Meters Constant (ft * 0.3048 = m) & (m / 0.3048 = ft) # Constants + h_b = 0 # Reference Height of Sea Level (m) h_sb = 11000 # Height at the Base of the Stratosphere (meters) h_st = 50000 # Height at the Top of the Stratosphere (meters) L_b = -0.0065 # Standard Temperature Lapse Rate [K/m] @@ -386,31 +390,41 @@ def to_altitude(df: pd.DataFrame, M = 0.0289644 # Molar Mass of Earth's Air [kg/mol] top_stratosphere_pressure = 100 # Air Pressure at Stratopause (1mb)=[100 Pa] - # Constants from Parameters - press_col_index = df.columns.get_loc("Pressure (Pa)") + # Finding pressure column + if press_col_index is None: + for col in df.columns: + if "press" in col.lower(): + press_col = col + press_col_index = df.columns.get_loc(press_col) + break + if press_col is None: + raise ValueError("Pressure column not found.") + else: + press_col = df.columns[press_col_index] + # Checking for base temp and converting to K if base_temp == None: - if "Temperature (C)" not in df.columns: - raise TypeError("Temp set to None with no existing temperature column.") - else: - temp_col_index = df.columns.get_loc("Temperature (C)") - T_b = df.iloc[0, temp_col_index] + C_K # Standard Temperature in Kelvin - T_bS = T_b -71.5 # Temperature at start of Stratosphere + if temp_col_index is None: + for col in df.columns: + if "temp" in col.lower(): + temp_col = col + temp_col_index = df.columns.get_loc(temp_col) + break + if temp_col is None: + raise ValueError('Temperature column not found.') + T_b = df.iloc[0, temp_col_index] + C_K # Standard Temperature in Kelvin + T_bS = T_b -71.5 # Temperature at start of Stratosphere else: T_b = base_temp + C_K # Standard Temperature in Kelvin T_bS = T_b -71.5 # Temperature at start of Stratosphere + # Checking for base pressure if base_press == None: # Set to first pressure recording in the dataframe P_b = df.iloc[0, press_col_index] # Static Pressure in Pascals else: P_b = base_press # Static Pressure in Pascals - if units == "ft": - h_b = base_altitude * ft_m # Height at Bottom of Atmospheric Layer in Meters - else: - h_b = base_altitude # Height at Bottom of Atmospheric Layer in Meters - # Pressure at base of Stratosphere base_stratosphere_pressure = (P_b * (1 + (L_b / T_b) * (h_sb - h_b)) ** ((-g_0 * M) / (R * L_b))) @@ -420,7 +434,7 @@ def to_altitude(df: pd.DataFrame, # Calculate Altitude for the DataFrame for index, row in df.iterrows(): - P = row["Pressure (Pa)"] + P = row[press_col] if P > base_stratosphere_pressure: h = h_b + (T_b / L_b) * (((P / P_b) ** ((-R * L_b) / (g_0 * M))) - 1) altitude_column.append(h) diff --git a/tests/calc/test_utils.py b/tests/calc/test_utils.py index 6d801a4..03dd104 100644 --- a/tests/calc/test_utils.py +++ b/tests/calc/test_utils.py @@ -116,7 +116,7 @@ def test_convert_units(): def test_to_altitude(): """ Tests the accuracy of the to_altitude function, which converts air pressure - to altitude. These tests only cover measurements BELOW the stratosphere. + to altitude. """ # Pressure Data CSV File --> DataFrame df = pd.read_csv("tests/calc/csv_to_df/default_sea_lvl.csv") @@ -189,14 +189,14 @@ def test_to_altitude(): beyond_strat_df = pd.read_csv("tests/calc/csv_to_df/beyond_stratosphere.csv") # No temperature column - with pytest.raises(TypeError) as exc_info: + with pytest.raises(ValueError) as exc_info: utils.to_altitude(df=temp_df, base_temp=None) - assert (exc_info.type == TypeError), "Error not raised for missing temperature column." + assert (exc_info.type == ValueError), "Error not raised for missing temperature column." # No pressure column - with pytest.raises(TypeError) as exc_info: + with pytest.raises(ValueError) as exc_info: utils.to_altitude(df=press_df, base_press=None) - assert (exc_info.type == TypeError), "Error not raised for missing pressure column." + assert (exc_info.type == ValueError), "Error not raised for missing pressure column." # Beyond stratosphere (50km) with pytest.raises(ValueError) as exc_info: From 5e1bc0533fccce4a221ecf2f295dfe517851e986 Mon Sep 17 00:00:00 2001 From: Elin O'Neill Date: Wed, 23 Jul 2025 12:29:31 -0400 Subject: [PATCH 14/14] Parameterized and split up test_to_altitude. --- .../calc/csv_to_df/keys/base_values_none.csv | 18 +++ .../calc/csv_to_df/keys/default_settings.csv | 18 +++ .../csv_to_df/keys/diff_base_pressure.csv | 18 +++ tests/calc/csv_to_df/keys/diff_base_temp.csv | 18 +++ .../csv_to_df/keys/diff_temp_and_pressure.csv | 18 +++ tests/calc/csv_to_df/keys/stratosphere.csv | 12 ++ tests/calc/csv_to_df/keys/units_feet.csv | 18 +++ tests/calc/test_utils.py | 145 ++++++------------ 8 files changed, 171 insertions(+), 94 deletions(-) create mode 100644 tests/calc/csv_to_df/keys/base_values_none.csv create mode 100644 tests/calc/csv_to_df/keys/default_settings.csv create mode 100644 tests/calc/csv_to_df/keys/diff_base_pressure.csv create mode 100644 tests/calc/csv_to_df/keys/diff_base_temp.csv create mode 100644 tests/calc/csv_to_df/keys/diff_temp_and_pressure.csv create mode 100644 tests/calc/csv_to_df/keys/stratosphere.csv create mode 100644 tests/calc/csv_to_df/keys/units_feet.csv diff --git a/tests/calc/csv_to_df/keys/base_values_none.csv b/tests/calc/csv_to_df/keys/base_values_none.csv new file mode 100644 index 0000000..0bc8d1f --- /dev/null +++ b/tests/calc/csv_to_df/keys/base_values_none.csv @@ -0,0 +1,18 @@ +Key,base settings = None +0.00 +110.88 +540.34 +988.50 +1457.30 +1948.99 +2466.23 +3012.18 +3590.69 +4206.43 +4865.22 +5574.44 +6343.62 +7185.44 +8117.27 +9163.96 +10362.95 \ No newline at end of file diff --git a/tests/calc/csv_to_df/keys/default_settings.csv b/tests/calc/csv_to_df/keys/default_settings.csv new file mode 100644 index 0000000..74291a5 --- /dev/null +++ b/tests/calc/csv_to_df/keys/default_settings.csv @@ -0,0 +1,18 @@ +Key,settings are default +0.00 +110.88 +540.34 +988.50 +1457.30 +1948.99 +2466.23 +3012.18 +3590.69 +4206.43 +4865.22 +5574.44 +6343.62 +7185.44 +8117.27 +9163.96 +10362.95 \ No newline at end of file diff --git a/tests/calc/csv_to_df/keys/diff_base_pressure.csv b/tests/calc/csv_to_df/keys/diff_base_pressure.csv new file mode 100644 index 0000000..486f9f3 --- /dev/null +++ b/tests/calc/csv_to_df/keys/diff_base_pressure.csv @@ -0,0 +1,18 @@ +Key,the base pressure is not set to the default value +-111.16 +0 +430.53 +879.82 +1349.79 +1842.71 +2361.25 +2908.57 +3488.53 +4105.81 +4766.26 +5477.25 +6248.37 +7092.29 +8026.46, +9075.77 +10277.77 \ No newline at end of file diff --git a/tests/calc/csv_to_df/keys/diff_base_temp.csv b/tests/calc/csv_to_df/keys/diff_base_temp.csv new file mode 100644 index 0000000..7d580ee --- /dev/null +++ b/tests/calc/csv_to_df/keys/diff_base_temp.csv @@ -0,0 +1,18 @@ +Key,the base temperature is not set to the default value +0.00 +116.66 +568.47 +1039.96 +1533.16 +2050.45 +2594.61 +3168.99 +3777.61 +4425.40 +5118.48 +5864.62 +6673.85 +7559.48 +8539.82 +9641.00 +10902.40 \ No newline at end of file diff --git a/tests/calc/csv_to_df/keys/diff_temp_and_pressure.csv b/tests/calc/csv_to_df/keys/diff_temp_and_pressure.csv new file mode 100644 index 0000000..2e72d29 --- /dev/null +++ b/tests/calc/csv_to_df/keys/diff_temp_and_pressure.csv @@ -0,0 +1,18 @@ +Key,the base temperature and pressure are not set to default values +-116.95 +0.00 +452.94 +925.62 +1420.06 +1938.64 +2484.17 +3059.98 +3670.13 +4319.54 +5014.37 +5762.38 +6573.63 +7461.49 +8444.29 +9548.22 +10812.79 \ No newline at end of file diff --git a/tests/calc/csv_to_df/keys/stratosphere.csv b/tests/calc/csv_to_df/keys/stratosphere.csv new file mode 100644 index 0000000..41bbcdf --- /dev/null +++ b/tests/calc/csv_to_df/keys/stratosphere.csv @@ -0,0 +1,12 @@ +Key,altitude is in the stratosphere +11784.05 +12452.21 +13199.14 +14045.95 +15023.51 +16179.72 +17594.82 +19419.19 +21990.49 +26386.17 +35177.52 \ No newline at end of file diff --git a/tests/calc/csv_to_df/keys/units_feet.csv b/tests/calc/csv_to_df/keys/units_feet.csv new file mode 100644 index 0000000..a250c2a --- /dev/null +++ b/tests/calc/csv_to_df/keys/units_feet.csv @@ -0,0 +1,18 @@ +Key,units are set to feet ('ft') +0.00 +363.79 +1772.76 +3243.11 +4781.17 +6394.32 +8091.29 +9882.49 +11780.47 +13800.61 +15962.0 +18288.84 +20812.4, +23574.27 +26631.46 +30065.48 +33999.16 \ No newline at end of file diff --git a/tests/calc/test_utils.py b/tests/calc/test_utils.py index 03dd104..b8a5ea9 100644 --- a/tests/calc/test_utils.py +++ b/tests/calc/test_utils.py @@ -113,102 +113,59 @@ def test_convert_units(): np.testing.assert_allclose(utils.convert_units('degC', 'degF', df).Val[1], 32) -def test_to_altitude(): +# Dataframes for the following altitude test +df_1 = pd.read_csv("tests/calc/csv_to_df/default_sea_lvl.csv") +df_strat = pd.read_csv("tests/calc/csv_to_df/stratosphere.csv") + +@pytest.mark.parametrize("kwargs, key", [ + ({"df": df_1}, "default_settings.csv"), + ({"df": df_1, "base_temp": 30}, "diff_base_temp.csv"), + ({"df": df_1, "base_press": 100000}, "diff_base_pressure.csv"), + ({"df": df_1, "base_temp": 30, "base_press": 100000}, "diff_temp_and_pressure.csv"), + ({"df": df_1, "units":'ft'}, "units_feet.csv"), + ({"df": df_1, "base_press": None, "base_temp": None}, "base_values_none.csv"), + ({"df": df_strat}, "stratosphere.csv"), + ]) +def test_to_altitude(kwargs, key): """ Tests the accuracy of the to_altitude function, which converts air pressure to altitude. """ - # Pressure Data CSV File --> DataFrame - df = pd.read_csv("tests/calc/csv_to_df/default_sea_lvl.csv") - - # Meters - # DataFrame 1; Default settings: - def_key = [0.00, 110.88, 540.34, 988.50, 1457.30, 1948.99, 2466.23, - 3012.18, 3590.69, 4206.43, 4865.22, 5574.44, 6343.62, 7185.44, - 8117.27, 9163.96, 10362.95] - default_df = utils.to_altitude(df=df) - altitude_list_default = default_df['Altitude (m)'].tolist() - altitude_list_default = [round(num, 2) for num in altitude_list_default] - assert (altitude_list_default == def_key - ), "Equation is not accurate with default settings." - - - # DataFrame 2; Different base temperature: - temp_key = [0.00, 116.66, 568.47, 1039.96, 1533.16, 2050.45, 2594.61, - 3168.99, 3777.61, 4425.40, 5118.48, 5864.62, 6673.85, 7559.48, - 8539.82, 9641.00, 10902.40] - diff_temp_df = utils.to_altitude(df=df, base_temp=30) - altitude_list_diff_temp = diff_temp_df['Altitude (m)'].tolist() - altitude_list_diff_temp = [round(num, 2) for num in altitude_list_diff_temp] - assert (altitude_list_diff_temp == temp_key - ), "Equation is not accurate with non-default base temperature." - - # DataFrame 3: Different base pressure: - press_key = [-111.16, 0, 430.53, 879.82, 1349.79, 1842.71, 2361.25, 2908.57, - 3488.53, 4105.81, 4766.26, 5477.25, 6248.37, 7092.29, 8026.46, - 9075.77, 10277.77] - diff_press_df = utils.to_altitude(df=df, base_press=100000) - altitude_list_diff_press = diff_press_df['Altitude (m)'].tolist() - altitude_list_diff_press = [round(num, 2) for num in altitude_list_diff_press] - assert (altitude_list_diff_press == press_key - ), "Equation is not accurate with non-default base pressure." - - # DataFrame 4: Different base temperature and pressure: - temp_press_key = [-116.95, 0.00, 452.94, 925.62, 1420.06, 1938.64, 2484.17, - 3059.98, 3670.13, 4319.54, 5014.37, 5762.38, 6573.63, - 7461.49, 8444.29, 9548.22, 10812.79] - diff_temp_and_press_df = utils.to_altitude(df=df, base_temp=30, - base_press=100000) - altitude_list_diff_temp_press = diff_temp_and_press_df['Altitude (m)'].tolist() - altitude_list_diff_temp_press = [round(num, 2) for num in - altitude_list_diff_temp_press] - assert (altitude_list_diff_temp_press == temp_press_key - ), "Equation is not accurate with non-default base temperature and pressure." - - # Feet --> Meters --> Feet - # DataFrame 1; Default settings; Units = Feet: - def_ft_key = [0.00, 363.79, 1772.76, 3243.11, 4781.17, 6394.32, 8091.29, - 9882.49, 11780.47, 13800.61, 15962.0, 18288.84, 20812.4, - 23574.27, 26631.46, 30065.48, 33999.16] - default_df = utils.to_altitude(df=df, units='ft') - altitude_list_default = default_df['Altitude (ft)'].tolist() - altitude_list_default = [round(num, 2) for num in altitude_list_default] - assert (altitude_list_default == def_ft_key - ), "Equation is not accurate for units='ft'." - - # Base Values = None - base_none_df = utils.to_altitude(df=df, base_press=None, base_temp=None) - altitude_list_none = base_none_df['Altitude (m)'].tolist() - altitude_list_none = [round(num, 2) for num in altitude_list_none] - assert (altitude_list_none == def_key - ), "Equation is not accurate with base settings = None." - - # Error test dfs - temp_df = pd.read_csv("tests/calc/csv_to_df/temp_error.csv") - press_df = df = pd.read_csv("tests/calc/csv_to_df/press_error.csv") - beyond_strat_df = pd.read_csv("tests/calc/csv_to_df/beyond_stratosphere.csv") - - # No temperature column - with pytest.raises(ValueError) as exc_info: - utils.to_altitude(df=temp_df, base_temp=None) - assert (exc_info.type == ValueError), "Error not raised for missing temperature column." - - # No pressure column - with pytest.raises(ValueError) as exc_info: - utils.to_altitude(df=press_df, base_press=None) - assert (exc_info.type == ValueError), "Error not raised for missing pressure column." - - # Beyond stratosphere (50km) + possible_altitude_cols = ['Altitude (m)', 'Altitude (ft)'] + altitude_col = None + + key_df = pd.read_csv("tests/calc/csv_to_df/keys/" + key) + key_list = key_df['Key'].tolist() + + to_alt_df = utils.to_altitude(**kwargs) + for col in possible_altitude_cols: + if col in to_alt_df.columns: + altitude_col = col + break + + to_alt_list = to_alt_df[altitude_col].tolist() + to_alt_list = [round(num, 2) for num in to_alt_list] + assert (to_alt_list == key_list + ), f"Equation is not accurate when {key_df.columns[1]}." + + +# Dataframes for the following altitude error test +df_err_temp = pd.read_csv("tests/calc/csv_to_df/temp_error.csv") +df_err_press = pd.read_csv("tests/calc/csv_to_df/press_error.csv") +df_err_strat = pd.read_csv("tests/calc/csv_to_df/beyond_stratosphere.csv") + +@pytest.mark.parametrize("kwargs, error_message", [ + ({"df": df_err_temp, "base_temp": None}, + "there's no temperature column and base_temp = None"), + ({"df": df_err_press, "base_press": None}, + "there's no pressure column and base_press = None"), + ({"df": df_err_strat}, + "altitude is above the stratosphere (50km)"), + ]) +def test_to_altitude_errors(kwargs, error_message): + """ + Tests that the to_altitude function raises errors when expected to. + """ with pytest.raises(ValueError) as exc_info: - utils.to_altitude(df=beyond_strat_df) - assert(exc_info.type == ValueError), "Error not raised when above stratosphere." - - # Stratosphere Tests - df_2 = pd.read_csv("tests/calc/csv_to_df/stratosphere.csv") - strat_key = [11784.05, 12452.21, 13199.14, 14045.95, 15023.51, 16179.72, - 17594.82, 19419.19, 21990.49, 26386.17, 35177.52] - strat_df = utils.to_altitude(df=df_2) - altitude_list_strat = strat_df['Altitude (m)'].tolist() - altitude_list_strat = [round(num, 2) for num in altitude_list_strat] - assert (altitude_list_strat == strat_key - ), "Equation is not accurate in the Stratosphere." + utils.to_altitude(**kwargs) + assert (exc_info.type == ValueError), f"Error not raised when {error_message}."