diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 89a7a6b..2734f0f 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.10'] + python-version: ['3.11'] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} diff --git a/pyproject.toml b/pyproject.toml index 420e086..21d0e97 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,16 +8,18 @@ version = "0.10.2" description = "Analysis software for the Lyte probe, a digital penetrometer for studying snow" keywords = ["snow penetrometer", "smart probe", "digital penetrometer", 'lyte probe', "avalanches", "snow densiy"] readme = "README.rst" -requires-python = ">=3.8" +requires-python = ">=3.9" classifiers = [ 'Development Status :: 2 - Pre-Alpha', 'Intended Audience :: Developers', 'Natural Language :: English', - 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10' + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + ] -dependencies = [ "pandas > 2.0.0", "pandas < 3.0.0"] +dependencies = [ "numpy > 2.0.0", "pandas > 2.0.0", "pandas < 3.0.0"] [project.optional-dependencies] dev = [ diff --git a/study_lyte/profile.py b/study_lyte/profile.py index 60a3e4f..17996dd 100644 --- a/study_lyte/profile.py +++ b/study_lyte/profile.py @@ -4,7 +4,7 @@ from pathlib import Path from types import SimpleNamespace import numpy as np - +from functools import cached_property from . io import read_data, find_metadata from .adjustments import get_neutral_bias_at_border, remove_ambient, apply_calibration, get_points_from_fraction, zfilter from .detect import get_acceleration_start, get_acceleration_stop, get_nir_surface, get_nir_stop, get_sensor_start, get_ground_strike @@ -151,6 +151,11 @@ def metadata(self): return self._meta + @cached_property + def end(self): + """End of the data used for analysis, prefer ground if detected""" + return self.stop.index if self.ground.index is None else self.ground.index + @property def nir(self): """ @@ -159,9 +164,8 @@ def nir(self): if self._nir is None: self._nir = self.raw[["Sensor2", "Sensor3", "nir"]] self._nir['depth'] = self.depth.values - end = self.stop.index if self.ground.index is None else self.ground.index - if self.surface.nir.index < end: - self._nir = self._nir.iloc[self.surface.nir.index:end].reset_index() + if self.surface.nir.index < self.end: + self._nir = self._nir.iloc[self.surface.nir.index:self.end].reset_index() self._nir = self._nir.drop(columns='index') self._nir['depth'] = self._nir['depth'] - self._nir['depth'].iloc[0] else: @@ -182,9 +186,7 @@ def force(self): force = force - np.nanmean(force[0:20]) self._force = pd.DataFrame({'force': force, 'depth': self.depth.values}) - # prefer a ground index if available - end = self.stop.index if self.ground.index is None else self.ground.index - self._force = self._force.iloc[self.surface.force.index:end].reset_index() + self._force = self._force.iloc[self.surface.force.index:self.end].reset_index() self._force = self._force.drop(columns='index') if not self._force.empty: self._force['depth'] = self._force['depth'] - self._force['depth'].iloc[0] @@ -486,7 +488,7 @@ def surface(self): """ if self._surface is None: # Call to populate nir in raw - idx = get_nir_surface(self.raw['Sensor3']) + idx = get_nir_surface(self.raw['nir']) if idx == 0: LOG.warning("Unable to find snow surface, defaulting to first data point") # Event according the NIR sensors @@ -496,6 +498,7 @@ def surface(self): # Event according to the force sensor force_surface_depth = depth + self.surface_detection_offset f_idx = abs(self.depth - force_surface_depth).argmin() + # Retrieve force estimated start f_start = get_sensor_start(self.raw['Sensor1'], max_threshold=0.02, threshold=-0.02) f_start = f_start or f_idx diff --git a/tests/test_adjustments.py b/tests/test_adjustments.py index 41752b7..9eab334 100644 --- a/tests/test_adjustments.py +++ b/tests/test_adjustments.py @@ -83,6 +83,7 @@ def poly_function(elapsed, amplitude=4096, frequency=1): @pytest.mark.parametrize('data1_hz, data2_hz, desired_hz', [ + # Typical usage (75, 100, 16000), (100, 75, 100), @@ -108,8 +109,10 @@ def test_merge_on_to_time(data1_hz, data2_hz, desired_hz): # Check timing on both dataframes assert df1['data1'].idxmax() == pytest.approx(final['data1'].idxmax(), abs=3e-2) assert df1['data1'].idxmin() == pytest.approx(final['data1'].idxmin(), abs=3e-2) + # Confirm the handling of multiple datasets assert len(final.columns) == 2 + # Confirm an exact match of length of data assert len(final['data1'][~np.isnan(final['data1'])]) == len(desired) diff --git a/tests/test_profile.py b/tests/test_profile.py index a72190b..0e5e38d 100644 --- a/tests/test_profile.py +++ b/tests/test_profile.py @@ -29,7 +29,7 @@ def test_stop_property(self, profile, filename, depth_method, expected): @pytest.mark.parametrize('filename, depth_method, expected', [ - ('kaslo.csv', 'fused', 11641) + ('kaslo.csv', 'fused', 12565) ]) def test_nir_surface_property(self, profile, filename, depth_method, expected): nir_surface = profile.surface.nir.index