diff --git a/breath_plot.html b/breath_plot.html index fb7eda9e7213c8a9312a78a05235ab4e97f62a9c..2c052288274c44fb6a3efe93912ddb9c3c64dadf 100644 --- a/breath_plot.html +++ b/breath_plot.html @@ -325,7 +325,7 @@ an interactive or static analysis of a respiration. It's primary purpose is </div> </div> <div> - <label for="taip">TAIP (ms): </label> + <label for="taip">Rise Time (ms): </label> <div class="value_and_fences"> <div class="calcnum"> <label id="taip"> </label> @@ -343,7 +343,7 @@ an interactive or static analysis of a respiration. It's primary purpose is </div> </div> <div> - <label for="taip">TRIP (ms): </label> + <label for="taip">Fall Time (ms): </label> <div class="value_and_fences"> <div class="calcnum"> <label id="trip"> </label> @@ -359,6 +359,23 @@ an interactive or static analysis of a respiration. It's primary purpose is </div> </div> </div> + <div> + <label for="wob">W. of B. (J/L): </label> + <div class="value_and_fences"> + <div class="calcnum"> + <label id="wob"> </label> + </div> + <div class="vertical_alarms"> + <div class="limit max"> + <label for="wob_h">H:</label> + <input class="fence" id="wob_h" type='text'> </input> + </div> + <div class="limit min"> + <label for="wob_l">L:</label> + <input class="fence" id="wob_l" type='text'> </input> + </div> + </div> + </div> </div> </div> </div> @@ -370,11 +387,11 @@ an interactive or static analysis of a respiration. It's primary purpose is <div> Parameters: <div> - <label for="tarip_h">TAIP/TRIP High Pressure (cm H2O):</label> + <label for="tarip_h">Rise/Fall High Pressure (cm H2O):</label> <input class="fence" id="tarip_h" type='text'> </input> </div> <div> - <label for="tarip_l">TAIP/TRIP Low Pressure (cm H2O):</label> + <label for="tarip_l">Rise/Fall Low Pressure (cm H2O):</label> <input class="fence" id="tarip_l" type='text'> </input> </div> </div> @@ -669,10 +686,30 @@ function plot(samples, trans, breaths) { ,0); var max_v = Math.max(max_inh,max_exh); + // not sure why I have null work, have to understand. + var max_work_per_liter = 0; + var work_per_liter = []; + for(var i = 0; i < breaths.length; i++) { + var b = breaths[i]; + if (b.work != null) { + var wpl = b.work / b.vol_i; + if (wpl > max_work_per_liter) + max_work_per_liter = wpl; + var center = ((trans[b.trans_begin_inhale].ms + trans[b.trans_cross_zero].ms) - 2*min) / (2.0 * 1000.0); + work_per_liter.push({ wpl: wpl, center: center }); + } + } + var exh_v = unpack(breaths, 'vol_e').map(e => 100 * e / max_v); var t_exh_v = unpack(breaths, 'vol_e').map(e => Math.round(e*1000.0)+"ml exh"); var inh_v = unpack(breaths, 'vol_i').map(i => 100 * i / max_v); var t_inh_v = unpack(breaths, 'vol_i').map(e => Math.round(e*1000.0)+"ml inh"); + + + var work_v = work_per_liter.map(w => (1/3) * (100 * w.wpl / max_work_per_liter)); + var t_work = work_per_liter.map(w => w.wpl.toFixed(2)+"J/L"); + var work_centers = unpack(work_per_liter, 'center'); + // now to graph properly, I must find the center of an inhalation. // I have packed these into the breaths... var inhale_centers = breaths.map(b => ((trans[b.trans_begin_inhale].ms + trans[b.trans_cross_zero].ms) - 2*min) / (2.0 * 1000.0)); @@ -700,6 +737,17 @@ function plot(samples, trans, breaths) { text: t_exh_v, }; + var workPlot = {type: "scatter", mode: "markers+text", + name: "WoB J", + textposition: 'bottom center', + x: work_centers, + y: work_v, + text: t_work, + marker: { size: 8, + color: 'blue', + symbol: 'triangle-right'} + }; + event_graph.push(workPlot); event_graph.push(inhPlot); event_graph.push(exhPlot); } @@ -738,22 +786,6 @@ function plot(samples, trans, breaths) { }; return plot; - } - function gen_clock_graph(selected,name,color, textposition) { - var millis = unpack(selected, 'ms'); - var zeroed = millis.map(m =>(m-min)/1000.0); - var vs = selected.map( s => -30) - var vs_t = selected.map(v => v.buff); - var plot = {type: "scatter", mode: "markers+text", - name: name, - textposition: textposition, - x: zeroed, - y: vs, - text: vs_t, - marker: { size: 10, color: color } - }; - return plot; - } function gen_message_events(samples,name,color, textposition) { var messages = samples.filter(s => s.event == 'E' && s.type == 'M'); @@ -763,7 +795,7 @@ function plot(samples, trans, breaths) { // Basically, we will show only the first of these in a "run" // This is rather difficult; we will instead just use 200 ms // as a window. - const time_window_ms = 200; + const TIME_WINDOW_MS = 400; var lows = []; var highs = []; @@ -771,10 +803,10 @@ function plot(samples, trans, breaths) { for(var i = 0; i < messages.length; i++) { var m = messages[i]; if (m.buff == FLOW_TOO_LOW) { - if ((lows.length == 0) || ((lows.length > 0) && (lows[lows.length-1].ms < (m.ms - time_window_ms)))) + if ((lows.length == 0) || ((lows.length > 0) && (lows[lows.length-1].ms < (m.ms - TIME_WINDOW_MS)))) lows.push(m); } else if (m.buff == FLOW_TOO_HIGH) { - if ((highs.length == 0) || ((highs.length > 0) && (highs[highs.length-1].ms < (m.ms - time_window_ms)))) + if ((highs.length == 0) || ((highs.length > 0) && (highs[highs.length-1].ms < (m.ms - TIME_WINDOW_MS)))) highs.push(m); } else { others.push(m); @@ -797,20 +829,6 @@ function plot(samples, trans, breaths) { (v => v.buff)); return [lowsPlot,highsPlot,othersPlot]; } - function gen_clock_events(samples,name,color, textposition) { - var messages = samples.filter(s => s.event == 'E' && s.type == 'C'); - const time_window_ms = 200; - - var clocks = []; - for(var i = 0; i < messages.length; i++) { - var m = messages[i]; - if ((clocks.length == 0) || ((clocks.length > 0) && (clocks[clocks.length-1].ms < (m.ms - time_window_ms)))) - clocks.push(m); - } - - var clocksPlot = gen_clock_graph(clocks,name,color,textposition); - return clocksPlot; - } { var fio2AirwayPlot = gen_graph_measurement_timed(samples, "FiO2 (%)", @@ -865,12 +883,6 @@ function plot(samples, trans, breaths) { event_graph.push(o); } - { - var cs = gen_clock_events(samples,"Wall Clock","black",'top center'); - - event_graph.push(cs); - } - // The BME680 sensor detects VOC gases. I don't think has any clinical value @@ -987,7 +999,13 @@ function compute_fio2_mean(secs,samples) { } } - +// returns: +// [ respiration rate, +// tidal volume (avg), +// minute_volume +// EIRatio, +// Work-of-Breathing (avg) +// ] function compute_respiration_rate(secs,samples,transitions,breaths) { // In order to compute the number of breaths // in the last s seconds, I compute those breaths @@ -998,7 +1016,7 @@ function compute_respiration_rate(secs,samples,transitions,breaths) { var first_inhale_ms = -1; var last_inhale_ms = -1; if (breaths.length == 0) { - return [0,0,0,"NA"]; + return [0,0,0,"NA","NA"]; } else { const recent_ms = samples[samples.length - 1].ms; var cur_ms = recent_ms; @@ -1013,6 +1031,9 @@ function compute_respiration_rate(secs,samples,transitions,breaths) { // divide by the inhlation_duration. var vol_ci = 0.0; + var wob = 0.0; + var wob_cnt = 0; + while((i >=0) && (breaths[i].ms > (cur_ms - secs*1000))) { cnt++; vol_i += breaths[i].vol_i; @@ -1029,6 +1050,11 @@ function compute_respiration_rate(secs,samples,transitions,breaths) { const zero_ms = transitions[breaths[i].trans_cross_zero].ms; time_inh += (zero_ms - inh_ms); time_exh += (exh_ms - zero_ms); + + if (breaths[i].work != null) { + wob += breaths[i].work / breaths[i].vol_i; + wob_cnt++; + } i--; } if ((cnt > 1) && (first_inhale_ms != last_inhale_ms)) { @@ -1044,16 +1070,18 @@ function compute_respiration_rate(secs,samples,transitions,breaths) { var tidal_volume = 1000.0 * vol_i / cnt; var EIratio = (time_inh == 0) ? null : time_exh / time_inh; + var WorkOfBreathing_J_per_L = wob / wob_cnt; return [ rr, tidal_volume, minute_volume, - EIratio]; + EIratio, + WorkOfBreathing_J_per_L]; } else { - return [0,0,0,"NA"]; + return [0,0,0,"NA","NA"]; } } - } +} var LIMITS = { max: { h: 40, @@ -1075,7 +1103,9 @@ var LIMITS = { taip: { h: 100, // These are in ms l: 0}, trip: { h: 100, // These are in ms - l: 0}, + l: 0}, + wob: { h: 3, + l: 0.2}, } function load_ui_with_defaults(limits) { @@ -1121,13 +1151,19 @@ function process(samples) { var [transitions,breaths] = computeMovingWindowTrace(samples,t,v); plot(samples,transitions,breaths); // How many seconds backwards should we look? Perhaps 20? - var [bpm,tv,mv,EIratio,fio2] = compute_respiration_rate(RESPIRATION_RATE_WINDOW_SECONDS,samples,transitions,breaths); + var [bpm,tv,mv,EIratio,wob] = compute_respiration_rate(RESPIRATION_RATE_WINDOW_SECONDS,samples,transitions,breaths); var alarms = []; $("#bpm").text(bpm.toFixed(1)); $("#tv").text(tv.toFixed(0)); - $("#mv").text((mv).toFixed(2)); + $("#mv").text(mv.toFixed(2)); + + if (wob == "NA") { + $("#wob").text("NA"); + } else { + $("#wob").text(wob.toFixed(2)); + } if (EIratio == "NA") { $("#ier").text("NA"); } else { @@ -1150,8 +1186,10 @@ function process(samples) { alarms = alarms.concat(check_high_and_low(LIMITS,"bpm",bpm.toFixed(1),b_ms)); alarms = alarms.concat(check_high_and_low(LIMITS,"tv",tv.toFixed(0),b_ms)); - alarms = alarms.concat(check_high_and_low(LIMITS,"mv",(mv).toFixed(2),b_ms)); + alarms = alarms.concat(check_high_and_low(LIMITS,"mv",mv.toFixed(2),b_ms)); alarms = alarms.concat(check_high_and_low(LIMITS,"ier",(1.0 / EIratio).toFixed(1),b_ms)); + if (wob != "NA") + alarms = alarms.concat(check_high_and_low(LIMITS,"wob",wob.toFixed(2),b_ms)); // These must be moved out; in fact, // they must be made configurable! @@ -1236,26 +1274,34 @@ function retrieveAndPlot(){ // WARNING: This is a hack...if the timestamp is negative, // we treat it as a limited (beyond range of sensor) measurement. // Our goal is to warn about this, but for now we will just - // ignore and correct. + // ignore and correct. + if (cur_sam == null) { + console.log("No return"); + return; + } if (cur_sam && cur_sam.length == 0) { console.log("no samples; potential misconfiguration!"); } else { - cur_sam = sanitize_samples(cur_sam); - if (INITS_ONLY) { - samples = cur_sam; - INITS_ONLY = false; - } else { - var discard = Math.max(0, - samples.length + cur_sam.length - MAX_SAMPLES_TO_STORE_S); - samples = samples.slice(discard); - } - - - samples = samples.concat(cur_sam); - // We are not guaranteeed to get samples in order - // we sort them.... - samples = samples.sort((a,b) => a.ms - b.ms); - process(samples); + if (typeof(cur_sam) == "string") { + console.log(cur_sam); + } else { + cur_sam = sanitize_samples(cur_sam); + if (INITS_ONLY) { + samples = cur_sam; + INITS_ONLY = false; + } else { + var discard = Math.max(0, + samples.length + cur_sam.length - MAX_SAMPLES_TO_STORE_S); + samples = samples.slice(discard); + } + + + samples = samples.concat(cur_sam); + // We are not guaranteeed to get samples in order + // we sort them.... + samples = samples.sort((a,b) => a.ms - b.ms); + process(samples); + } } }, error: function(xhr, ajaxOptions, thrownError) { @@ -1457,49 +1503,198 @@ function compute_current_TRIP(TRIP_min,TRIP_max, samples) return TRIP_m; } } -// A simple computation of a moving window trace -// computing [A + -B], where A is volume to left -// of sample int time window t, and B is volume to right -// t is in milliseconds -function computeMovingWindowTrace(samples,t,v) { - var flows = samples.filter(s => s.event == 'M' && s.type == 'F'); - var first_time = flows[0].ms; - var last_time = flows[flows.length - 1].ms; - var duration = last_time - first_time; - // Here is an idea... - // We define you to be in one of three states: - // Inspiring, expiring, or neither. - // Every transition between these states is logged. - // Having two inspirations between an expiration is - // weird but could happen. - // We record transitions. - // When the time series crossed a fixed threshold - // or zero, it causes a transition. If you are inspiring, - // you have to cross zero to transition to neither, - // and you start expiring when you cross the treshold. - // This is measured in standard liters per minute. - const vm = 10; // previously used 4 + // A routine to calculate work per breath +function PressureVolumeWork(breath, transitions, samples) { + // -1 for quadilateral approximation + if (breath.vol_i == 0) { + return null; + } else { + var beginTransition = transitions[breath.trans_begin_inhale]; + var beginTime_ms = beginTransition.ms; + var endTransition = transitions[breath.trans_cross_zero]; + var endTime_ms = endTransition.ms; + var flows = samples.filter(s => s.event == 'M' && s.type == 'F' && + s.ms >= beginTime_ms && s.ms <= endTime_ms); + var pressures = samples.filter(s => s.event == 'M' && s.type == 'D' && + s.loc == 'A'&& s.ms >= beginTime_ms && s.ms <= endTime_ms); + + // Note: The algorithm below relies on the fact that there is + // only one flow or pressure with a single ms value; and that + // increvementing an index necessarily incremenst the .ms value. + + // Without two samples, we have no duration and can't define + // work. + if (pressures.length < 2 || flows.length < 2) return null; + + var ct = Math.min(flows[0].ms,pressures[0].ms); + var lfp = { val : flows[0].val, ms: flows[0].ms } ; // last flow point + var lpp = { val : pressures[0].val, ms: pressures[0].ms }; // last pressure_point + var fi = increment_past(flows,flows[0].ms,0); // Index of next flow sample + var pi = increment_past(pressures,pressures[0].ms,0); // Index of next pressure sample + var w = 0; // current work + + // compute flow at time ms give index and last point + // This is just a simple linear interpolation + function f(ms,cur,last) { + var ms0 = last.ms; + var ms1 = cur.ms; + return last.val + (cur.val - last.val)*(ms - ms0)/(ms1 - ms0); + } + function increment_past(array,ms,index) { + var begin = index; + while(index < array.length && array[index].ms <= ms) + index++; + if (index == begin) debugger; + if (index >= array.length) + return null; + else + return index; + } - // We will model this as a list of transitions. - // A breath is any number of inspirations followed by - // any number of expirations. (I+)(E+) + // A fundamental invariant: + // pressures[pi].ms > lpp.ms + // flows[pi].ms > lfp.ms + while ((fi + pi) < (flows.length + pressures.length)) { + // Invariant always increment fi or pi + // fi and pi point to unprocessed value + console.assert(pressures[pi].ms > lpp.ms); + console.assert(flows[fi].ms > lfp.ms); + var ms; + if (pressures[pi].ms <= flows[fi].ms) { // process pressure + ms = pressures[pi].ms; + pi = increment_past(pressures,ms,pi); + if (flows[fi].ms <= ms) { + fi = increment_past(flows,ms,fi); + } + } else { + ms = flows[fi].ms; + fi = increment_past(flows,ms,fi); + if (pressures[pi].ms <= ms) { + pi = increment_past(pressures,ms,pi); + } + } + if ((fi === null) || (pi === null)) + break; + var dur_s = (ms - ct) / 1000; + console.assert(pressures[pi].ms > lpp.ms); + console.assert(flows[fi].ms > lfp.ms); + var nf = f(ms,flows[fi],lfp); + var np = f(ms,pressures[pi],lpp); + var f1 = (lfp.val + nf)/2; + var p1 = (lpp.val + np)/2; + // convert 10ths of cm H2O to pascals.. + var p1_pa = (p1 * 98.0665) / 10; + + // convert flows in lpm to cubic meters per seconds + var f1_m_cubed_per_s = f1 / (1000 * 1000 * 60); + + // work is now in Joules! + w += dur_s * p1_pa * f1_m_cubed_per_s; + lfp = { val : f1, ms: ms }; + lpp = { val : p1, ms: ms }; + ct = ms; + } + return w; + } +} + +// Let's first set up a perfectly square 1-second +function generate_synthetic_trace() { + const SAMPLES_PER_PHASE = 1000; + const SAMPLES_MS_PER_SAMPLE = 1; + var trace = []; + var cur = 0; + // p is a probability of occuring. + function push_samples(start,num,d,f,pd,pf) { + for(var i = start; i < start+num; i++) { + if (Math.random() < pd) { + trace.push( + { + event: "M", + loc: "A", + ms: i, + num: 0, + type: "D", + val: d + }); + } + if (Math.random() < pf) { + trace.push( + { + event: "M", + loc: "A", + ms: i, + num: 0, + type: "F", + val: f + }); + } + } + } + const P1 = 1/2; + const P2 = 1/2; + push_samples(SAMPLES_PER_PHASE,SAMPLES_PER_PHASE,200,50000,P1,P2); + push_samples(0,SAMPLES_PER_PHASE,-200,-50000,P1,P2); + push_samples(SAMPLES_PER_PHASE,SAMPLES_PER_PHASE,200,50000,P1,P2); + push_samples(SAMPLES_PER_PHASE*2,SAMPLES_PER_PHASE,-200,-50000,P1,P2); + push_samples(SAMPLES_PER_PHASE*3,SAMPLES_PER_PHASE,200,50000,P1,P2); + return trace; +} + +function nearp(x,y,d) { + return Math.abs(x - y) <= d; +} + +function testWorkSynthetic(){ // breaths give us inspiration transition points + var samples = generate_synthetic_trace(); + + const JOULES_IN_BREATH = 1 * 1961.33 * 50000 / (60e+6); + var flows = samples.filter(s => s.event == 'M' && s.type == 'F'); + var first_time = flows[0].ms; + var last_time = flows[flows.length - 1].ms; + var duration = last_time - first_time; + console.log(flows); + + + const vm = 10; + // There is a problem here that this does not create a transition at the beginning. var transitions = compute_transitions(vm,flows); + var breaths = compute_breaths_based_without_negative_flow(transitions,flows); + console.log(breaths); + for(i = 0; i<breaths.length; i++) { + var w = PressureVolumeWork(breaths[i], transitions, samples); + console.assert((w == null) || (nearp(w,JOULES_IN_BREATH),0.1)); + console.log("final (Joules) = ",w); + } + return true; +} + + + function testWork(samples){ // breaths give us inspiration transition points + var flows = samples.filter(s => s.event == 'M' && s.type == 'F'); + var first_time = flows[0].ms; + var last_time = flows[flows.length - 1].ms; + var duration = last_time - first_time; + console.log(flows); + + const vm = 10; + var transitions = compute_transitions(vm,flows); + var breaths = compute_breaths_based_without_negative_flow(transitions,flows); + console.log(breaths); + for(i = 0; i<breaths.length; i++) { + var w = PressureVolumeWork(breaths[i], transitions, samples); + console.log(w); + } + } + - // Now that we have transitions, we can apply a - // diferrent algorithm to try to define "breaths". - // Because a breath is defined as an inspiration - // and then an expiration, we will define a breath - // as from the first inspiration, until there has - // been one expiration, until the next inspiration. - var breaths = []; - var expiring = false; // This should be in liters... - function integrateSamples(a,z) { + function integrateSamples(a,z,flows) { // -1 for quadilateral approximation var vol = 0; for(var j = a; j < z-1; j++) { @@ -1520,16 +1715,34 @@ function computeMovingWindowTrace(samples,t,v) { return vol/(60*1000); } - function compute_breaths_based_on_exhalations(transitions) { + // This is based only on inhalations, and + // is therefore functional when there is a check valve + // in place. Such a system will rarely + // have negative flows, and we must mark + // the beginning of a breath from a transition to a "1" + // state from any other state. + // This algorithm is simple: A breath begins on a trasition + // to 1 from a not 1 state. This algorithm is susceptible + // to "stutter" near the boundary point, but if necessary + // a digital filter would sove that; we have not yet found + // that level of sophistication needed. + // We still want to track zeros, but now must strack them + // as a falling signal. + + function compute_breaths_based_without_negative_flow(transitions,flows) { var beg = 0; var zero = 0; var last = 0; var voli = 0; var vole = 0; + var breaths = []; + var expiring = true; + for(var i = 0; i < transitions.length; i++) { // We're looking for the end of the inhalation here!! - if (((i -1) >= 0) && transitions[i-1].state == 1 && (transitions[i].state == 0 || transitions[i].state == -1 )) { + if (((i -1) >= 0) && transitions[i-1].state == 1 && + (transitions[i].state == 0 || transitions[i].state == -1 )) { zero = i; } if (expiring && transitions[i].state == 1) { @@ -1542,45 +1755,74 @@ function computeMovingWindowTrace(samples,t,v) { trans_end_exhale: i, } ); + var w = PressureVolumeWork(breaths[breaths.length-1], transitions, samples); + breaths[breaths.length-1].work = w; beg = i; expiring = false; - vole = integrateSamples(last,transitions[i].sample); + vole = integrateSamples(last,transitions[i].sample,flows); + last = transitions[i].sample; } - if (!expiring && transitions[i].state == -1) { + if (!expiring && ((transitions[i].state == -1) || (transitions[i].state == 0))) { expiring = true; - voli = integrateSamples(last,transitions[i].sample); + voli = integrateSamples(last,transitions[i].sample,flows); last = transitions[i].sample; } } + return breaths; } - // This is based only on inhalations, and - // is therefore functional when there is a check valve - // in place. Such a system will rarely - // have negative flows, and we must mark - // the beginning of a breath from a transition to a "1" - // state from any other state. - // This algorithm is simple: A breath begins on a trasition - // to 1 from a not 1 state. This algorithm is susceptible - // to "stutter" near the boundary point, but if necessary - // a digital filter would sove that; we have not yet found - // that level of sophistication needed. - // We still want to track zeros, but now must strack them - // as a falling signal. - function compute_breaths_based_without_negative_flow(transitions) { + +// A simple computation of a moving window trace +// computing [A + -B], where A is volume to left +// of sample int time window t, and B is volume to right +// t is in milliseconds +function computeMovingWindowTrace(samples,t,v) { + + var flows = samples.filter(s => s.event == 'M' && s.type == 'F'); + var first_time = flows[0].ms; + var last_time = flows[flows.length - 1].ms; + var duration = last_time - first_time; + + // Here is an idea... + // We define you to be in one of three states: + // Inspiring, expiring, or neither. + // Every transition between these states is logged. + // Having two inspirations between an expiration is + // weird but could happen. + // We record transitions. + // When the time series crossed a fixed threshold + // or zero, it causes a transition. If you are inspiring, + // you have to cross zero to transition to neither, + // and you start expiring when you cross the treshold. + + // This is measured in standard liters per minute. + const vm = 10; // previously used 4 + + // We will model this as a list of transitions. + // A breath is any number of inspirations followed by + // any number of expirations. (I+)(E+) + + var transitions = compute_transitions(vm,flows); + + // Now that we have transitions, we can apply a + // diferrent algorithm to try to define "breaths". + // Because a breath is defined as an inspiration + // and then an expiration, we will define a breath + // as from the first inspiration, until there has + // been one expiration, until the next inspiration. + var breaths = []; + var expiring = false; + + function compute_breaths_based_on_exhalations(transitions) { var beg = 0; var zero = 0; var last = 0; var voli = 0; var vole = 0; - var breaths = []; - var expiring = true; - for(var i = 0; i < transitions.length; i++) { // We're looking for the end of the inhalation here!! - if (((i -1) >= 0) && transitions[i-1].state == 1 && - (transitions[i].state == 0 || transitions[i].state == -1 )) { + if (((i -1) >= 0) && transitions[i-1].state == 1 && (transitions[i].state == 0 || transitions[i].state == -1 )) { zero = i; } if (expiring && transitions[i].state == 1) { @@ -1593,22 +1835,22 @@ function computeMovingWindowTrace(samples,t,v) { trans_end_exhale: i, } ); + var w = PressureVolumeWork(breaths[0], transitions, samples); + breaths[0].work = w; beg = i; expiring = false; - vole = integrateSamples(last,transitions[i].sample); - + vole = integrateSamples(last,transitions[i].sample,flows); last = transitions[i].sample; } - if (!expiring && ((transitions[i].state == -1) || (transitions[i].state == 0))) { + if (!expiring && transitions[i].state == -1) { expiring = true; - voli = integrateSamples(last,transitions[i].sample); + voli = integrateSamples(last,transitions[i].sample,flows); last = transitions[i].sample; } } - return breaths; } - breaths = compute_breaths_based_without_negative_flow(transitions); + breaths = compute_breaths_based_without_negative_flow(transitions,flows); return [transitions,breaths]; }