calculateLengthOfFHREpisodes method

void calculateLengthOfFHREpisodes(
  1. List<int?> beats,
  2. List<int?>? baselineFHRDR
)

Episodes of low and high FHR variation (calculated in minutes and bpm)

  1. discard all deaccelerations + more than 50% signal loss
  2. for each minute calculate minuteRange = (max-baseline)+(baseline-min) a. if no max or min, use baseline value as it is
  3. meanMinuteRange = average of consecutive minuteRanges
  4. Episode of low FHR variation a. mean of atleast 5-6 minuteRanges < 30 ms
  5. 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:
  }
}