Skip to content

Commit 33c7f9d

Browse files
authored
Add barometer temperature checks to sai-cli diagnose (#88)
* Add barometer temperature checks to sai-cli diagnose * Adjust temperature thresholds * Improve barometer warning messages a little * Add barometer altitude plot to sai-cli diagnose (#90)
1 parent dc81991 commit 33c7f9d

2 files changed

Lines changed: 118 additions & 17 deletions

File tree

python/cli/diagnose/diagnose.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def define_subparser(subparsers):
2424
return define_args(sub)
2525

2626
def generateReport(args):
27+
import numpy as np
2728
from datetime import datetime
2829

2930
datasetPath = args.dataset_path
@@ -41,7 +42,12 @@ def generateReport(args):
4142
'accelerometer': {"v": [], "t": [], "td": []},
4243
'gyroscope': {"v": [], "t": [], "td": []},
4344
'magnetometer': {"v": [], "t": [], "td": []},
44-
'barometer': {"v": [], "t": [], "td": []},
45+
'barometer': {
46+
"t": [],
47+
"td": [],
48+
"pressure": [], # Hectopascals
49+
"temperature": [] # Kelvins (optional)
50+
},
4551
'cpu': {"v": [], "t": [], "td": [], "processes": {}},
4652
'gnss': {
4753
"name": "GNSS",
@@ -155,7 +161,13 @@ def convertGnss(gnss, gnssData):
155161
framesMissingNextGyroTime.clear()
156162

157163
elif barometer is not None:
158-
addMeasurement("barometer", t, barometer["pressureHectopascals"])
164+
barometerData = data["barometer"]
165+
barometerData["pressure"].append(barometer["pressureHectopascals"])
166+
barometerData["temperature"].append(barometer.get("temperatureKelvins", np.nan))
167+
if len(barometerData["t"]) > 0:
168+
diff = t - barometerData["t"][-1]
169+
barometerData["td"].append(diff)
170+
barometerData["t"].append(t)
159171

160172
elif gnss is not None:
161173
convertGnss(gnss, data["gnss"])

python/cli/diagnose/sensors.py

Lines changed: 104 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
from enum import Enum
33

44
SECONDS_TO_MILLISECONDS = 1e3
5+
CELCIUS_TO_KELVIN = 273.15
6+
KELVIN_TO_CELCIUS = -CELCIUS_TO_KELVIN
57
TO_PERCENT = 100.0
68

79
SIGNAL_PLOT_KWARGS = {
@@ -468,6 +470,96 @@ def analyzeAccelerometerSignalHasGravity(self, signal):
468470
"This suggests the signal may be missing gravitational acceleration."
469471
)
470472

473+
def analyzeBarometerSignal(self, pressure, temperature, timestamps, data):
474+
self.images.append(plotFrame(
475+
timestamps,
476+
pressure,
477+
"Barometer signal",
478+
yLabel="Pressure (hPa)",
479+
**SIGNAL_PLOT_KWARGS))
480+
481+
if temperature is not None:
482+
self.images.append(plotFrame(
483+
timestamps,
484+
temperature + KELVIN_TO_CELCIUS, # Plot in °C
485+
"Air temperature",
486+
yLabel="Temperature (°C)",
487+
**SIGNAL_PLOT_KWARGS))
488+
489+
groundTruths = getGroundTruths(data)
490+
if len(groundTruths) > 0:
491+
import matplotlib.pyplot as plt
492+
fig, ax1 = plt.subplots(figsize=(8, 6))
493+
ax2 = ax1.twinx()
494+
495+
# https://en.wikipedia.org/wiki/Pressure_altitude
496+
# p = 1013.25 * (1 - h / 44330.694) ** 5.255 (hPa)
497+
altitude = 44330.694 * (1 - (pressure / 1013.25) ** (1 / 5.255))
498+
ax1.plot(timestamps, altitude, label="Barometer altitude")
499+
500+
for groundTruth in groundTruths:
501+
marker = "." if len(groundTruth["t"]) == 1 else ""
502+
ax2.plot(
503+
groundTruth["t"],
504+
groundTruth["altitude"],
505+
label=groundTruth["name"],
506+
color=groundTruth["color"],
507+
linestyle="-",
508+
marker=marker)
509+
510+
ax1.set_xlabel("Time")
511+
ax1.set_ylabel("Standard pressure altitude (m)")
512+
ax1.set_title("Standard pressure altitude and GNSS altitude")
513+
ax1.legend()
514+
ax2.set_ylabel("GNSS altitude (m)")
515+
ax2.legend()
516+
fig.tight_layout()
517+
self.images.append(base64(fig))
518+
519+
if temperature is None: return
520+
521+
minTemp = np.nanmin(temperature)
522+
maxTemp = np.nanmax(temperature)
523+
524+
def temperatureUnitCheck(thresholdKelvins, severityLevel):
525+
thresholdCelcius = thresholdKelvins + KELVIN_TO_CELCIUS
526+
self.__addIssue(severityLevel,
527+
"Barometer temperature measurements have values below threshold "
528+
f"{thresholdKelvins:.1f}K ({thresholdCelcius}°C). "
529+
f"Please verify measurement unit is Kelvins."
530+
)
531+
532+
BARO_UNIT_CHECK_ERROR_THRESHOLD_KELVINS = -60 + CELCIUS_TO_KELVIN
533+
BARO_UNIT_CHECK_WARNING_THRESHOLD_KELVINS = -30 + CELCIUS_TO_KELVIN
534+
if minTemp < BARO_UNIT_CHECK_ERROR_THRESHOLD_KELVINS:
535+
temperatureUnitCheck(BARO_UNIT_CHECK_ERROR_THRESHOLD_KELVINS, DiagnosisLevel.ERROR)
536+
elif minTemp < BARO_UNIT_CHECK_WARNING_THRESHOLD_KELVINS:
537+
temperatureUnitCheck(BARO_UNIT_CHECK_WARNING_THRESHOLD_KELVINS, DiagnosisLevel.WARNING)
538+
539+
def temperatureHighCheck(thresholdKelvins, severityLevel):
540+
thresholdCelcius = thresholdKelvins + KELVIN_TO_CELCIUS
541+
self.__addIssue(severityLevel,
542+
"Barometer temperature measurements have high values above threshold "
543+
f"{thresholdKelvins:.1f}K ({thresholdCelcius}°C). "
544+
"This may indicate that the barometer temperature measurements are not measuring air temperature."
545+
)
546+
547+
AIR_TEMPERATURE_ERROR_THRESHOLD_KELVINS = 60 + CELCIUS_TO_KELVIN
548+
AIR_TEMPERATURE_WARN_THRESHOLD_KELVINS = 40 + CELCIUS_TO_KELVIN
549+
if maxTemp > AIR_TEMPERATURE_ERROR_THRESHOLD_KELVINS:
550+
temperatureHighCheck(AIR_TEMPERATURE_ERROR_THRESHOLD_KELVINS, DiagnosisLevel.ERROR)
551+
elif maxTemp > AIR_TEMPERATURE_WARN_THRESHOLD_KELVINS:
552+
temperatureHighCheck(AIR_TEMPERATURE_WARN_THRESHOLD_KELVINS, DiagnosisLevel.WARNING)
553+
554+
AIR_TEMPERATURE_DELTA_THRESHOLD = 10
555+
if maxTemp - minTemp > AIR_TEMPERATURE_DELTA_THRESHOLD:
556+
self.__addIssue(
557+
DiagnosisLevel.WARNING,
558+
"Barometer temperature measurements changed over "
559+
f"{AIR_TEMPERATURE_DELTA_THRESHOLD:.1f}K over the dataset. "
560+
"This may indicate that the barometer temperature measurements are not measuring air temperature."
561+
)
562+
471563
def analyzeCpuUsage(self, signal, timestamps, processes):
472564
CPU_USAGE_THRESHOLD = 90.0
473565

@@ -953,13 +1045,15 @@ def diagnoseBarometer(data, output):
9531045
BARO_MIN_FREQUENCY_HZ = 1.0
9541046
BARO_MAX_FREQUENCY_HZ = 1e3
9551047
BARO_DUPLICATE_VALUE_THRESHOLD = 0.2
956-
BARO_UNIT_CHECK_MIN_THRESHOLD = 800 # hPa
957-
BARO_UNIT_CHECK_MAX_THRESHOLD = 1200 # hPa
1048+
BARO_PRESSURE_UNIT_CHECK_MIN_THRESHOLD = 800 # hPa
1049+
BARO_PRESSURE_UNIT_CHECK_MAX_THRESHOLD = 1200 # hPa
9581050

9591051
sensor = data["barometer"]
9601052
timestamps = np.array(sensor["t"])
9611053
deltaTimes = np.array(sensor["td"])
962-
signal = np.array(sensor['v'])
1054+
pressure = np.array(sensor["pressure"])
1055+
temperature = np.array(sensor["temperature"])
1056+
if np.all(np.isnan(temperature)): temperature = None
9631057

9641058
if len(timestamps) == 0: return
9651059

@@ -974,28 +1068,23 @@ def diagnoseBarometer(data, output):
9741068
"title": "Barometer time diff"
9751069
},
9761070
isOptionalSensor=True)
977-
status.analyzeSignalDuplicateValues(signal, BARO_DUPLICATE_VALUE_THRESHOLD)
1071+
status.analyzeSignalDuplicateValues(pressure, BARO_DUPLICATE_VALUE_THRESHOLD)
9781072
status.analyzeSignalUnit(
979-
signal,
1073+
pressure,
9801074
timestamps,
9811075
"hPa",
9821076
"Barometer",
983-
BARO_UNIT_CHECK_MIN_THRESHOLD,
984-
BARO_UNIT_CHECK_MAX_THRESHOLD)
1077+
BARO_PRESSURE_UNIT_CHECK_MIN_THRESHOLD,
1078+
BARO_PRESSURE_UNIT_CHECK_MAX_THRESHOLD)
1079+
1080+
status.analyzeBarometerSignal(pressure, temperature, timestamps, data)
9851081

9861082
output["barometer"] = {
9871083
"diagnosis": status.diagnosis.toString(),
9881084
"issues": status.serializeIssues(),
9891085
"frequency": computeSamplingRate(deltaTimes),
9901086
"count": len(timestamps),
991-
"images": [
992-
plotFrame(
993-
timestamps,
994-
signal,
995-
"Barometer signal",
996-
yLabel="Pressure (hPa)",
997-
**SIGNAL_PLOT_KWARGS)
998-
] + status.images
1087+
"images": status.images
9991088
}
10001089
if status.diagnosis == DiagnosisLevel.ERROR:
10011090
output["passed"] = False

0 commit comments

Comments
 (0)