main
1import 'package:blood_pressure_app/logging.dart';
2
3import 'ble_date_time.dart';
4import 'ble_measurement_status.dart';
5import 'decoding_util.dart';
6
7/// https://developer.nordicsemi.com/nRF51_SDK/nRF51_SDK_v4.x.x/doc/html/structble__bps__meas__s.html
8/// https://github.com/NordicSemiconductor/Kotlin-BLE-Library/blob/6b565e59de21dfa53ef80ff8351ac4a4550e8d58/profile/src/main/java/no/nordicsemi/android/kotlin/ble/profile/bps/BloodPressureMeasurementParser.kt
9class BleMeasurementData with TypeLogger {
10 BleMeasurementData({
11 required this.systolic,
12 required this.diastolic,
13 required this.meanArterialPressure,
14 required this.isMMHG,
15 required this.pulse,
16 required this.userID,
17 required this.status,
18 required this.timestamp,
19 });
20
21 static BleMeasurementData? decode(List<int> data, int offset) {
22 // https://github.com/NordicSemiconductor/Kotlin-BLE-Library/blob/6b565e59de21dfa53ef80ff8351ac4a4550e8d58/profile/src/main/java/no/nordicsemi/android/kotlin/ble/profile/bps/BloodPressureMeasurementParser.kt
23
24 // Reading specific bits: `(byte & (1 << bitIdx))`
25
26 if (data.length < 7) {
27 log.finest('BleMeasurementData decodeMeasurement: Not enough data, $data has less than 7 bytes.');
28 return null;
29 }
30
31 int offset = 0;
32
33 final int flagsByte = data[offset];
34 offset += 1;
35
36 final bool isMMHG = !isBitIntByteSet(flagsByte, 0); // 0 => mmHg 1 =>kPA
37 final bool timestampPresent = isBitIntByteSet(flagsByte, 1);
38 final bool pulseRatePresent = isBitIntByteSet(flagsByte, 2);
39 final bool userIdPresent = isBitIntByteSet(flagsByte, 3);
40 final bool measurementStatusPresent = isBitIntByteSet(flagsByte, 4);
41
42 if (data.length < (7
43 + (timestampPresent ? 7 : 0)
44 + (pulseRatePresent ? 2 : 0)
45 + (userIdPresent ? 1 : 0)
46 + (measurementStatusPresent ? 2 : 0)
47 )) {
48 log.finest("BleMeasurementData decodeMeasurement: Flags don't match, $data has less bytes than expected.");
49 return null;
50 }
51
52 final double? systolic = readSFloat(data, offset);
53 offset += 2;
54 final double? diastolic = readSFloat(data, offset);
55 offset += 2;
56 final double? meanArterialPressure = readSFloat(data, offset);
57 offset += 2;
58
59 if (systolic == null || diastolic == null || meanArterialPressure == null) {
60 log.finest('BleMeasurementData decodeMeasurement: Unable to decode required values sys, dia, and meanArterialPressure, $data.');
61 return null;
62 }
63
64 DateTime? timestamp;
65 if (timestampPresent) {
66 timestamp = BleDateTimeParser.parseBle(data, offset);
67 offset += 7;
68 }
69
70 double? pulse;
71 if (pulseRatePresent) {
72 pulse = readSFloat(data, offset);
73 offset += 2;
74 }
75
76 int? userId;
77 if (userIdPresent) {
78 userId = data[offset];
79 offset += 1;
80 }
81
82 BleMeasurementStatus? status;
83 if (measurementStatusPresent) {
84 status = BleMeasurementStatus.decode(data[offset]);
85 }
86
87 return BleMeasurementData(
88 systolic: systolic,
89 diastolic: diastolic,
90 meanArterialPressure: meanArterialPressure,
91 isMMHG: isMMHG,
92 pulse: pulse,
93 userID: userId,
94 status: status,
95 timestamp: timestamp,
96 );
97 }
98
99 final double systolic;
100 final double diastolic;
101 final double meanArterialPressure;
102 final bool isMMHG; // mmhg or kpa
103 final double? pulse;
104 final int? userID;
105 final BleMeasurementStatus? status;
106 final DateTime? timestamp;
107
108 @override
109 String toString() => 'BleMeasurementData{systolic: $systolic, diastolic: $diastolic, meanArterialPressure: $meanArterialPressure, isMMHG: $isMMHG, pulse: $pulse, userID: $userID, status: $status, timestamp: $timestamp}';
110}