Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 40 additions & 4 deletions study_lyte/calibrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,27 @@
import logging
from dataclasses import dataclass
from typing import List
from datetime import datetime
import pandas as pd

setup_log()

LOG = logging.getLogger('study_lyte.calibrations')

class MissingMeasurementDateException(Exception):
"""
Exception to raise when a probe has multiple calibrations but the date has
not been specified.
"""
pass


@dataclass()
class Calibration:
"""Small class to make accessing calibration data a bit more convenient"""
serial: str
calibration: dict[str, List[float]]
date: datetime = None


class Calibrations:
Expand All @@ -26,16 +37,41 @@ def __init__(self, filename:Path):
with open(filename, mode='r') as fp:
self._info = json.load(fp)

def from_serial(self, serial:str) -> Calibration:
def from_serial(self, serial:str, date: datetime=None) -> Calibration:
""" Build data object from the calibration result """
cal = self._info.get(serial)
if cal is None:
LOG.warning(f"No Calibration found for serial {serial}, using default")
calibrations = self._info.get(serial)
cal = None

if calibrations is None:
cal = self._info['default']
serial = 'UNKNOWN'

else:
# Single calibration, returned as a dict
if isinstance(calibrations, dict):
cal = calibrations

# Account for multiple calibrations
elif isinstance(calibrations, list):
# Check the date is provided
if date is None and len(calibrations) > 1:
raise MissingMeasurementDateException("Multiple calibrations found, but no date provided")
else:
# Find the calibration that matches the date
for c in calibrations:
if date >= pd.to_datetime(c['date']):
cal = c

# No matches were found, date is too early
if cal is None:
LOG.warning(f"All available calibrations for {serial} are not available before {date}, using default")
cal = self._info['default']
serial = 'UNKNOWN'

if cal is not None and serial != 'UNKNOWN':
LOG.info(f"Calibration found ({serial})!")
else:
LOG.warning(f"No calibration found for {serial}, using default")

result = Calibration(serial=serial, calibration=cal)
return result
2 changes: 1 addition & 1 deletion study_lyte/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def set_calibration(self, ext_calibrations:Calibrations):
Args:
ext_calibrations: External collection of calibrations
"""
cal = ext_calibrations.from_serial(self.serial_number)
cal = ext_calibrations.from_serial(self.serial_number, date=self.datetime)
self._calibration = cal.calibration

@property
Expand Down
2 changes: 1 addition & 1 deletion tests/data/angled_measurement.csv
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ MODEL NUMBER = 3
SAMPLE RATE = 16000
ZPFO = 50
ACC. Range = 16
Serial Num. = 252813070A020004
Serial Num. = 252813070A020005
time,Sensor1,Sensor2,Sensor3,Sensor4,depth,X-Axis,Y-Axis,Z-Axis
0.0,3451,6,1216,3665,-0.5615234375,-0.49348,-0.7920499999999999,-0.12045
6.250247868332343e-05,3438,8,1212,3666,-0.5806001142951156,-0.49361417545537245,-0.7920525373676652,-0.1206102717929017
Expand Down
15 changes: 13 additions & 2 deletions tests/data/calibrations.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
{
"252813070A020004":{"Sensor1":[0, 0, -10, 409],
"comment": "Test"},
"252813070A020004":{
"Sensor1":[0, 0, -10, 409],
"comment": "Test"},

"252813070A020005":[
{"date": "2024-01-01",
"Sensor1":[0, 0, -10, 200],
"comment": "Date Test"},

{"date": "2025-05-01",
"Sensor1":[0, 0, -10, 600],
"comment": "Date Test2"}
],

"default":{"Sensor1":[0, 0, -1, 4096],
"comment": "default"}
Expand Down
23 changes: 22 additions & 1 deletion tests/test_calibration.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import pytest
from os.path import join
from pathlib import Path
from study_lyte.calibrations import Calibrations
from study_lyte.calibrations import Calibrations, MissingMeasurementDateException
import pandas as pd


class TestCalibrations:
Expand All @@ -14,12 +15,32 @@ def calibrations(self, calibration_json):
return Calibrations(calibration_json)

@pytest.mark.parametrize("serial, expected", [
# Test actual calibration
("252813070A020004", -10),

# Test no calibration found
("NONSENSE", -1),
])
def test_attributes(self, calibrations, serial, expected):
""""""
result = calibrations.from_serial(serial)
assert result.calibration['Sensor1'][2] == expected

@pytest.mark.parametrize("serial, date, expected", [
# Test valid multi calibration between date 1 and date 2
("252813070A020005", "2024-02-01", 200),
# Test valid multi calibration with exact match on date 2
("252813070A020005", "2025-05-01", 600),
# Test single calibration with a date provided.
("252813070A020004", "2025-01-01", 409),
])
def test_date_based(self, calibrations, serial, date, expected):
""""""
dt = pd.to_datetime(date)
result = calibrations.from_serial(serial, date=dt)
assert result.calibration['Sensor1'][3] == expected

def test_missing_measurement_date_exception(self, calibrations):
""" Confirm this raises an exception when no date is provided """
with pytest.raises(MissingMeasurementDateException):
calibrations.from_serial("252813070A020005", date=None)
3 changes: 3 additions & 0 deletions tests/test_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,9 @@ def test_serial_number(self, profile,filename, depth_method, expected):
("open_air.csv", 'fused', -10),
# Test serial number not found
("mores_20230119.csv", 'fused', -1),
# Tests the date based calibration serial in the profile
("angled_measurement.csv", 'fused', -10),

])
def test_set_calibration(self, data_dir, profile,filename, depth_method, expected):
p = Path(join(data_dir,'calibrations.json'))
Expand Down