calculateLengthOfFHREpisodes method
Episodes of low and high FHR variation (calculated in minutes and bpm)
- discard all deaccelerations + more than 50% signal loss
- for each minute calculate minuteRange = (max-baseline)+(baseline-min) a. if no max or min, use baseline value as it is
- meanMinuteRange = average of consecutive minuteRanges
- Episode of low FHR variation a. mean of atleast 5-6 minuteRanges < 30 ms
- Episode of high FHR variation a. mean of atleast 5-6 minuteRanges > 32 ms b. use gestetional age and percentile range to confirm (not implemented yet)
TODO: How to consider parallel scanning of signal for deaccelerations and processing? Are we loosing some part of signal here?
Implementation
void calculateLengthOfFHREpisodes(List<int?> beats, List<int?>? baselineFHRDR) {
try {
int size = beats.length;
List<double?> maxVariationsPerMinute =
List.filled((size / NO_OF_SAMPLES_PER_MINUTE).truncate(), null, growable: false);
double maxDiffAboveBaseline = 0, maxDiffBelowBaseline = 0;
int counter1 = 0, counter2 = 0;
List<int?> beatsBpm = List.filled(size, null, growable: false);
for (int j = 0; j < size; j++) {
if (beats[j] != 0) {
beatsBpm[j] = (SIXTY_THOUSAND_MS / beats[j]!).truncate();
} else {
beatsBpm[j] = 0;
}
}
/** processing only parts of signal which contain no deacceleration **/
for (int i = 0; i < size / NO_OF_SAMPLES_PER_MINUTE; i++) {
for (int j = 0; j < NO_OF_SAMPLES_PER_MINUTE; j++) {
/** first criteria **/
if (baselineFHRDR![i * NO_OF_SAMPLES_PER_MINUTE + j]! -
beatsBpm[i * NO_OF_SAMPLES_PER_MINUTE + j]! >=
5) {
counter1++;
if (counter1 == 16) {
// 60 seconds = 16 samples
// deacceleration is detected here
for (int k = 0; k < counter1; k++) {
if (maxDiffAboveBaseline <
baselineFHRDR[i * NO_OF_SAMPLES_PER_MINUTE + j - k]! -
beats[i * NO_OF_SAMPLES_PER_MINUTE + j - k]!) {
maxDiffAboveBaseline =
(baselineFHRDR[i * NO_OF_SAMPLES_PER_MINUTE + j - k]! -
beats[i * NO_OF_SAMPLES_PER_MINUTE + j - k]!)
.toDouble();
}
if (maxDiffBelowBaseline <
beats[i * NO_OF_SAMPLES_PER_MINUTE + j - k]! -
baselineFHRDR[i * NO_OF_SAMPLES_PER_MINUTE + j - k]!) {
maxDiffBelowBaseline = (beats[
i * NO_OF_SAMPLES_PER_MINUTE + j - k]! -
baselineFHRDR[i * NO_OF_SAMPLES_PER_MINUTE + j - k]!)
.toDouble();
}
}
}
} else {
counter1 = 0;
if (maxDiffAboveBaseline <
baselineFHRDR[i * NO_OF_SAMPLES_PER_MINUTE + j]! -
beats[i * NO_OF_SAMPLES_PER_MINUTE + j]!) {
maxDiffAboveBaseline =
(baselineFHRDR[i * NO_OF_SAMPLES_PER_MINUTE + j]! -
beats[i * NO_OF_SAMPLES_PER_MINUTE + j]!)
.toDouble();
}
if (maxDiffBelowBaseline <
beats[i * NO_OF_SAMPLES_PER_MINUTE + j]! -
baselineFHRDR[i * NO_OF_SAMPLES_PER_MINUTE + j]!) {
maxDiffBelowBaseline = (beats[i * NO_OF_SAMPLES_PER_MINUTE + j]! -
baselineFHRDR[i * NO_OF_SAMPLES_PER_MINUTE + j]!)
.toDouble();
}
}
/** second criteria **/
if (baselineFHRDR[i * NO_OF_SAMPLES_PER_MINUTE + j]! -
beatsBpm[i * NO_OF_SAMPLES_PER_MINUTE + j]! >=
10) {
counter2++;
if (counter2 == 8) {
// 30 seconds = 8 samples
// deacceleration is occuring here
for (int k = 0; k < counter2; k++) {
if (maxDiffAboveBaseline <
baselineFHRDR[i * NO_OF_SAMPLES_PER_MINUTE + j - k]! -
beats[i * NO_OF_SAMPLES_PER_MINUTE + j - k]!) {
maxDiffAboveBaseline =
(baselineFHRDR[i * NO_OF_SAMPLES_PER_MINUTE + j - k]! -
beats[i * NO_OF_SAMPLES_PER_MINUTE + j - k]!)
.toDouble();
}
if (maxDiffBelowBaseline <
beats[i * NO_OF_SAMPLES_PER_MINUTE + j - k]! -
baselineFHRDR[i * NO_OF_SAMPLES_PER_MINUTE + j - k]!) {
maxDiffBelowBaseline = (beats[
i * NO_OF_SAMPLES_PER_MINUTE + j - k]! -
baselineFHRDR[i * NO_OF_SAMPLES_PER_MINUTE + j - k]!)
.toDouble();
}
}
}
} else {
counter2 = 0;
if (maxDiffAboveBaseline <
baselineFHRDR[i * NO_OF_SAMPLES_PER_MINUTE + j]! -
beats[i * NO_OF_SAMPLES_PER_MINUTE + j]!) {
maxDiffAboveBaseline =
(baselineFHRDR[i * NO_OF_SAMPLES_PER_MINUTE + j]! -
beats[i * NO_OF_SAMPLES_PER_MINUTE + j]!)
.toDouble();
}
if (maxDiffBelowBaseline <
beats[i * NO_OF_SAMPLES_PER_MINUTE + j]! -
baselineFHRDR[i * NO_OF_SAMPLES_PER_MINUTE + j]!) {
maxDiffBelowBaseline = (beats[i * NO_OF_SAMPLES_PER_MINUTE + j]! -
baselineFHRDR[i * NO_OF_SAMPLES_PER_MINUTE + j]!)
.toDouble();
}
}
}
maxVariationsPerMinute[i] = maxDiffAboveBaseline + maxDiffBelowBaseline;
}
double meanMinuteInterval = 0,
sum = 0,
meanLowEpisodeBpm = 0,
meanHighEpisodeBpm = 0;
int highEpisodes = 0, lowEpisodes = 0, howManyLow = 0, howManyHigh = 0;
for (int i = 0; i < size / NO_OF_SAMPLES_PER_MINUTE - 6; i++) {
for (int j = i; j < i + 6; j++) {
sum += maxVariationsPerMinute[j]!;
}
meanMinuteInterval = sum / 6;
sum = 0;
for (int j = i; j < i + 6; j++) {
if ((meanMinuteInterval * 6 - maxVariationsPerMinute[j]!) / 5 <= 30) {
howManyLow++;
}
if ((meanMinuteInterval * 6 - maxVariationsPerMinute[j]!) / 5 >= 32) {
howManyHigh++;
}
}
if (howManyLow >= 5) {
lowEpisodes++;
meanLowEpisodeBpm += meanMinuteInterval;
}
if (howManyHigh >= 5) {
// ignoring correlation and distribution of original traces for now
highEpisodes++;
meanHighEpisodeBpm += meanMinuteInterval;
if (gestAge <= 25) {
// Do nothing
} else if (meanHighEpisodeBpm / highEpisodes <
highFHREpisodePercentiles[gestAge - 26][1]) {
// checking 3rd percentile criteria
highEpisodes--;
meanHighEpisodeBpm -= meanMinuteInterval;
}
}
howManyLow = howManyHigh = 0;
}
lengthOfHighFHREpisodes = highEpisodes.toDouble();
lengthOfLowFHREpisodes = lowEpisodes.toDouble();
if (meanHighEpisodeBpm != 0) {
highFHRVariationBpm =
(SIXTY_THOUSAND_MS / meanHighEpisodeBpm).truncate();
}
if (meanLowEpisodeBpm != 0) {
lowFHRVariationBpm = (SIXTY_THOUSAND_MS / meanLowEpisodeBpm).truncate();
}
} catch (Exception) {
//todo:
}
}