diff --git a/docs/analysis/.gitignore b/docs/analysis/.gitignore
new file mode 100644
index 0000000..ef81b1e
--- /dev/null
+++ b/docs/analysis/.gitignore
@@ -0,0 +1 @@
+/.venv/
diff --git a/docs/analysis/__init__.py b/docs/analysis/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/docs/analysis/chart-connection-boxplot.png b/docs/analysis/chart-connection-boxplot.png
new file mode 100644
index 0000000..1136047
Binary files /dev/null and b/docs/analysis/chart-connection-boxplot.png differ
diff --git a/docs/analysis/chart-measurement-stats.svg b/docs/analysis/chart-measurement-stats.svg
new file mode 100644
index 0000000..b077aff
--- /dev/null
+++ b/docs/analysis/chart-measurement-stats.svg
@@ -0,0 +1,245 @@
+
+
+
+
diff --git a/docs/analysis/chart-measurement-stats.xcf b/docs/analysis/chart-measurement-stats.xcf
new file mode 100644
index 0000000..714fa8e
Binary files /dev/null and b/docs/analysis/chart-measurement-stats.xcf differ
diff --git a/docs/analysis/charts.py b/docs/analysis/charts.py
new file mode 100644
index 0000000..e5c3e28
--- /dev/null
+++ b/docs/analysis/charts.py
@@ -0,0 +1,217 @@
+import matplotlib.pyplot as plt
+import numpy as np
+
+plt.rcParams['font.family'] = 'serif'
+plt.rcParams['font.size'] = 12
+
+def generate_measurement_stats_pie_chart(show: bool, save: bool):
+ fig, ax = plt.subplots()
+
+ size_outer = 0.3
+ size_inner = 0.2
+
+ vals_outer = np.array([28., 7.]) # Measurements vs Measurement failures
+ vals_middle = np.array([17., 11., 7., 0.]) # MEWS calculations, Measurement only, P2 failures, P1 failures
+ vals_inner = np.array([16., 1., 8., 3., 2., 5., 0., 0.]) # Innermost ring values
+
+ cmap = plt.get_cmap("tab20b")
+ outer_colors = cmap(np.array([6, 13]))
+ middle_colors = cmap(np.array([5, 15, 14, 14]))
+ inner_colors = cmap(np.array([11, 10, 11, 10, 11, 10, 11, 10]))
+
+ # Outer ring
+ ax.pie(vals_outer, radius=1, colors=outer_colors,
+ wedgeprops=dict(width=size_outer, edgecolor='w'))
+
+ # Middle ring
+ ax.pie(vals_middle, radius=1-size_outer, colors=middle_colors,
+ wedgeprops=dict(width=size_inner, edgecolor='w'))
+
+ # Innermost ring
+ inner_pie = ax.pie(vals_inner, radius=1-size_outer-size_inner, colors=inner_colors,
+ wedgeprops=dict(width=size_inner, edgecolor='w'))
+ #labels_inner = ["home", "on the go"]
+ #ax.legend(inner_pie[0], labels_inner, loc="center right")
+
+ ax.set(aspect="equal")
+
+ if save:
+ plt.savefig(
+ "chart-measurement-stats.png",
+ dpi=900,
+ bbox_inches='tight',
+ transparent=True
+ )
+
+ if show:
+ plt.show()
+
+
+def generate_measurement_repeats_histogram(show: bool, save: bool):
+ device_names = ["Scanwatch", "BPM Core", "Thermo"]
+ labels_attempts = [f"{name} measurement attempts" for name in device_names]
+ labels_failures = ["S\u2081 failures", "B\u2081 failures", "T\u2081 failures"]
+ values_attempts = [43, 33, 28]
+ values_failures = [15, 5, 0]
+
+ # Define a list of colors for the bars
+ cmap = plt.get_cmap("tab20b")
+ colors_attempts = cmap(np.array([15, 11, 7]))
+ colors_failures = cmap(np.array([13, 9, 5]))
+
+ # Plot the bars with the specified colors and labels for the legend
+ bars_attempts = plt.bar(device_names, values_attempts, color=colors_attempts)
+ bars_failures = plt.bar(device_names, values_failures, color=colors_failures)
+
+ # Add individual labels for each bar in the legend
+ for i in range(3):
+ bars_attempts[i].set_label(labels_attempts[i])
+ bars_failures[i].set_label(labels_failures[i])
+
+ plt.ylabel("Count")
+
+ plt.ylim(0, 55)
+ plt.yticks(range(0, 56, 5))
+
+ plt.legend(loc="upper right", prop={"size":8})
+
+ if save:
+ plt.savefig(
+ "chart-measurement-repeats.png",
+ dpi=900,
+ bbox_inches="tight",
+ transparent=True
+ )
+
+ if show:
+ plt.show()
+
+
+def generate_connection_boxplots(show: bool, save: bool):
+ data_downlink = [
+ [50.42, 46.29, 39.94, 39.44, 50.21, 44.34, 41.03, 47.88, 50.34, 41.72, 43.93, 49.34, 50.49, 52.47, 47.04, 45.86, 51.17, 50.12, 52.16, 50.55, 50.69, 53.43, 42.21, 46.81],
+ [15.3, 25.47, 33.91, 12.27]
+ ]
+ data_uplink = [
+ [11.38, 11.24, 8.46, 9.33, 11.39, 9.33, 9.03, 10.65, 11.59, 8.81, 8.91, 10.56, 10.98, 10.04, 9.11, 8.62, 11.08, 10.62, 11.24, 11.29, 10.86, 10.46, 9.16, 9.15],
+ [5.33, 5.46, 3.38, 4.23]
+ ]
+ data_rtt = [
+ [14, 16, 15, 15, 15, 15, 16, 12, 15, 14, 18, 14, 19, 16, 23, 12, 16, 14, 12, 13, 13, 13, 17, 15],
+ [145, 127, 104, 108]
+ ]
+
+ fig, axes = plt.subplots(nrows=1, ncols=3, sharex=True, figsize=(15,5))
+ #plt.subplots_adjust(wspace=0.4)
+
+ cmap = plt.get_cmap("tab20b")
+
+ medianprops= {
+ "color": cmap(1),
+ "linewidth": 2,
+ }
+
+ # Downlink Boxplot
+ axes[1].boxplot(data_downlink, medianprops=medianprops)
+ axes[1].set_ylabel("Datarate [Mbps]")
+ axes[1].set_title("Downlink")
+ axes[1].set_xticks([1, 2])
+ axes[1].set_xticklabels(["At home", "On the go"])
+
+ # Uplink Boxplot
+ axes[0].boxplot(data_uplink, medianprops=medianprops)
+ axes[0].set_ylabel("Datarate [Mbps]")
+ axes[0].set_title("Uplink")
+ axes[0].set_xticks([1, 2])
+ axes[0].set_xticklabels(["At home", "On the go"])
+
+ # RTT Boxplot
+ axes[2].boxplot(data_rtt, medianprops=medianprops)
+ axes[2].set_ylabel("RTT [ms]")
+ axes[2].set_title("RTT")
+ axes[2].set_xticks([1, 2])
+ axes[2].set_xticklabels(["At home", "On the go"])
+
+ # Function to add jitter
+ def jitter(x, width=0.1):
+ """Return the given x coordinate plus a small random offset."""
+ np.random.seed = 0
+ return x + np.random.uniform(-width, width)
+
+ # Sample points to plot on the boxplots
+ points_uplink = [
+ (1, 11.38),
+ (2, 5.33),
+ (1, 9.33),
+ (2, 5.46),
+ (1, 11.59),
+ (1, 10.56),
+ (1, 10.98),
+ (1, 11.24),
+ (2, 4.23),
+ (1, 10.86),
+ (1, 10.46),
+ ]
+ points_downlink = [
+ (1, 50.42),
+ (2, 15.3),
+ (1, 39.44),
+ (2, 25.47),
+ (1, 50.34),
+ (1, 49.34),
+ (1, 50.49),
+ (1, 52.16),
+ (2, 12.27),
+ (1, 50.69),
+ (1, 53.43),
+ ]
+ points_rtt = [
+ (1, 14),
+ (2, 145),
+ (1, 15),
+ (2, 127),
+ (1, 15),
+ (1, 14),
+ (1, 19),
+ (1, 12),
+ (2, 108),
+ (1, 13),
+ (1, 13),
+ ]
+
+ # Plotting points on Uplink Boxplot
+ for x, y in points_uplink:
+ axes[0].scatter(jitter(x), y, marker='x', color=cmap(14), linewidth=0.5)
+
+ # Plotting points on Downlink Boxplot
+ for x, y in points_downlink:
+ axes[1].scatter(jitter(x), y, marker='x', color=cmap(14), linewidth=0.5)
+
+ # Plotting points on RTT Boxplot
+ for x, y in points_rtt:
+ axes[2].scatter(jitter(x), y, marker='x', color=cmap(14), linewidth=0.5)
+
+ # Plot a dummy point for the legend (outside the visible range)
+ #axes[0].scatter([-100], [-100], marker='x', color=cmap(14), linewidth=0.5, label='synchronization failure')
+ #fig.legend(loc='upper center', bbox_to_anchor=(0.5, 1.1))
+
+ if save:
+ plt.savefig(
+ "chart-connection-boxplot.png",
+ dpi=900,
+ bbox_inches="tight",
+ transparent=True
+ )
+
+ if show:
+ plt.show()
+
+
+def main():
+ #generate_measurement_stats_pie_chart(False, True)
+ #generate_measurement_repeats_histogram(False, True)
+ generate_connection_boxplots(False, True)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/docs/analysis/data.json b/docs/analysis/data.json
new file mode 100644
index 0000000..b9d3f39
--- /dev/null
+++ b/docs/analysis/data.json
@@ -0,0 +1,1402 @@
+[
+ {
+ "blood_pressure": {
+ "time": "2023-08-14T10:26:35",
+ "value_diastolic": 86,
+ "value_systolic": 131
+ },
+ "body_temp": {
+ "time": "2023-08-14T10:25:48",
+ "value": 36.98
+ },
+ "heart_rate": {
+ "time": "2023-08-14T10:26:35",
+ "value": 90
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-14T10:24:51",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-14T10:25:35",
+ "value": 96
+ },
+ "s1": 0,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": true,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 11.38,
+ "downlink": 50.42,
+ "rtt": 14.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-14T13:05:06",
+ "value_diastolic": 75,
+ "value_systolic": 117
+ },
+ "body_temp": {
+ "time": "2023-08-14T13:04:18",
+ "value": 37.49
+ },
+ "heart_rate": {
+ "time": "2023-08-14T13:05:06",
+ "value": 71
+ },
+ "mews": {
+ "time": "2023-08-14T13:06:18",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-14T13:03:24",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-14T13:03:59",
+ "value": 97
+ },
+ "s1": 0,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": false,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 11.24,
+ "downlink": 46.29,
+ "rtt": 16.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-14T16:48:16",
+ "value_diastolic": 85,
+ "value_systolic": 126
+ },
+ "body_temp": {
+ "time": "2023-08-14T16:47:12",
+ "value": 37.16
+ },
+ "heart_rate": {
+ "time": "2023-08-14T16:48:16",
+ "value": 73
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-14T16:46:18",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-14T16:46:57",
+ "value": 98
+ },
+ "s1": 0,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": true,
+ "t2": true,
+ "p1": false,
+ "p2": false,
+ "home": false,
+ "on_the_go": true,
+ "uplink": 5.33,
+ "downlink": 15.3,
+ "rtt": 145.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-14T19:06:01",
+ "value_diastolic": 86,
+ "value_systolic": 124
+ },
+ "body_temp": {
+ "time": "2023-08-14T19:05:10",
+ "value": 37.28
+ },
+ "heart_rate": {
+ "time": "2023-08-14T19:06:01",
+ "value": 82
+ },
+ "mews": {
+ "time": "2023-08-14T19:07:22",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-14T19:03:39",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-14T19:04:57",
+ "value": 96
+ },
+ "s1": 1,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": false,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 8.46,
+ "downlink": 39.94,
+ "rtt": 15.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-14T22:27:50",
+ "value_diastolic": 86,
+ "value_systolic": 114
+ },
+ "body_temp": {
+ "time": "2023-08-14T22:27:12",
+ "value": 37.32
+ },
+ "heart_rate": {
+ "time": "2023-08-14T22:27:50",
+ "value": 56
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-14T22:24:14",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-14T22:26:54",
+ "value": 98
+ },
+ "s1": 3,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": true,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 9.33,
+ "downlink": 39.44,
+ "rtt": 15.0
+ },
+ {
+ "blood_pressure": {
+ "time": null,
+ "value_diastolic": null,
+ "value_systolic": null
+ },
+ "body_temp": {
+ "time": null,
+ "value": null
+ },
+ "heart_rate": {
+ "time": null,
+ "value": null
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": null,
+ "value": null
+ },
+ "spo2": {
+ "time": null,
+ "value": null
+ },
+ "s1": null,
+ "b1": null,
+ "t1": null,
+ "s2": null,
+ "b2": null,
+ "t2": null,
+ "p1": true,
+ "p2": null,
+ "home": true,
+ "on_the_go": false,
+ "uplink": null,
+ "downlink": null,
+ "rtt": null
+ },
+ {
+ "blood_pressure": {
+ "time": null,
+ "value_diastolic": null,
+ "value_systolic": null
+ },
+ "body_temp": {
+ "time": null,
+ "value": null
+ },
+ "heart_rate": {
+ "time": null,
+ "value": null
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": null,
+ "value": null
+ },
+ "spo2": {
+ "time": null,
+ "value": null
+ },
+ "s1": null,
+ "b1": null,
+ "t1": null,
+ "s2": null,
+ "b2": null,
+ "t2": null,
+ "p1": true,
+ "p2": null,
+ "home": true,
+ "on_the_go": false,
+ "uplink": null,
+ "downlink": null,
+ "rtt": null
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-15T16:13:28",
+ "value_diastolic": 83,
+ "value_systolic": 115
+ },
+ "body_temp": {
+ "time": "2023-08-15T16:12:25",
+ "value": 37.64
+ },
+ "heart_rate": {
+ "time": "2023-08-15T16:13:28",
+ "value": 84
+ },
+ "mews": {
+ "time": "2023-08-15T16:16:55",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-15T16:11:23",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-15T16:12:03",
+ "value": 96
+ },
+ "s1": 0,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": false,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 11.39,
+ "downlink": 50.21,
+ "rtt": 15.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-15T19:37:37",
+ "value_diastolic": 80,
+ "value_systolic": 114
+ },
+ "body_temp": {
+ "time": "2023-08-15T19:36:32",
+ "value": 37.33
+ },
+ "heart_rate": {
+ "time": "2023-08-15T19:37:37",
+ "value": 75
+ },
+ "mews": {
+ "time": "2023-08-15T19:40:31",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-15T19:35:07",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-15T19:36:19",
+ "value": 97
+ },
+ "s1": 1,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": false,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 9.33,
+ "downlink": 44.34,
+ "rtt": 15.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-15T22:37:09",
+ "value_diastolic": 78,
+ "value_systolic": 114
+ },
+ "body_temp": {
+ "time": "2023-08-15T22:36:27",
+ "value": 37.27
+ },
+ "heart_rate": {
+ "time": "2023-08-15T22:37:09",
+ "value": 63
+ },
+ "mews": {
+ "time": "2023-08-15T22:37:55",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-15T22:35:34",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-15T22:36:09",
+ "value": 99
+ },
+ "s1": 0,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": false,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 9.03,
+ "downlink": 41.03,
+ "rtt": 16.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-16T10:33:29",
+ "value_diastolic": 82,
+ "value_systolic": 112
+ },
+ "body_temp": {
+ "time": "2023-08-16T10:32:39",
+ "value": 37.39
+ },
+ "heart_rate": {
+ "time": "2023-08-16T10:33:29",
+ "value": 93
+ },
+ "mews": {
+ "time": "2023-08-16T10:34:59",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-16T10:31:40",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-16T10:32:23",
+ "value": 96
+ },
+ "s1": 0,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": false,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 10.65,
+ "downlink": 47.88,
+ "rtt": 12.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-16T14:38:04",
+ "value_diastolic": 93,
+ "value_systolic": 119
+ },
+ "body_temp": {
+ "time": "2023-08-16T14:36:55",
+ "value": 37.65
+ },
+ "heart_rate": {
+ "time": "2023-08-16T14:38:04",
+ "value": 72
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-16T14:36:04",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-16T14:36:42",
+ "value": 97
+ },
+ "s1": 0,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": false,
+ "t2": true,
+ "p1": false,
+ "p2": false,
+ "home": false,
+ "on_the_go": true,
+ "uplink": 5.46,
+ "downlink": 25.47,
+ "rtt": 127.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-16T16:47:26",
+ "value_diastolic": 83,
+ "value_systolic": 118
+ },
+ "body_temp": {
+ "time": "2023-08-16T16:45:42",
+ "value": 37.64
+ },
+ "heart_rate": {
+ "time": "2023-08-16T16:47:26",
+ "value": 89
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-16T16:44:39",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-16T16:45:28",
+ "value": 98
+ },
+ "s1": 0,
+ "b1": 1,
+ "t1": 0,
+ "s2": false,
+ "b2": false,
+ "t2": true,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 11.59,
+ "downlink": 50.34,
+ "rtt": 15.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-16T20:12:49",
+ "value_diastolic": 81,
+ "value_systolic": 118
+ },
+ "body_temp": {
+ "time": "2023-08-16T20:11:58",
+ "value": 37.52
+ },
+ "heart_rate": {
+ "time": "2023-08-16T20:12:49",
+ "value": 97
+ },
+ "mews": {
+ "time": "2023-08-16T20:15:42",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-16T20:10:52",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-16T20:11:35",
+ "value": 98
+ },
+ "s1": 0,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": false,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 8.81,
+ "downlink": 41.72,
+ "rtt": 14.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-16T23:18:08",
+ "value_diastolic": 74,
+ "value_systolic": 125
+ },
+ "body_temp": {
+ "time": "2023-08-16T23:15:39",
+ "value": 37.65
+ },
+ "heart_rate": {
+ "time": "2023-08-16T23:18:08",
+ "value": 85
+ },
+ "mews": {
+ "time": "2023-08-16T23:18:50",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-16T23:13:39",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-16T23:15:17",
+ "value": 99
+ },
+ "s1": 1,
+ "b1": 2,
+ "t1": 0,
+ "s2": false,
+ "b2": false,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 8.91,
+ "downlink": 43.93,
+ "rtt": 18.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-17T11:01:05",
+ "value_diastolic": 79,
+ "value_systolic": 112
+ },
+ "body_temp": {
+ "time": "2023-08-17T11:00:04",
+ "value": 37.05
+ },
+ "heart_rate": {
+ "time": "2023-08-17T11:01:05",
+ "value": 87
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-17T10:58:59",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-17T10:59:48",
+ "value": 95
+ },
+ "s1": 0,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": true,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 10.56,
+ "downlink": 49.34,
+ "rtt": 14.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-17T14:27:40",
+ "value_diastolic": 82,
+ "value_systolic": 118
+ },
+ "body_temp": {
+ "time": "2023-08-17T14:27:09",
+ "value": 37.43
+ },
+ "heart_rate": {
+ "time": "2023-08-17T14:27:40",
+ "value": 80
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-17T14:25:59",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-17T14:26:45",
+ "value": 98
+ },
+ "s1": 0,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": true,
+ "t2": true,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 10.98,
+ "downlink": 50.49,
+ "rtt": 19.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-17T16:53:03",
+ "value_diastolic": 71,
+ "value_systolic": 121
+ },
+ "body_temp": {
+ "time": "2023-08-17T16:52:21",
+ "value": 37.73
+ },
+ "heart_rate": {
+ "time": "2023-08-17T16:53:03",
+ "value": 83
+ },
+ "mews": {
+ "time": "2023-08-17T16:54:15",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-17T16:50:34",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-17T16:51:58",
+ "value": 98
+ },
+ "s1": 1,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": false,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 10.04,
+ "downlink": 52.47,
+ "rtt": 16.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-17T20:12:24",
+ "value_diastolic": 69,
+ "value_systolic": 129
+ },
+ "body_temp": {
+ "time": "2023-08-17T20:11:32",
+ "value": 37.46
+ },
+ "heart_rate": {
+ "time": "2023-08-17T20:12:24",
+ "value": 73
+ },
+ "mews": {
+ "time": "2023-08-17T20:13:09",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-17T20:10:31",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-17T20:11:13",
+ "value": 96
+ },
+ "s1": 0,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": false,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 9.11,
+ "downlink": 47.04,
+ "rtt": 23.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-17T22:47:55",
+ "value_diastolic": 81,
+ "value_systolic": 121
+ },
+ "body_temp": {
+ "time": "2023-08-17T22:47:18",
+ "value": 36.79
+ },
+ "heart_rate": {
+ "time": "2023-08-17T22:47:55",
+ "value": 95
+ },
+ "mews": {
+ "time": "2023-08-17T22:50:02",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-17T22:46:04",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-17T22:46:54",
+ "value": 97
+ },
+ "s1": 0,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": false,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 8.62,
+ "downlink": 45.86,
+ "rtt": 12.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-18T10:54:12",
+ "value_diastolic": 75,
+ "value_systolic": 124
+ },
+ "body_temp": {
+ "time": "2023-08-18T10:53:27",
+ "value": 37.34
+ },
+ "heart_rate": {
+ "time": "2023-08-18T10:54:12",
+ "value": 73
+ },
+ "mews": {
+ "time": "2023-08-18T10:55:50",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-18T10:51:36",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-18T10:53:12",
+ "value": 96
+ },
+ "s1": 1,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": false,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 11.08,
+ "downlink": 51.17,
+ "rtt": 16.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-18T13:32:51",
+ "value_diastolic": 86,
+ "value_systolic": 123
+ },
+ "body_temp": {
+ "time": "2023-08-18T13:32:16",
+ "value": 37.09
+ },
+ "heart_rate": {
+ "time": "2023-08-18T13:32:51",
+ "value": 61
+ },
+ "mews": {
+ "time": "2023-08-18T13:34:01",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-18T13:29:25",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-18T13:31:55",
+ "value": 97
+ },
+ "s1": 2,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": false,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 10.62,
+ "downlink": 50.12,
+ "rtt": 14.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-18T16:39:14",
+ "value_diastolic": 83,
+ "value_systolic": 122
+ },
+ "body_temp": {
+ "time": "2023-08-18T16:37:22",
+ "value": 37.13
+ },
+ "heart_rate": {
+ "time": "2023-08-18T16:39:14",
+ "value": 73
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-18T16:36:14",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-18T16:37:02",
+ "value": 96
+ },
+ "s1": 0,
+ "b1": 1,
+ "t1": 0,
+ "s2": true,
+ "b2": false,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 11.24,
+ "downlink": 52.16,
+ "rtt": 12.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-18T19:38:58",
+ "value_diastolic": 75,
+ "value_systolic": 123
+ },
+ "body_temp": {
+ "time": "2023-08-18T19:36:37",
+ "value": 37.65
+ },
+ "heart_rate": {
+ "time": "2023-08-18T19:38:58",
+ "value": 114
+ },
+ "mews": {
+ "time": "2023-08-18T19:41:43",
+ "value": 2
+ },
+ "respiration_score": {
+ "time": "2023-08-18T19:35:24",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-18T19:36:14",
+ "value": 95
+ },
+ "s1": 0,
+ "b1": 1,
+ "t1": 0,
+ "s2": false,
+ "b2": false,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": false,
+ "on_the_go": true,
+ "uplink": 3.38,
+ "downlink": 33.91,
+ "rtt": 104.0
+ },
+ {
+ "blood_pressure": {
+ "time": null,
+ "value_diastolic": null,
+ "value_systolic": null
+ },
+ "body_temp": {
+ "time": null,
+ "value": null
+ },
+ "heart_rate": {
+ "time": null,
+ "value": null
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": null,
+ "value": null
+ },
+ "spo2": {
+ "time": null,
+ "value": null
+ },
+ "s1": null,
+ "b1": null,
+ "t1": null,
+ "s2": null,
+ "b2": null,
+ "t2": null,
+ "p1": true,
+ "p2": null,
+ "home": false,
+ "on_the_go": true,
+ "uplink": null,
+ "downlink": null,
+ "rtt": null
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-19T11:36:26",
+ "value_diastolic": 76,
+ "value_systolic": 121
+ },
+ "body_temp": {
+ "time": "2023-08-19T11:35:24",
+ "value": 37.5
+ },
+ "heart_rate": {
+ "time": "2023-08-19T11:36:26",
+ "value": 54
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-19T11:34:30",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-19T11:35:05",
+ "value": 98
+ },
+ "s1": 0,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": true,
+ "t2": true,
+ "p1": false,
+ "p2": false,
+ "home": false,
+ "on_the_go": true,
+ "uplink": 4.23,
+ "downlink": 12.27,
+ "rtt": 108.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-19T13:24:56",
+ "value_diastolic": 73,
+ "value_systolic": 120
+ },
+ "body_temp": {
+ "time": "2023-08-19T13:24:02",
+ "value": 37.31
+ },
+ "heart_rate": {
+ "time": "2023-08-19T13:24:56",
+ "value": 106
+ },
+ "mews": {
+ "time": "2023-08-19T13:27:13",
+ "value": 1
+ },
+ "respiration_score": {
+ "time": "2023-08-19T13:21:46",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-19T13:23:43",
+ "value": 96
+ },
+ "s1": 2,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": false,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 11.29,
+ "downlink": 50.55,
+ "rtt": 13.0
+ },
+ {
+ "blood_pressure": {
+ "time": null,
+ "value_diastolic": null,
+ "value_systolic": null
+ },
+ "body_temp": {
+ "time": null,
+ "value": null
+ },
+ "heart_rate": {
+ "time": null,
+ "value": null
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": null,
+ "value": null
+ },
+ "spo2": {
+ "time": null,
+ "value": null
+ },
+ "s1": null,
+ "b1": null,
+ "t1": null,
+ "s2": null,
+ "b2": null,
+ "t2": null,
+ "p1": true,
+ "p2": null,
+ "home": false,
+ "on_the_go": true,
+ "uplink": null,
+ "downlink": null,
+ "rtt": null
+ },
+ {
+ "blood_pressure": {
+ "time": null,
+ "value_diastolic": null,
+ "value_systolic": null
+ },
+ "body_temp": {
+ "time": null,
+ "value": null
+ },
+ "heart_rate": {
+ "time": null,
+ "value": null
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": null,
+ "value": null
+ },
+ "spo2": {
+ "time": null,
+ "value": null
+ },
+ "s1": null,
+ "b1": null,
+ "t1": null,
+ "s2": null,
+ "b2": null,
+ "t2": null,
+ "p1": true,
+ "p2": null,
+ "home": false,
+ "on_the_go": true,
+ "uplink": null,
+ "downlink": null,
+ "rtt": null
+ },
+ {
+ "blood_pressure": {
+ "time": null,
+ "value_diastolic": null,
+ "value_systolic": null
+ },
+ "body_temp": {
+ "time": null,
+ "value": null
+ },
+ "heart_rate": {
+ "time": null,
+ "value": null
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": null,
+ "value": null
+ },
+ "spo2": {
+ "time": null,
+ "value": null
+ },
+ "s1": null,
+ "b1": null,
+ "t1": null,
+ "s2": null,
+ "b2": null,
+ "t2": null,
+ "p1": true,
+ "p2": null,
+ "home": false,
+ "on_the_go": true,
+ "uplink": null,
+ "downlink": null,
+ "rtt": null
+ },
+ {
+ "blood_pressure": {
+ "time": null,
+ "value_diastolic": null,
+ "value_systolic": null
+ },
+ "body_temp": {
+ "time": null,
+ "value": null
+ },
+ "heart_rate": {
+ "time": null,
+ "value": null
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": null,
+ "value": null
+ },
+ "spo2": {
+ "time": null,
+ "value": null
+ },
+ "s1": null,
+ "b1": null,
+ "t1": null,
+ "s2": null,
+ "b2": null,
+ "t2": null,
+ "p1": true,
+ "p2": null,
+ "home": false,
+ "on_the_go": true,
+ "uplink": null,
+ "downlink": null,
+ "rtt": null
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-20T13:21:35",
+ "value_diastolic": 82,
+ "value_systolic": 119
+ },
+ "body_temp": {
+ "time": "2023-08-20T13:20:44",
+ "value": 37.29
+ },
+ "heart_rate": {
+ "time": "2023-08-20T13:21:35",
+ "value": 83
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-20T13:18:46",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-20T13:20:20",
+ "value": 98
+ },
+ "s1": 1,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": false,
+ "t2": true,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 10.86,
+ "downlink": 50.69,
+ "rtt": 13.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-20T16:13:04",
+ "value_diastolic": 79,
+ "value_systolic": 118
+ },
+ "body_temp": {
+ "time": "2023-08-20T16:12:05",
+ "value": 37.38
+ },
+ "heart_rate": {
+ "time": "2023-08-20T16:13:04",
+ "value": 95
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-20T16:10:40",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-20T16:11:52",
+ "value": 97
+ },
+ "s1": 1,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": true,
+ "t2": true,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 10.46,
+ "downlink": 53.43,
+ "rtt": 13.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-20T19:06:47",
+ "value_diastolic": 82,
+ "value_systolic": 120
+ },
+ "body_temp": {
+ "time": "2023-08-20T19:05:37",
+ "value": 37.25
+ },
+ "heart_rate": {
+ "time": "2023-08-20T19:06:47",
+ "value": 93
+ },
+ "mews": {
+ "time": "2023-08-20T19:12:52",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-20T19:04:47",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-20T19:05:24",
+ "value": 97
+ },
+ "s1": 0,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": false,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 9.16,
+ "downlink": 42.21,
+ "rtt": 17.0
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-20T22:25:47",
+ "value_diastolic": 73,
+ "value_systolic": 115
+ },
+ "body_temp": {
+ "time": "2023-08-20T22:25:06",
+ "value": 37.45
+ },
+ "heart_rate": {
+ "time": "2023-08-20T22:25:47",
+ "value": 83
+ },
+ "mews": {
+ "time": "2023-08-20T22:29:09",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-20T22:23:27",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-20T22:24:51",
+ "value": 97
+ },
+ "s1": 1,
+ "b1": 0,
+ "t1": 0,
+ "s2": false,
+ "b2": false,
+ "t2": false,
+ "p1": false,
+ "p2": false,
+ "home": true,
+ "on_the_go": false,
+ "uplink": 9.15,
+ "downlink": 46.81,
+ "rtt": 15.0
+ }
+]
diff --git a/docs/analysis/data.ods b/docs/analysis/data.ods
new file mode 100644
index 0000000..84cc6a8
Binary files /dev/null and b/docs/analysis/data.ods differ
diff --git a/docs/analysis/main.py b/docs/analysis/main.py
new file mode 100644
index 0000000..b98bc0a
--- /dev/null
+++ b/docs/analysis/main.py
@@ -0,0 +1,108 @@
+import json
+from datetime import datetime, timedelta
+
+import records as r
+
+def get_records() -> list[r.Collection]:
+ """Returns the trial data as a list of `records.Collection` objects."""
+
+ json_data = []
+ with open('data.json', 'r') as file:
+ json_data = json.load(file)
+
+ def incremented(time: datetime) -> datetime:
+ if time.hour == 22:
+ time += timedelta(hours=12)
+ else:
+ time += timedelta(hours=3)
+ return time
+
+ records: list[r.Collection] = []
+ time = datetime.fromisoformat("2023-08-14T10:00:00")
+
+ for item in json_data:
+ p1 = item['p1']
+ home_environment = item['home']
+
+ if p1:
+ mews = None
+ blood_pressure = None
+ body_temp = None
+ heart_rate = None
+ respiration_score = None
+ spo2 = None
+ s1 = None
+ b1 = None
+ t1 = None
+ p2 = None
+ s2 = None
+ b2 = None
+ t2 = None
+ uplink = None
+ downlink = None
+ rtt = None
+ else:
+ blood_pressure = r.BloodPressure(
+ time=datetime.fromisoformat(item['blood_pressure']['time']),
+ value_systolic=item['blood_pressure']['value_systolic'],
+ value_diastolic=item['blood_pressure']['value_systolic']
+ )
+ body_temp = r.BodyTemp(
+ time=datetime.fromisoformat(item['body_temp']['time']),
+ value=item['body_temp']['value']
+ )
+ heart_rate = r.HeartRate(
+ time=datetime.fromisoformat(item['heart_rate']['time']),
+ value=item['heart_rate']['value']
+ )
+ respiration_score = r.RespirationScore(
+ time=datetime.fromisoformat(item['respiration_score']['time']),
+ value=item['respiration_score']['value']
+ )
+ spo2 = r.Spo2(
+ time=datetime.fromisoformat(item['spo2']['time']),
+ value=item['spo2']['value']
+ )
+ s1 = item['s1']
+ b1 = item['b1']
+ t1 = item['t1']
+ p2 = item['p2']
+ s2 = item['s2']
+ b2 = item['b2']
+ t2 = item['t2']
+ uplink = item['uplink']
+ downlink = item['downlink']
+ rtt = item['rtt']
+ if s2 or b2 or t2:
+ mews = None
+ else:
+ mews = r.Mews(
+ time=datetime.fromisoformat(item['mews']['time']),
+ value=item['mews']['value']
+ )
+
+ records.append(r.Collection(
+ notification_time=time,
+ mews=mews,
+ blood_pressure=blood_pressure,
+ body_temp=body_temp,
+ heart_rate=heart_rate,
+ respiration_score=respiration_score,
+ spo2=spo2,
+ s1=s1,
+ b1=b1,
+ t1=t1,
+ s2=s2,
+ b2=b2,
+ t2=t2,
+ p1=p1,
+ p2=p2,
+ home_environment=home_environment,
+ uplink=uplink,
+ downlink=downlink,
+ rtt=rtt
+ ))
+
+ time = incremented(time)
+
+ return records
diff --git a/docs/analysis/records.py b/docs/analysis/records.py
new file mode 100644
index 0000000..664c98f
--- /dev/null
+++ b/docs/analysis/records.py
@@ -0,0 +1,65 @@
+"""Type classes used during analysis."""
+
+from datetime import datetime
+from dataclasses import dataclass
+from typing import Optional
+
+
+@dataclass
+class BloodPressure:
+ time: datetime
+ value_diastolic: int
+ value_systolic: int
+
+
+@dataclass
+class BodyTemp:
+ time: datetime
+ value: float
+
+
+@dataclass
+class HeartRate:
+ time: datetime
+ value: int
+
+
+@dataclass
+class Mews:
+ time: datetime
+ value: int
+
+
+@dataclass
+class RespirationScore:
+ time: datetime
+ value: int
+
+
+@dataclass
+class Spo2:
+ time: datetime
+ value: int
+
+
+@dataclass
+class Collection:
+ notification_time: datetime
+ mews: Optional[Mews]
+ blood_pressure: Optional[BloodPressure]
+ body_temp: Optional[BodyTemp]
+ heart_rate: Optional[HeartRate]
+ respiration_score: Optional[RespirationScore]
+ spo2: Optional[Spo2]
+ s1: Optional[int]
+ b1: Optional[int]
+ t1: Optional[int]
+ s2: Optional[bool]
+ b2: Optional[bool]
+ t2: Optional[bool]
+ p1: bool
+ p2: Optional[bool]
+ home_environment: bool
+ uplink: Optional[float]
+ downlink: Optional[float]
+ rtt: Optional[int]
diff --git a/docs/analysis/shell.nix b/docs/analysis/shell.nix
new file mode 100644
index 0000000..49e03a1
--- /dev/null
+++ b/docs/analysis/shell.nix
@@ -0,0 +1,14 @@
+{ pkgs ? import {} }:
+ pkgs.mkShell {
+ nativeBuildInputs = with pkgs; [
+ python3Packages.numpy
+ python3Packages.matplotlib
+ python310Packages.pyqt6
+ python310Packages.pyside6
+ qt6.qtwayland
+ ];
+
+ shellHook = ''
+ export MPLBACKEND=QtAgg
+ '';
+}
diff --git a/docs/bibliography/bibliography.bib b/docs/bibliography/bibliography.bib
index f84ed02..b1b2cac 100644
--- a/docs/bibliography/bibliography.bib
+++ b/docs/bibliography/bibliography.bib
@@ -1187,3 +1187,52 @@ Very low and high {EWS} are able to discriminate between patients who are not li
pmid = {30247849},
file = {Printable HTML:/home/ulinja/Zotero/storage/ISD8WIRR/NBK525974.html:text/html},
}
+
+@online{noauthor_worlds_nodate,
+ title = {The world’s first analog watch with clinically validated {ECG} - {ScanWatch} {\textbar} Withings},
+ url = {https://www.withings.com/de/en/scanwatch},
+ abstract = {The hybrid smartwatch that detects atrial fibrillation, with {ECG} and an oximeter for {SpO}2 measurements—plus activity and sleep tracking.},
+ urldate = {2023-08-22},
+ langid = {english},
+ file = {Snapshot:/home/ulinja/Zotero/storage/7ZR5X6SG/scanwatch.html:text/html},
+}
+
+@online{noauthor_scanwatch_nodate,
+ title = {{ScanWatch} - Performing a {SpO}2 measurement},
+ url = {https://support.withings.com/hc/en-us/articles/360010097498-ScanWatch-Performing-a-SpO2-measurement},
+ abstract = {Disclaimer: Due to health regulations and clearances, some {ScanWatch} features may not be available or may not have clinical validation in your region. Learn more.
+The result of a one-time measureme...},
+ titleaddon = {Withings {\textbar} Support},
+ urldate = {2023-08-22},
+ langid = {american},
+ file = {Snapshot:/home/ulinja/Zotero/storage/SXLEIBSV/360010097498-ScanWatch-Performing-a-SpO2-measurement.html:text/html},
+}
+
+@online{noauthor_bpm_nodate,
+ title = {{BPM} Core {\textbar} Withings},
+ url = {https://www.withings.com/de/en/bpm-core},
+ abstract = {The first Wi-Fi blood pressure monitor with {ECG} and a digital stethoscope to detect cardiovascular conditions like atrial fibrillation and valvular heart disease.},
+ urldate = {2023-08-22},
+ langid = {english},
+ file = {Snapshot:/home/ulinja/Zotero/storage/UTB8VF5C/bpm-core.html:text/html},
+}
+
+@online{noauthor_bpm_nodate-1,
+ title = {{BPM} Core - Positioning myself and the {BPM} for the measurement},
+ url = {https://support.withings.com/hc/en-us/articles/360024168954-BPM-Core-Positioning-myself-and-the-BPM-for-the-measurement},
+ abstract = {Note: With Firmware Release 841, we have introduced an optional Heart Sound Measurement Assistance Mode which can provide real-time feedback for correct positioning. Read about it here.
+
+
+{BPM} Cor...},
+ titleaddon = {Withings {\textbar} Support},
+ urldate = {2023-08-22},
+ langid = {american},
+ file = {Snapshot:/home/ulinja/Zotero/storage/8LN5S3XU/360024168954-BPM-Core-Positioning-myself-and-the-BPM-for-the-measurement.html:text/html},
+}
+
+@online{noauthor_guides_nodate,
+ title = {Guides documents {\textbar} Withings},
+ url = {https://www.withings.com/de/en/guides},
+ urldate = {2023-08-22},
+ file = {Guides documents | Withings:/home/ulinja/Zotero/storage/IJU54NKT/guides.html:text/html},
+}
diff --git a/docs/misc/trial-data.json b/docs/misc/trial-data.json
new file mode 100644
index 0000000..15c467d
--- /dev/null
+++ b/docs/misc/trial-data.json
@@ -0,0 +1,947 @@
+[
+ {
+ "blood_pressure": {
+ "time": "2023-08-14T10:26:35",
+ "value_diastolic": 86,
+ "value_systolic": 131
+ },
+ "body_temp": {
+ "time": "2023-08-14T10:25:48",
+ "value": 36.98
+ },
+ "heart_rate": {
+ "time": "2023-08-14T10:26:35",
+ "value": 90
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-14T10:24:51",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-14T10:25:35",
+ "value": 96
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-14T13:05:06",
+ "value_diastolic": 75,
+ "value_systolic": 117
+ },
+ "body_temp": {
+ "time": "2023-08-14T13:04:18",
+ "value": 37.49
+ },
+ "heart_rate": {
+ "time": "2023-08-14T13:05:06",
+ "value": 71
+ },
+ "mews": {
+ "time": "2023-08-14T13:06:18",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-14T13:03:24",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-14T13:03:59",
+ "value": 97
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-14T16:48:16",
+ "value_diastolic": 85,
+ "value_systolic": 126
+ },
+ "body_temp": {
+ "time": "2023-08-14T16:47:12",
+ "value": 37.16
+ },
+ "heart_rate": {
+ "time": "2023-08-14T16:48:16",
+ "value": 73
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-14T16:46:18",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-14T16:46:57",
+ "value": 98
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-14T19:06:01",
+ "value_diastolic": 86,
+ "value_systolic": 124
+ },
+ "body_temp": {
+ "time": "2023-08-14T19:05:10",
+ "value": 37.28
+ },
+ "heart_rate": {
+ "time": "2023-08-14T19:06:01",
+ "value": 82
+ },
+ "mews": {
+ "time": "2023-08-14T19:07:22",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-14T19:03:39",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-14T19:04:57",
+ "value": 96
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-14T22:27:50",
+ "value_diastolic": 86,
+ "value_systolic": 114
+ },
+ "body_temp": {
+ "time": "2023-08-14T22:27:12",
+ "value": 37.32
+ },
+ "heart_rate": {
+ "time": "2023-08-14T22:27:50",
+ "value": 56
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-14T22:24:14",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-14T22:26:54",
+ "value": 98
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": null,
+ "value_diastolic": null,
+ "value_systolic": null
+ },
+ "body_temp": {
+ "time": null,
+ "value": null
+ },
+ "heart_rate": {
+ "time": null,
+ "value": null
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": null,
+ "value": null
+ },
+ "spo2": {
+ "time": null,
+ "value": null
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": null,
+ "value_diastolic": null,
+ "value_systolic": null
+ },
+ "body_temp": {
+ "time": null,
+ "value": null
+ },
+ "heart_rate": {
+ "time": null,
+ "value": null
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": null,
+ "value": null
+ },
+ "spo2": {
+ "time": null,
+ "value": null
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-15T16:13:28",
+ "value_diastolic": 83,
+ "value_systolic": 115
+ },
+ "body_temp": {
+ "time": "2023-08-15T16:12:25",
+ "value": 37.64
+ },
+ "heart_rate": {
+ "time": "2023-08-15T16:13:28",
+ "value": 84
+ },
+ "mews": {
+ "time": "2023-08-15T16:16:55",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-15T16:11:23",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-15T16:12:03",
+ "value": 96
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-15T19:37:37",
+ "value_diastolic": 80,
+ "value_systolic": 114
+ },
+ "body_temp": {
+ "time": "2023-08-15T19:36:32",
+ "value": 37.33
+ },
+ "heart_rate": {
+ "time": "2023-08-15T19:37:37",
+ "value": 75
+ },
+ "mews": {
+ "time": "2023-08-15T19:40:31",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-15T19:35:07",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-15T19:36:19",
+ "value": 97
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-15T22:37:09",
+ "value_diastolic": 78,
+ "value_systolic": 114
+ },
+ "body_temp": {
+ "time": "2023-08-15T22:36:27",
+ "value": 37.27
+ },
+ "heart_rate": {
+ "time": "2023-08-15T22:37:09",
+ "value": 63
+ },
+ "mews": {
+ "time": "2023-08-15T22:37:55",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-15T22:35:34",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-15T22:36:09",
+ "value": 99
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-16T10:33:29",
+ "value_diastolic": 82,
+ "value_systolic": 112
+ },
+ "body_temp": {
+ "time": "2023-08-16T10:32:39",
+ "value": 37.39
+ },
+ "heart_rate": {
+ "time": "2023-08-16T10:33:29",
+ "value": 93
+ },
+ "mews": {
+ "time": "2023-08-16T10:34:59",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-16T10:31:40",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-16T10:32:23",
+ "value": 96
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-16T14:38:04",
+ "value_diastolic": 93,
+ "value_systolic": 119
+ },
+ "body_temp": {
+ "time": "2023-08-16T14:36:55",
+ "value": 37.65
+ },
+ "heart_rate": {
+ "time": "2023-08-16T14:38:04",
+ "value": 72
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-16T14:36:04",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-16T14:36:42",
+ "value": 97
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-16T16:47:26",
+ "value_diastolic": 83,
+ "value_systolic": 118
+ },
+ "body_temp": {
+ "time": "2023-08-16T16:45:42",
+ "value": 37.64
+ },
+ "heart_rate": {
+ "time": "2023-08-16T16:47:26",
+ "value": 89
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-16T16:44:39",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-16T16:45:28",
+ "value": 98
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-16T20:12:49",
+ "value_diastolic": 81,
+ "value_systolic": 118
+ },
+ "body_temp": {
+ "time": "2023-08-16T20:11:58",
+ "value": 37.52
+ },
+ "heart_rate": {
+ "time": "2023-08-16T20:12:49",
+ "value": 97
+ },
+ "mews": {
+ "time": "2023-08-16T20:15:42",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-16T20:10:52",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-16T20:11:35",
+ "value": 98
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-16T23:18:08",
+ "value_diastolic": 74,
+ "value_systolic": 125
+ },
+ "body_temp": {
+ "time": "2023-08-16T23:15:39",
+ "value": 37.65
+ },
+ "heart_rate": {
+ "time": "2023-08-16T23:18:08",
+ "value": 85
+ },
+ "mews": {
+ "time": "2023-08-16T23:18:50",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-16T23:13:39",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-16T23:15:17",
+ "value": 99
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-17T11:01:05",
+ "value_diastolic": 79,
+ "value_systolic": 112
+ },
+ "body_temp": {
+ "time": "2023-08-17T11:00:04",
+ "value": 37.05
+ },
+ "heart_rate": {
+ "time": "2023-08-17T11:01:05",
+ "value": 87
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-17T10:58:59",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-17T10:59:48",
+ "value": 95
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-17T14:27:40",
+ "value_diastolic": 82,
+ "value_systolic": 118
+ },
+ "body_temp": {
+ "time": "2023-08-17T14:27:09",
+ "value": 37.43
+ },
+ "heart_rate": {
+ "time": "2023-08-17T14:27:40",
+ "value": 80
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-17T14:25:59",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-17T14:26:45",
+ "value": 98
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-17T16:53:03",
+ "value_diastolic": 71,
+ "value_systolic": 121
+ },
+ "body_temp": {
+ "time": "2023-08-17T16:52:21",
+ "value": 37.73
+ },
+ "heart_rate": {
+ "time": "2023-08-17T16:53:03",
+ "value": 83
+ },
+ "mews": {
+ "time": "2023-08-17T16:54:15",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-17T16:50:34",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-17T16:51:58",
+ "value": 98
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-17T20:12:24",
+ "value_diastolic": 69,
+ "value_systolic": 129
+ },
+ "body_temp": {
+ "time": "2023-08-17T20:11:32",
+ "value": 37.46
+ },
+ "heart_rate": {
+ "time": "2023-08-17T20:12:24",
+ "value": 73
+ },
+ "mews": {
+ "time": "2023-08-17T20:13:09",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-17T20:10:31",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-17T20:11:13",
+ "value": 96
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-17T22:47:55",
+ "value_diastolic": 81,
+ "value_systolic": 121
+ },
+ "body_temp": {
+ "time": "2023-08-17T22:47:18",
+ "value": 36.79
+ },
+ "heart_rate": {
+ "time": "2023-08-17T22:47:55",
+ "value": 95
+ },
+ "mews": {
+ "time": "2023-08-17T22:50:02",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-17T22:46:04",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-17T22:46:54",
+ "value": 97
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-18T10:54:12",
+ "value_diastolic": 75,
+ "value_systolic": 124
+ },
+ "body_temp": {
+ "time": "2023-08-18T10:53:27",
+ "value": 37.34
+ },
+ "heart_rate": {
+ "time": "2023-08-18T10:54:12",
+ "value": 73
+ },
+ "mews": {
+ "time": "2023-08-18T10:55:50",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-18T10:51:36",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-18T10:53:12",
+ "value": 96
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-18T13:32:51",
+ "value_diastolic": 86,
+ "value_systolic": 123
+ },
+ "body_temp": {
+ "time": "2023-08-18T13:32:16",
+ "value": 37.09
+ },
+ "heart_rate": {
+ "time": "2023-08-18T13:32:51",
+ "value": 61
+ },
+ "mews": {
+ "time": "2023-08-18T13:34:01",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-18T13:29:25",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-18T13:31:55",
+ "value": 97
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-18T16:39:14",
+ "value_diastolic": 83,
+ "value_systolic": 122
+ },
+ "body_temp": {
+ "time": "2023-08-18T16:37:22",
+ "value": 37.13
+ },
+ "heart_rate": {
+ "time": "2023-08-18T16:39:14",
+ "value": 73
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-18T16:36:14",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-18T16:37:02",
+ "value": 96
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-18T19:38:58",
+ "value_diastolic": 75,
+ "value_systolic": 123
+ },
+ "body_temp": {
+ "time": "2023-08-18T19:36:37",
+ "value": 37.65
+ },
+ "heart_rate": {
+ "time": "2023-08-18T19:38:58",
+ "value": 114
+ },
+ "mews": {
+ "time": "2023-08-18T19:41:43",
+ "value": 2
+ },
+ "respiration_score": {
+ "time": "2023-08-18T19:35:24",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-18T19:36:14",
+ "value": 95
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": null,
+ "value_diastolic": null,
+ "value_systolic": null
+ },
+ "body_temp": {
+ "time": null,
+ "value": null
+ },
+ "heart_rate": {
+ "time": null,
+ "value": null
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": null,
+ "value": null
+ },
+ "spo2": {
+ "time": null,
+ "value": null
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-19T11:36:26",
+ "value_diastolic": 76,
+ "value_systolic": 121
+ },
+ "body_temp": {
+ "time": "2023-08-19T11:35:24",
+ "value": 37.5
+ },
+ "heart_rate": {
+ "time": "2023-08-19T11:36:26",
+ "value": 54
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-19T11:34:30",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-19T11:35:05",
+ "value": 98
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-19T13:24:56",
+ "value_diastolic": 73,
+ "value_systolic": 120
+ },
+ "body_temp": {
+ "time": "2023-08-19T13:24:02",
+ "value": 37.31
+ },
+ "heart_rate": {
+ "time": "2023-08-19T13:24:56",
+ "value": 106
+ },
+ "mews": {
+ "time": "2023-08-19T13:27:13",
+ "value": 1
+ },
+ "respiration_score": {
+ "time": "2023-08-19T13:21:46",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-19T13:23:43",
+ "value": 96
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": null,
+ "value_diastolic": null,
+ "value_systolic": null
+ },
+ "body_temp": {
+ "time": null,
+ "value": null
+ },
+ "heart_rate": {
+ "time": null,
+ "value": null
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": null,
+ "value": null
+ },
+ "spo2": {
+ "time": null,
+ "value": null
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": null,
+ "value_diastolic": null,
+ "value_systolic": null
+ },
+ "body_temp": {
+ "time": null,
+ "value": null
+ },
+ "heart_rate": {
+ "time": null,
+ "value": null
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": null,
+ "value": null
+ },
+ "spo2": {
+ "time": null,
+ "value": null
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": null,
+ "value_diastolic": null,
+ "value_systolic": null
+ },
+ "body_temp": {
+ "time": null,
+ "value": null
+ },
+ "heart_rate": {
+ "time": null,
+ "value": null
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": null,
+ "value": null
+ },
+ "spo2": {
+ "time": null,
+ "value": null
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": null,
+ "value_diastolic": null,
+ "value_systolic": null
+ },
+ "body_temp": {
+ "time": null,
+ "value": null
+ },
+ "heart_rate": {
+ "time": null,
+ "value": null
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": null,
+ "value": null
+ },
+ "spo2": {
+ "time": null,
+ "value": null
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-20T13:21:35",
+ "value_diastolic": 82,
+ "value_systolic": 119
+ },
+ "body_temp": {
+ "time": "2023-08-20T13:20:44",
+ "value": 37.29
+ },
+ "heart_rate": {
+ "time": "2023-08-20T13:21:35",
+ "value": 83
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-20T13:18:46",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-20T13:20:20",
+ "value": 98
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-20T16:13:04",
+ "value_diastolic": 79,
+ "value_systolic": 118
+ },
+ "body_temp": {
+ "time": "2023-08-20T16:12:05",
+ "value": 37.38
+ },
+ "heart_rate": {
+ "time": "2023-08-20T16:13:04",
+ "value": 95
+ },
+ "mews": {
+ "time": null,
+ "value": null
+ },
+ "respiration_score": {
+ "time": "2023-08-20T16:10:40",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-20T16:11:52",
+ "value": 97
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-20T19:06:47",
+ "value_diastolic": 82,
+ "value_systolic": 120
+ },
+ "body_temp": {
+ "time": "2023-08-20T19:05:37",
+ "value": 37.25
+ },
+ "heart_rate": {
+ "time": "2023-08-20T19:06:47",
+ "value": 93
+ },
+ "mews": {
+ "time": "2023-08-20T19:12:52",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-20T19:04:47",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-20T19:05:24",
+ "value": 97
+ }
+ },
+ {
+ "blood_pressure": {
+ "time": "2023-08-20T22:25:47",
+ "value_diastolic": 73,
+ "value_systolic": 115
+ },
+ "body_temp": {
+ "time": "2023-08-20T22:25:06",
+ "value": 37.45
+ },
+ "heart_rate": {
+ "time": "2023-08-20T22:25:47",
+ "value": 83
+ },
+ "mews": {
+ "time": "2023-08-20T22:29:09",
+ "value": 0
+ },
+ "respiration_score": {
+ "time": "2023-08-20T22:23:27",
+ "value": 0
+ },
+ "spo2": {
+ "time": "2023-08-20T22:24:51",
+ "value": 97
+ }
+ }
+]
diff --git a/docs/misc/trial-data.ods b/docs/misc/trial-data.ods
new file mode 100644
index 0000000..4cc467b
Binary files /dev/null and b/docs/misc/trial-data.ods differ
diff --git a/docs/thesis/attachments/trial-data.pdf b/docs/thesis/attachments/trial-data.pdf
new file mode 100644
index 0000000..8d4d97d
Binary files /dev/null and b/docs/thesis/attachments/trial-data.pdf differ
diff --git a/docs/thesis/figures/chart-connection-boxplot.png b/docs/thesis/figures/chart-connection-boxplot.png
new file mode 100644
index 0000000..9c0ab97
Binary files /dev/null and b/docs/thesis/figures/chart-connection-boxplot.png differ
diff --git a/docs/thesis/figures/chart-measurement-repeats.png b/docs/thesis/figures/chart-measurement-repeats.png
new file mode 100644
index 0000000..64660dc
Binary files /dev/null and b/docs/thesis/figures/chart-measurement-repeats.png differ
diff --git a/docs/thesis/figures/chart-measurement-stats.png b/docs/thesis/figures/chart-measurement-stats.png
new file mode 100644
index 0000000..8cf6984
Binary files /dev/null and b/docs/thesis/figures/chart-measurement-stats.png differ
diff --git a/docs/thesis/glossary.tex b/docs/thesis/glossary.tex
index 334373b..91ac0bb 100644
--- a/docs/thesis/glossary.tex
+++ b/docs/thesis/glossary.tex
@@ -34,3 +34,30 @@
The blood oxygen saturation represents the proportion of hemoglobin molecules in the bloodstream that are saturated with oxygen\cite{hafen_oxygen_2023}.
}
}
+\newglossaryentry{uplink-datarate}{
+ name={uplink datarate},
+ description={
+ The speed at which data is transmitted from a client device, such as a computer or smartphone, to a server or central network.
+ Typically measured in Mbps (megabits per second), it represents the efficiency of data sending capabilities of a network connection.
+ }
+}
+\newglossaryentry{downlink-datarate}{
+ name={downlink datarate},
+ description={
+ The rate at which data is received by a client device from a central server or network.
+ Expressed often in Mbps, it reflects the downloading or data reception efficiency of a network connection.
+ }
+}
+\newglossaryentry{rtt}{
+ type=\acronymtype,
+ name={RTT},
+ description={\Gls{rtt_full}},
+ first={\Gls{rtt_full} (RTT)}
+}
+\newglossaryentry{rtt_full}{
+ name={Round trip time},
+ description={
+ The time taken for a data packet to travel from a source to a destination and back again.
+ It provides an indication of the latency or delay inherent in a network connection and is usually measured in milliseconds (ms).
+ }
+}
diff --git a/docs/thesis/thesis.tex b/docs/thesis/thesis.tex
index 8d3613a..1481324 100644
--- a/docs/thesis/thesis.tex
+++ b/docs/thesis/thesis.tex
@@ -14,11 +14,15 @@
\usepackage{titlesec}
\usepackage{fancyhdr}
-\usepackage{color}
+\usepackage{booktabs} % thick horizontal lines
+\usepackage{multirow}
+\usepackage{array,tabularx}
+\usepackage[bf]{caption} % custom table captions
+\usepackage[table]{xcolor}
\usepackage[colorlinks]{hyperref}
-\usepackage{tcolorbox}
-\tcbuselibrary{most}
+\usepackage[most]{tcolorbox}
\pagestyle{plain}
+\usepackage{pdfpages} % PDFs in appendix
\usepackage[toc,nopostdot,nonumberlist,style=altlist,acronym]{glossaries}
% Page margins
@@ -84,17 +88,16 @@
% A command which generates a TODO message
\newcommand{\todo}[1]{{\fontfamily{lmtt}\selectfont{\color{orange}\small\underline{TODO:}} \textbf{#1}\normalsize \\}}
+
\input{./glossary.tex}
-\renewcommand{\thepage}{\Roman{page}}
\begin{document}
-%{\fontfamily{phv}\selectfont}
-\pagenumbering{Roman}
\input{cover.tex}
+\pagenumbering{Roman}
\section*{Summary}
\addcontentsline{toc}{section}{Summary}
@@ -253,7 +256,7 @@ process in Section~\ref{sec:modules}.
The centralized server components, including the Gotify server, a task scheduler used to schedule sending notifications and the Medwings application code itself are deployed
on a publicly accessible web server using a Docker container environment.
-\subsection{Design Challenges}
+\subsection{Design Challenges}\label{sec:design-challenges}
Since managing a user in Medwings requires the respective user's state to be mirrored by two other services, Withings and Gotify, keeping user accounts across
all three services in sync presents a challenge.
@@ -286,27 +289,152 @@ error message, prompting them to repeat the measurements.
\newpage
-\section{System Interaction and Usability}
-\begin{itemize}
- \item Personal experiences in interacting with the system and the medical devices
- \item What went well and what didn't? Why?
- \item Usability for potential medical staff, if applicable
- \item Strengths and weaknesses of patients taking their own measurements vs. having a medical professional take them
- \item How well did patients (or you, in this case) adhere to the measurement schedule? What factors influenced this?
-\end{itemize}
+\section{Methodology}
+Following the development and deployment of the application, Medwings underwent a performance and usability study.
+A test subject, impersonating a patient, used the application daily over the course of one week.
+Each day, five notifications were dispatched every three hours, starting at 10 AM.
+When prompted by a notification, the subject was asked to take vital sign readings using the three Withings medical sensors,
+followed by a MEWS calculation carried out by Medwings.
+
+The values of the vitals and MEWS records, as well as the time of measurement, were recorded by the application.
+For each MEWS calculation, the test subject manually kept track of which type of environment the system was used in.
+A distinction was made between the following environments:
+\begin{itemize}
+ \item At home: The subject was located at home, and their phone had access to a low latency broadband internet connection
+ \item On the go: The subject was away from home, and their phone had access to a high latency mobile internet connection
+\end{itemize}
+Preceeding each MEWS measurement, the maximum data transmission rates, both uplink and downlink, as well as the average connection round trip time
+from the subject's phone to a distant reference server was measured.
+The location of the reference server was kept constant throughout the trial.
+
+The occurrence of some measurement failures was anticipated, and manually recorded.
+Measurement failures were categorized into eight distinct classes, as listed in Table~\ref{tab:measurement-failures}.
+
+\begin{table}[!ht]
+ \centering
+ \begin{tcolorbox}[
+ enhanced, width=0.95\linewidth, boxrule=2pt, arc=4pt,
+ tabularx={>{\centering\arraybackslash}c|>{\centering\arraybackslash}c|>{\arraybackslash}X}
+ ]
+ \textbf{Device} & \textbf{Failure Class} & \textbf{Description} \\
+ \specialrule{2pt}{0em}{0em}
+ \multirow{2}{*}{Scanwatch} & $S_1$ & Device aborted measurement \\
+ \cline{2-3}
+ & $S_2$ & Measurement synchronization failure \\
+ \hline
+ \multirow{2}{*}{BPM Core} & $B_1$ & Device aborted measurement \\
+ \cline{2-3}
+ & $B_2$ & Measurement synchronization failure \\
+ \hline
+ \multirow{2}{*}{Thermo} & $T_1$ & Device aborted measurement \\
+ \cline{2-3}
+ & $T_2$ & Measurement synchronization failure \\
+ \hline
+ \multirow{2}{*}{---} & $P_1$ & Patient did not take any measurements \\
+ \cline{2-3}
+ & $P_2$ & MEWS calculation timed out \\
+ \end{tcolorbox}
+ \caption{\label{tab:measurement-failures}Classification of measurement failures during the usability trial}
+\end{table}
+
+The Scanwatch and BPM Core are equipped with accellerometers\cite{noauthor_worlds_nodate, noauthor_bpm_nodate}.
+If erratic movement is detected, the devices abort the measurement to avoid misinterpretation of sensor readings.
+Similarly, upon failure to process captured sensor data into a plausible result, a measurement may be aborted by the device\cite{noauthor_scanwatch_nodate, noauthor_bpm_nodate-1, noauthor_guides_nodate}.
+The measurement failure classes $S_1$, $B_1$ and $T_1$ were used to record these kinds of failure for each respective device.
+Following an $S_1$, $B_1$ or $T_1$ failure, the subject repeatedly carried out measurements unsing the affected device until a valid reading could be obtained.
+Subsequent failures were also recorded.
+
+As explained in Section~\ref{sec:design-challenges}, following a successful reading, a device may fail to push the measurement data to the Withings
+Cloud within the ten minute validity range for a MEWS calculation imposed by Medwings.
+Depending on which device failed to synchronize its data within the allowed time, a $S_2$, $B_2$ or $T_2$ failure was recorded.
+
+If the subject did not carry out any vitals measurements despite being prompted by a notification, a $P_1$ failure was noted.
+Finally, if the patient failed to carry out all three required measurements within the ten minute time limit, a $P_2$ failure was recorded.
+
+Following $S_2$, $B_2$, $T_2$, $P_1$ and $P_2$ failures, the measurement process was not repeated until the next notification.
\newpage
\section{Data Presentation and Analysis}
-\begin{itemize}
- \item Present your measured vitals data in an organized and visual manner (tables, graphs, etc.)
- \item Analyze the vitals and MEWS data: trends, anomalies, etc.
- \item Discuss the regularity of measurements and the system’s effectiveness in prompting and collecting data
-\end{itemize}
+
+The trial period encompassed seven days, on each of which five notifications were dispatched to the patient.
+Thus, an overall of $N=35$ system interactions were recorded.
+The patient was at home during $26$ of these, and on the go in all other cases.
+
+Seven $P_1$ measurement failures occurred, wherein the patient did not react to a received notification by taking measurements.
+Out of these, five occurred while the patient was on the go and, notably, four $P_1$ failures were consequtive, stemming from a period of 24 hours
+during which the patient did not have access to the smart devices.
+No $P_2$ measurement failures occured during the trial.
+
+In total, vitals were measured using all three devices in $28$ cases.
+However, in $11$ cases, at least one device failed to synchronize its readings within the ten minute timeout.
+Throughout the trial, $17$ MEWS calculations were recorded successfully.
+Figure~\ref{fig:measurement-stats} visualizes the overall measurement and failure counts.
+
+\begin{figure}[!ht]
+ \begin{center}
+ \includegraphics[width=\textwidth]{./figures/chart-measurement-stats.png}
+ \caption{\label{fig:measurement-stats}Measurement and measurement failure statistics at home and on the go.}
+ \end{center}
+\end{figure}
+
+Out of $84$ successful individual measurements across all devices throughout the trial, $18\%$ took longer than premitted by Medwings to synchronize with the Withings Cloud.
+Particularly while on the go, synchronization was prone to taking too long: $25\%$ of measurements resulted in synchronization failure, compared
+to $11\%$ at home.
+Especially the BPM Core and Thermo devices suffered from slow synchronization times: in a total of $15$ synchronization timeouts, $n_{B_2} = 7$ were caused
+by the blood pressure meter, and $n_{T_2} = 7$ by the thermometer.
+
+Three causing factors were identified: firstly, while the Scanwatch is constantly connected to the patient's phone, the BPM Core and Thermo devices only establish
+a connection intermittently.
+Presumably, measurement data updates from the smart device to the phone are sent less frequently.
+
+The second factor becomes apparent when examining the likelihood of each device aborting a measurement due to inconclusive sensor data,
+as displayed in Figure~\ref{fig:measurement-repeats}:
+for the BPM Core, $15\%$ of attempted measurements had to be repeated ($n_{B_1} = 5$).
+For the Scanwatch, over $34\%$ of readings ($n_{S_1} = 15$) were inconclusive and had to be repeated.
+The Withings Thermo did not abort any measurements ($n_{T_1} = 0$).
+Although aborted measurements did not cause synchronization failures directly, the time taken to repeat measurements did impact
+the likelihood of the MEWS calculation timing out before all vitals data was synchronized.
+
+\begin{figure}[!ht]
+ \begin{center}
+ \includegraphics[width=0.8\textwidth]{./figures/chart-measurement-repeats.png}
+ \caption{\label{fig:measurement-repeats}Number of measurement attempts and aborted measurements for each smart device.}
+ \end{center}
+\end{figure}
+
+Figure~\ref{fig:connection-boxplot} illustrates the comparative boxplots for the \gls{downlink-datarate}, \gls{uplink-datarate}, and \Gls{rtt} connection metrics when
+the patient was at home versus on the go.
+While there are evident differences in the distributions of these metrics between the two environments, the points representing synchronization failures do not
+predominantly cluster around areas of low datarate or high \Gls{rtt}.
+
+\begin{figure}[!ht]
+ \begin{center}
+ \includegraphics[width=\textwidth]{./figures/chart-connection-boxplot.png}
+ \caption{\label{fig:connection-boxplot}Connection quality and synchronization failures.}
+ \end{center}
+\end{figure}
+
+A reaction delay $t_r$ existed from when a notification was dispatched until the subject visited the Medwings website to take measurements.
+The average ($\overline{t_{r,\text{home}}} = 36\text{ min}$) and median ($M_{t_{r,\text{home}}} = 33\text{ min}$) delay was significantly lower
+while the patient was at home, compared to when they were out of the house ($\overline{t_{r,\text{on the go}}} = 68\text{ min}$, $M_{t_{r,\text{on the go}}} = 70\text{ min}$).
+The average time taken to carry out all three measurements was $4.5$ minutes in both enviroments.
+
+In all cases where vitals measurements were taken using the devices, the vitals data was captured and stored by Medwings.
+All MEWS calculations carried out by the application returned the correct value as expected from the vitals data they were based upon.
+The subject's vital signs were within their normal ranges representative of the patient's age for all measurements, with the exception of
+two outliers where a slightly increased heart rate was measured.
+Both outliers were detected correctly by Medwings.
+The complete trial data can be seen in Appendix~\ref{apdx:trial-data}.
\newpage
\section{Evaluation and Validation}
+% - Personal experiences in interacting with the system and the medical devices
+% - What went well and what didn't? Why?
+% - Usability for potential medical staff, if applicable
+% - Strengths and weaknesses of patients taking their own measurements vs. having a medical professional take them
+% - How well did patients (or you, in this case) adhere to the measurement schedule? What factors influenced this?
\begin{itemize}
\item Describe the methods used to evaluate the system
\item Discuss the validity of the study, especially considering that the test patient was yourself
@@ -314,6 +442,9 @@ error message, prompting them to repeat the measurements.
\item Are the results likely to generalize? Why or why not?
\end{itemize}
+% The absence of a clear correlation suggests that synchronization failures were not majorly influenced by the fundamental connection quality metrics.
+% The root cause of these failures likely lies elsewhere and is not solely dictated by raw connection performance.
+
\newpage
\section{Lessons Learned and Reflections}
@@ -352,14 +483,20 @@ error message, prompting them to repeat the measurements.
\newpage
\listoffigures
+\addcontentsline{toc}{section}{List of figures}
\newpage
\listoftables
+\addcontentsline{toc}{section}{List of tables}
\newpage
\printbibliography
+\addcontentsline{toc}{section}{References}
-% Appendix here
+\newpage
+\appendix
+\includepdf[pages=1, scale=0.95, pagecommand={\vspace*{-2.75cm}\section{Trial Data}\label{apdx:trial-data}}, angle=-90]{./attachments/trial-data.pdf}
+\includepdf[pages=2-, scale=0.95, pagecommand={}, angle=-90]{./attachments/trial-data.pdf}
\newpage
\section*{Ehrenwörtliche Erklärung}