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 @@ + + + +no measurements taken (n=7)P1 failures (n=7)MEWS calculations (n=17)measurements taken (n=28)synchronization failures (n=11)home (n=26)on the go (n=9) 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}