Breathing Data (how to determine & calculate inhale/exhale duration)

Hi everybody,

I’m currently working on a project in which I use a breath-belt (arduino+stretch sensor attached around the chest) to capture the breathing patterns of a user. The data is transferred and integrated into the project using the p5.serialport library. Within the project the data is used to control visualisations/small breathing games that are meant to guide and encourage the user to breath in a certain rhythm (ratio+duration). All this works fine.

What I’m struggling with at the moment is:

To be able to asses how well a user was able to follow along the given visual guidance with his breath, I would like to determine the average inhaltion/exhalation duration within a certain time frame.

I’ve already implemented this using an array of simplified data instead of the real-time breathing input (See code below)

However, when using the actual input data, that obviously isn’t as clean as the data used in the example, the inhalation/exhalation phases aren’t registered correctly providing me with wrong results.

I’m already working on improving the smoothing of the breathing data.

However I’m wondering if anybody might have a better idea on how to determine the different phases within the data?
Currently ,I’m only comparing the last value with the current value to assess whether the values are increasing or decreasing. Due to noise in the input data, false phase switches are being recorded…

Any help, on how to best approach this, is much appreciated :slightly_smiling_face:

const breathingData = [
  { breathVal: 17, timestamp: 22 },
  { breathVal: 18, timestamp: 23 },
  { breathVal: 19, timestamp: 24 },
  { breathVal: 20, timestamp: 25 },
  { breathVal: 21, timestamp: 26 },
  { breathVal: 22, timestamp: 27 },
  { breathVal: 21, timestamp: 28 },
  { breathVal: 20, timestamp: 29 },
  { breathVal: 19, timestamp: 30 },
  { breathVal: 18, timestamp: 31 },
  { breathVal: 16, timestamp: 32 },
  { breathVal: 17, timestamp: 33 },
  { breathVal: 18, timestamp: 34 },
  { breathVal: 19, timestamp: 35 },
  { breathVal: 20, timestamp: 36 },
  { breathVal: 19, timestamp: 37 },
  { breathVal: 18, timestamp: 38 },
  { breathVal: 17, timestamp: 39 },
]

function getBaselineData(startTime, endTime) {
  const baselineData = breathingData.filter(breathingDataPoint => breathingDataPoint.timestamp >= startTime && breathingDataPoint.timestamp <= endTime);

    // To check whether inhale or exhale phase
    let isInhaling = false;

    // To momentarily store the last switch timestamp
    // for later substraction with current switch timestamp
    let prevSwitchInToEx = 0;
    let prevSwitchExToIn = 0;

    // To store the current inhale/exhale time
    let inhaleTime = 0;
    let exhaleTime = 0;

    // To store all recorded inhale/exhale times
    let allInhaleTimes = [];
    let allExhaleTimes = [];

    // To save overall average inhale/exhale time
    let sumInhaleTimes = 0;
    let sumExhaleTimes = 0;
    let avgInhaleTime = 0;
    let avgExhaleTime = 0;

    for (i = 1; i < baselineData.length; i++) {
        const currBreath = baselineData[i];
        const prevBreath = baselineData[i - 1];
        const wasInhaling = isInhaling;

        // Check if exhale or inhale phase
        if (currBreath.breathVal > prevBreath.breathVal) {
            isInhaling = true;
        } else {
            isInhaling = false;
        }

        // Check if switch in breathing phase (inhale <--> exhale)
        if (isInhaling !== wasInhaling) {
          
            // inhalation
            if (wasInhaling === false) {
              
                // Save switch timestamp (exhale --> inhale) for calculation of next exhale phase 
                prevSwitchExToIn = currBreath.timestamp;
              
                // Calculate inhale time and convert milliseconds to seconds
                inhaleTime = (currBreath.timestamp - prevSwitchInToEx) / 1000;
              
                // Store inhaleTime in array 
                allInhaleTimes.push(inhaleTime);
              
            // exhalation
            } else {
              
                // Save switch timestamp (inhale --> exhale) for calculation of next inhale phase 
                prevSwitchInToEx = currBreath.timestamp;
              
                // Calculate exhale time and convert milliseconds to seconds
                exhaleTime = (currBreath.timestamp - prevSwitchExToIn) / 1000;
              
                 // Store exhaleTime in array 
                allExhaleTimes.push(exhaleTime);
              
            }
        }
    }

    // Get sum of all inhale/exhale times
    for (let i = 0; i < allInhaleTimes.length; i++) {
        sumInhaleTimes += allInhaleTimes[i];
    }
    for (let i = 0; i < allExhaleTimes.length; i++) {
        sumExhaleTimes += allExhaleTimes[i];
    }

    // Get average of all inhale/exhale times
    avgInhaleTime = sumInhaleTimes / allInhaleTimes.length;
    avgExhaleTime = sumExhaleTimes / allExhaleTimes.length;
    
    // Print results
    console.log("All Inhale Times: " + allInhaleTimes);
    console.log("All Exhale Times: " + allExhaleTimes);
    console.log("Sum Inhale Times: " + sumInhaleTimes);
    console.log("Sum Exhale Times: " + sumExhaleTimes);
    console.log("Average Inhale Time: " + avgInhaleTime);
    console.log("Average Exhale Time: " + avgExhaleTime);

    return baselineData;
}

console.log(getBaselineData(22, 39));

It seems like using the slope of a moving average instead of the slope of the raw data would help quite a bit. Check this out: p5.js Web Editor

Thanks for the great example! I already use a moving average to smooth the input data. However, the idea of using a threshold like you did in your example is pretty neat and might fix my problem. :+1: :+1:

I will try and see if I can include it that way into my code.

Could you maybe make a small sketch without the actual plotting of the data?
I’m having a bit of a hard time figuring out how to set the threshold, as with the actual breath input data there is not such a fixed range from 0-height as with mouseY.