scripts/channelClasses/standard/HRpt.java
/////////////////////////////////////////////////////////////////
// Processes ECG data by the Pan-Tompkins algorithm, to generate and
// append HR and RR timeseries
//
// This code is compiled at runtime, but compilation can be tested by:
// javac -cp build scripts/channelClasses/standard/HRpt.java
// rm scripts/channelClasses/standard/HRpt.class
//
// Try this script, HRpt, with
// java -ea -cp build:lib/derby.jar:jri/JRI.jar -Dserver.cache.enable=false -Djava.library.path=c:jri frontendClasses/CLI -fn data/10006636.EC.NS5 -scriptChannel HRpt -paradigm ec -scriptEpoch OneLong -reviewSeries
//
// and see, from the additional series, that the mean HR is about 59.8 bpm.
// Then try this script again, but do spectral transformation (but not score)
//
// java -ea -cp build:lib/derby.jar:jri/JRI.jar -Dserver.cache.enable=false -Djava.library.path=c:jri frontendClasses/CLI -fn data/10006636.EC.NS5 -scriptChannel HRpt -paradigm ec -scriptEpoch OneLong -chanSelection "PrimarySite LIKE '_z' OR Mode='ECG' OR Mode='CED'" -transform power -binZ PrimarySite -display "TiledStack(offsetV=0.2, offsetH=0.25)"
//
// and see if ECG peaks at 0.970 Hz, and respiratory sinus arrhythmia is
// apparent in ECG_Rate at 0.2 -- 0.3 Hz. Finally, try another script and
// the HeartRate scoring option (but no spectral transform)
//
// java -cp build:lib/derby.jar -Dserver.cache.enable=false -Djava.library.path=c frontendClasses/CLI -fn data/10006636.EC.NS5 -scriptChannel HRpt -paradigm ec -scriptEpoch OneLong -chanSelection "PrimarySite LIKE '_z' OR Mode='ECG' OR Mode='CED'" -score "HeartRate()" -display "TiledStack()" -output "Export(outAttr=false,outWave=false,as=TXT,tableAsCols=false)"
//
// to see the summary HRV scores.
/////////////////////////////////////////////////////////////////
package standard;
import java.io.*;
import java.util.*;
import generalClasses.*;
import recordingClasses.Recording;
import seriesClasses.*;
import channelClasses.ChannelScript;
import static channelClasses.Channel.*;
/////////////////////////////////////////////////////////////////
/** Processes ECG data by the Pan-Tompkins algorithm, to generate
* and append HR and RR timeseries. More about this algorithm can
* be found in {@link generalClasses.QRSpt}. Time-domain plots are
* useful for visually checking QRS detection for artifacts.
* Frequency-domain plots are useful for visualizing the structure of
* HR variability.
* <p>This script is <b>not</b> required in order to obtain HRV scores.
*/
public class HRpt extends ChannelScript
{
/** Recording instance to be operated on */
Recording rec = null;
/** Log of warnings generated during update */
private ArrayList<String> warnings = null;
/** This is used only for debugging purposes */
private ByteArrayOutputStream ba = null;
////////////////////////////////////////////////////////////////////
/** Initialize instance by setting its parameters to default values.
*/
public HRpt(Recording rec) {
this.rec = rec;
warnings = new ArrayList<String>();
ba = new ByteArrayOutputStream();
} // HRpt
////////////////////////////////////////////////////////////////////
/** Update recording data by performing channel-oriented operations.
*/
public void update() {
// DO standard processing of EEG channels
float highpassCutoff = 0.5f;
ArrayList<SeriesAnalog> subsetEEG = selectMode(rec, DataMode.EEG);
filterHP(highpassCutoff, subsetEEG); // NB: this does not remove DC
subtractTemporalMean(subsetEEG);
updateMatchingSeries(rec, subsetEEG);
// Find one and only ECG
ArrayList<SeriesAnalog> subset = selectMode(rec, DataMode.ECG);
if(subset.size()==0) {
warnings.add("HRpt: unable to find any ECG channels");
return;
} else if(subset.size()>1) {
String list = "";
for(SeriesAnalog s: subset) list += s.getPrimaryLabel()+" ";
warnings.add("HRpt: "+list+"all ECG; only one is allowed");
return;
}
SeriesAnalog series = subset.get(0);
SeriesAnalog hrSeries = QRSpt.getHRSeries(series, new PrintStream(ba));
SeriesAnalog rrSeries = QRSpt.getRRSeries(series, null);
// This converts the two extra series from DataMode=ECG to
// something else (anything else) in order that the heart rate
// scoring algorithm can still identify the read ECG channel
hrSeries.setMode(DataMode.CED); // +ve up, yDelta=0.4
rrSeries.setMode(DataMode.CED); // +ve up, yDelta=0.4
// Update Recording object
appendToCurrentSeries(rec, hrSeries);
appendToCurrentSeries(rec, rrSeries);
} // update
////////////////////////////////////////////////////////////////////
/** Dump summary of this class or object
* @return String representation of this object
*/
public String toString() {
String s = "<<<"+this.getClass().toString()+">>>\n";
s += ba.toString();
for(String w: warnings)
s += w+"\n";
return s;
} // toString
}