package ua.stu.scplib.structure;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.io.IOException;
import ua.stu.scplib.attribute.BinaryInputStream;
/**
* <p>A class to encapsulate an entire SCP-ECG object.</p>
*
* <p>Typically this class would be used to read an SCP-ECG object from a file,
* and to do something with the decompressed waveform data values. For example:</p>
*
* <pre>
try {
BinaryInputStream i = new BinaryInputStream(new BufferedInputStream(new FileInputStream(arg[0])),false); // always little endian
SCPECG scpecg = new SCPECG(i,verbose);
short[][] data = scpecg.getDecompressedRhythmData();
BinaryOutputStream o = new BinaryOutputStream(new BufferedOutputStream(new FileOutputStream(arg[1])),false); // little endian
// write interleaved raw little endian data
int numberOfChannels = data.length;
int nSamplesPerChannel = data[0].length; // assume all the same
for (int sample=0; sample<nSamplesPerChannel; ++sample) {
for (int lead=0;lead<numberOfChannels; ++lead) {
o.writeSigned16(data[lead][sample]);
}
}
o.close();
}
catch (Exception e) {
e.printStackTrace(System.err);
}
* </pre>
* <p>One might want to dump the entire contents of the instance as a string, as follows:</p>
*
* <pre>
System.err.print(scpecg);
* </pre>
* <p>or perhaps to display the contents as a JTree:</p>
*
* <pre>
ApplicationFrame af = new ApplicationFrame();
JScrollPane scrollPane = new JScrollPane();
SCPTreeBrowser browser = new SCPTreeBrowser(scpecg,scrollPane);
af.getContentPane().add(scrollPane);
af.pack();
af.show();
* </pre>
*
*/
public class SCPECG {
private static String nameOfCodeFild="";
public static String getNameOfCodeFild() {
return nameOfCodeFild;
}
public void setNameOfCodeFild(String nameOfCodeFild) {
this.nameOfCodeFild = nameOfCodeFild;
}
private class ReferenceBeatSubtractionZone {
private int numberOfReferenceBeatSubtractionZones;
long fcm; // sample number of the fiducial relative to the beginning of reference beat 0
private long[] start;
private long[] fc;
private long[] end;
ReferenceBeatSubtractionZone(long sampleNumberOfQRSOfFiducial,
long[] sampleNumberOfResidualToStartSubtractingQRS,
long[] sampleNumberOfResidualOfFiducial,
long[] sampleNumberOfResidualToEndSubtractingQRS) {
fcm=sampleNumberOfQRSOfFiducial;
start=sampleNumberOfResidualToStartSubtractingQRS;
fc=sampleNumberOfResidualOfFiducial;
end=sampleNumberOfResidualToEndSubtractingQRS;
//assert(start != null);
//assert(fc != null);
//assert(end != null);
numberOfReferenceBeatSubtractionZones=start.length;
//assert(numberOfReferenceBeatSubtractionZones == fc.length);
//assert(numberOfReferenceBeatSubtractionZones == end.length);
}
public int getSampleOffsetWithinReferenceBeatSubtractionZone(long sample) { // -1 is flag that it is not in the zone
for (int qrs=0; qrs<numberOfReferenceBeatSubtractionZones; ++qrs) {
if (sample >= start[qrs] && sample <= end[qrs]) {
//assert((fc[qrs] - start[qrs]) <= max Java int);
int offsetToAlignFiducial = (int)(fcm - (fc[qrs] - start[qrs]));
//assert((sample - start[qrs]) <= max Java int);
int offsetFromStartOfReferenceBeat = (int)(sample - start[qrs] + offsetToAlignFiducial); // numbered from zero
//assert(offsetFromStartOfReferenceBeat != -1);
//System.err.println("In the ReferenceBeatSubtractionZone "+sample);
return offsetFromStartOfReferenceBeat;
}
}
return -1;
}
}
private class ProtectedArea {
private int numberOfProtectedAreas;
private long[] start;
private long[] end;
ProtectedArea(long[] sampleNumberOfResidualToStartProtectedArea,long[] sampleNumberOfResidualToEndProtectedArea) {
start=sampleNumberOfResidualToStartProtectedArea;
end=sampleNumberOfResidualToEndProtectedArea;
//assert(start != null);
//assert(end != null);
numberOfProtectedAreas=start.length;
//assert(numberOfProtectedAreas == end.length);
}
public boolean isSampleWithinProtectedArea(long sample) {
boolean within=false;
for (int area=0; area<numberOfProtectedAreas; ++area) {
if (sample >= start[area] && sample <= end[area]) {
within=true;
break;
}
}
return within;
}
}
private TreeMap sections;
private RecordHeader recordHeader;
private short[][] decompressedRhythmData;
/**
* <p>Get the decompressed rhythm data.</p>
*
* <p>This includes undecimating and adding in any reference beat.</p>
*
* <p>Note that interpolation and filtering are NOT applied in the current implementation.</p>
*
* @return arrays of samples for each lead
*/
public short[][] getDecompressedRhythmData() { return decompressedRhythmData; }
private void decompressRhythmData() {
Section3 section3 = getSection3();
int numberOfLeads = section3 == null ? 0 : section3.getNumberOfLeads();
long[] numbersOfSamples = section3 == null ? null : section3.getNumbersOfSamples();
boolean referenceBeatUsedForCompression = section3 == null ? false : section3.getReferenceBeatUsedForCompression();
Section2 section2 = getSection2();
int numberOfHuffmanTables = section2 == null ? 0 : section2.getNumberOfHuffmanTables();
ArrayList huffmanTablesList = section2 == null ? null : section2.getHuffmanTables();
Section4 section4 = getSection4();
int sampleNumberOfQRSOfFiducial = section4 == null ? 0 : section4.getSampleNumberOfQRSOfFiducial();
long[] sampleNumberOfResidualToStartSubtractingQRS = section4 == null ? null : section4.getSampleNumberOfResidualToStartSubtractingQRS();
long[] sampleNumberOfResidualOfFiducial = section4 == null ? null : section4.getSampleNumberOfResidualOfFiducial();
long[] sampleNumberOfResidualToEndSubtractingQRS = section4 == null ? null : section4.getSampleNumberOfResidualToEndSubtractingQRS();
long[] sampleNumberOfResidualToStartProtectedArea = section4 == null ? null : section4.getSampleNumberOfResidualToStartProtectedArea();
long[] sampleNumberOfResidualToEndProtectedArea = section4 == null ? null : section4.getSampleNumberOfResidualToEndProtectedArea();
Section5Or6 section5 = getSection5();
int sampleTimeIntervalForReference = section5 == null ? 0 : section5.getSampleTimeInterval();
Section5Or6 section6 = getSection6();
int amplitudeValueMultiplier = section6 == null ? 0 : section6.getAmplitudeValueMultiplier();
int differenceDataUsed = section6 == null ? 0 : section6.getDifferenceDataUsed();
byte[][] compressedLeadData = section6 == null ? null : section6.getCompressedLeadData();
short[][] leadData = section6 == null ? null : section6.getLeadData();
int bimodalCompressionUsed = section6 == null ? 0 : section6.getBimodalCompressionUsed();
int sampleTimeIntervalForRhythm = section6 == null ? 0 : section6.getSampleTimeInterval();
int samplingRateDecimationFactor = (sampleTimeIntervalForReference == 0)
? 1
: sampleTimeIntervalForRhythm/sampleTimeIntervalForReference;
//System.err.println("samplingRateDecimationFactor "+samplingRateDecimationFactor);
//System.err.println("bimodalCompressionUsed "+bimodalCompressionUsed);
//ProtectedArea protectedAreas = bimodalCompressionUsed == 0
ProtectedArea protectedAreas = (sampleNumberOfResidualToStartProtectedArea == null || sampleNumberOfResidualToEndProtectedArea == null)
? null
: new ProtectedArea(sampleNumberOfResidualToStartProtectedArea,sampleNumberOfResidualToEndProtectedArea);
ReferenceBeatSubtractionZone referenceBeatSubtractionZones = referenceBeatUsedForCompression
? new ReferenceBeatSubtractionZone(sampleNumberOfQRSOfFiducial,
sampleNumberOfResidualToStartSubtractingQRS,sampleNumberOfResidualOfFiducial,sampleNumberOfResidualToEndSubtractingQRS)
: null;
decompressedRhythmData = new short[numberOfLeads][];
for (int lead=0; lead<numberOfLeads; ++lead) {
//System.err.println("Decompressing rhythm or residual data for lead "+lead);
decompressedRhythmData[lead] = null;
// assert(numbersOfSamples[lead] <= largest Java int);
int useNumberOfSamples = (int)(numbersOfSamples[lead]);
//System.err.println("useNumberOfSamples = "+useNumberOfSamples);
try {
HuffmanDecoder decoder=null;
if (section2!=null){
decoder = new HuffmanDecoder(
compressedLeadData[lead],
differenceDataUsed,amplitudeValueMultiplier/1000, // amplitudeValueMultiplier is nanoVolts, not microVolts
numberOfHuffmanTables,huffmanTablesList);
}
{
decompressedRhythmData[lead] = new short[useNumberOfSamples];
short value = 0;
int decimationOffsetCount = 0;
short lastDecimatedValue = 0;
short currentDecimatedValue = 0;
for (int sample=1; sample<=useNumberOfSamples; ++sample) {
boolean within = protectedAreas != null && protectedAreas.isSampleWithinProtectedArea(sample);
//System.err.println("["+sample+"] in protected area = "+within);
if (within) {
if (section2!=null) value = decoder.decode();
else {
value = leadData[lead][sample-1];
value*=amplitudeValueMultiplier/1000;
}
decimationOffsetCount = 0;
}
else {
if (samplingRateDecimationFactor <= 1) { // should never happen, but if we didn't check, division by zero
if (section2!=null) value = decoder.decode();
else if ((sample -1) < useNumberOfSamples - 2){
value = leadData[lead][sample-1];
value*=(float) amplitudeValueMultiplier/1000;
}
}
else {
//int interpolationOffset = decimationOffsetCount%samplingRateDecimationFactor;
//double interpolationMultipier = ((double)interpolationOffset)/samplingRateDecimationFactor;
//if (interpolationOffset == 0) {
// lastDecimatedValue = currentDecimatedValue;
// currentDecimatedValue = decoder.decode();
// if (decimationOffsetCount < samplingRateDecimationFactor) {
// lastDecimatedValue=currentDecimatedValue; // there was no previous value to interpolate from
// }
//}
//value = (short)(lastDecimatedValue + (currentDecimatedValue-lastDecimatedValue)*interpolationMultipier);
int interpolationOffset = decimationOffsetCount%samplingRateDecimationFactor;
if (interpolationOffset == 0) {
if (section2!=null) currentDecimatedValue = decoder.decode();
else {
currentDecimatedValue = leadData[lead][sample-1];
currentDecimatedValue*=amplitudeValueMultiplier/1000;
}
}
value = (short)currentDecimatedValue;
}
++decimationOffsetCount;
}
if (referenceBeatSubtractionZones == null) {
decompressedRhythmData[lead][sample-1]=value;
}
else {
int offset = referenceBeatSubtractionZones.getSampleOffsetWithinReferenceBeatSubtractionZone(sample);
//System.err.println("["+sample+"] reference beat subtraction zone offset="+offset);
if (offset != -1) {
//System.err.print("["+lead+","+sample+"] = "+value+" BEFORE; AFTER ");
value+=decompressedReferenceBeatData[lead][offset];
}
decompressedRhythmData[lead][sample-1]=value;
}
//System.err.println("["+lead+","+sample+"] = "+value);
}
}
//System.err.println("Decoder status after all samples done "+decoder.toString());
}
catch (Exception e) {
e.printStackTrace(System.err);
}
}
System.out.println();
}
private short[][] decompressedReferenceBeatData;
/**
* <p>Get the decompressed reference beat data.</p>
*
* @return arrays of samples for each lead
*/
public short[][] getDecompressedReferenceBeatData() { return decompressedReferenceBeatData; }
private void decompressReferenceBeatData() {
Section3 section3 = (Section3)(sections.get(new Integer(3)));
int numberOfLeads = section3 == null ? 0 : section3.getNumberOfLeads();
Section2 section2 = (Section2)(sections.get(new Integer(2)));
int numberOfHuffmanTables = section2 == null ? 0 : section2.getNumberOfHuffmanTables();
ArrayList huffmanTablesList = section2 == null ? null : section2.getHuffmanTables();
Section4 section4 = (Section4)(sections.get(new Integer(4)));
int lengthOfReferenceBeat0DataInMilliSeconds = section4 == null ? 0 : section4.getLengthOfReferenceBeat0DataInMilliSeconds();
Section5Or6 section5 = (Section5Or6)(sections.get(new Integer(5)));
if (section5 == null) {
return;
}
int amplitudeValueMultiplier = section5 == null ? 0 : section5.getAmplitudeValueMultiplier();
int sampleTimeInterval = section5 == null ? 0 : section5.getSampleTimeInterval();
int differenceDataUsed = section5 == null ? 0 : section5.getDifferenceDataUsed();
byte[][] compressedLeadData = section5 == null ? null : section5.getCompressedLeadData();
int numberOfSamples = 1000*lengthOfReferenceBeat0DataInMilliSeconds/sampleTimeInterval; // See prENV 1064 5.7.2
//System.err.println("Number of reference beat samples "+numberOfSamples);
decompressedReferenceBeatData = new short[numberOfLeads][];
for (int lead=0; lead<numberOfLeads; ++lead) {
//System.err.println("Decompressing reference beat data for lead "+lead);
HuffmanDecoder decoder = new HuffmanDecoder(
compressedLeadData[lead],
differenceDataUsed,amplitudeValueMultiplier/1000, // amplitudeValueMultiplier is nanoVolts, not microVolts
numberOfHuffmanTables,huffmanTablesList);
try {
// assert numbersOfSamples[lead] <= largest Java int
decompressedReferenceBeatData[lead] = decoder.decode((int)(numberOfSamples));
}
catch (Exception e) {
e.printStackTrace(System.err);
}
}
}
/**
* <p>Read an SCP-ECG object from an input stream.</p>
*
* <p>As errors are encountered, they are described on <code>stderr</code>.</p>
*
* <p>The sections are also validated as they are read, and Section 0
* is validated against the other sections.</p>
*
* @param i the input stream (should have been opened as little endian)
* @param verbose if true, then the progress of the read is described
*/
public SCPECG(BinaryInputStream i,boolean verbose) throws IOException {
sections = new TreeMap();
recordHeader = new RecordHeader();
long bytesRead = recordHeader.read(i);
long recordBytesRemaining = recordHeader.getRecordLength()-bytesRead; // i.e. CRC and length itself are included in length
long byteOffset = bytesRead;
if (verbose) System.err.print(recordHeader);
while (recordBytesRemaining>0) { // do sections
SectionHeader sectionHeader = new SectionHeader();
bytesRead = sectionHeader.read(i,byteOffset);
//System.err.println("Section "+sectionHeader.getSectionIDNumber());
//System.err.println("[Bytes read = "+bytesRead+" dec (0x"+Long.toHexString(bytesRead)+")]");
recordBytesRemaining-=bytesRead;
//System.err.println("[Record bytes remaining = "+recordBytesRemaining+" dec (0x"+Long.toHexString(recordBytesRemaining)+")]");
byteOffset+=bytesRead;
//System.err.println("[Byte offset = "+byteOffset+" dec (0x"+Long.toHexString(byteOffset)+")]");
if (verbose) System.err.print(sectionHeader);
Section section = Section.makeSection(sectionHeader,sections);
bytesRead = section.read(i);
//System.err.println("[Bytes read = "+bytesRead+" dec (0x"+Long.toHexString(bytesRead)+")]");
recordBytesRemaining-=bytesRead;
//System.err.println("[Record bytes remaining = "+recordBytesRemaining+" dec (0x"+Long.toHexString(recordBytesRemaining)+")]");
byteOffset+=bytesRead;
//System.err.println("[Byte offset = "+byteOffset+" dec (0x"+Long.toHexString(byteOffset)+")]");
if (verbose) System.err.println(section);
System.err.print(section.validate());
sections.put(new Integer(sectionHeader.getSectionIDNumber()),section);
}
Section0 section0 = (Section0)(sections.get(new Integer(0)));
/*System.out.println(sections.get(new Integer(1)).toString());
System.out.println(sections.get(new Integer(2)).toString());
System.out.println(sections.get(new Integer(3)).toString());
System.out.println(sections.get(new Integer(4)).toString());
System.out.println(sections.get(new Integer(5)).toString());
System.out.println(sections.get(new Integer(6)).toString());*/
System.err.print(section0.validateAgainstOtherSections(sections));
decompressReferenceBeatData();
decompressRhythmData();
getIsoCode();
}
/**
* <p>Dump the object as a <code>String</code>.</p>
*
* @return the object as a <code>String</code>
*/
public String toString() {
StringBuffer strbuf = new StringBuffer();
Section1 section1 = getSection1();
if (section1 == null) {
strbuf.append("No demographic and administrative data\n");
}
else {
strbuf.append(section1);
}
Section2 section2 = getSection2();
if (section2 == null) {
strbuf.append("No Huffman entropy coding\n");
}
else {
if (section2.useDefaultTable()) {
strbuf.append("Huffman entropy coding with default table\n");
}
else {
strbuf.append("Huffman entropy coding with "+section2.getNumberOfHuffmanTables()+" specified tables\n");
}
}
Section3 section3 = getSection3();
if (section3 == null) {
strbuf.append("No lead description :(\n");
}
else {
strbuf.append("Number of Leads = "+section3.getNumberOfLeads()+"\n");
strbuf.append((section3.getReferenceBeatUsedForCompression() ? "Reference Beat Used For Compression" : "Reference Beat Not Used For Compression")+"\n");
strbuf.append((section3.getLeadsAllSimultaneouslyRecorded() ? "Leads All Simultaneously Recorded" : "Leads Not All Simultaneously Recorded")+"\n");
strbuf.append("Number of Simultaneously Recorded Leads = "+section3.getNumberOfSimultaneouslyRecordedLeads()+"\n");
}
Section4 section4 = getSection4();
if (section4 == null) {
strbuf.append("No reference beat description\n");
}
else {
strbuf.append("Length ofReference Beat 0 Data In MilliSeconds = "+section4.getLengthOfReferenceBeat0DataInMilliSeconds()+"\n");
strbuf.append("Sample Number ofQRS of Fiducial = "+section4.getSampleNumberOfQRSOfFiducial()+"\n");
strbuf.append("Total Number of QRS Complexes = "+section4.getTotalNumberOfQRSComplexes()+"\n");
}
Section5Or6 section5 = getSection5();
if (section5 == null) {
strbuf.append("No reference beat data\n");
}
else {
strbuf.append("Reference beat data\n");
strbuf.append("\tAmplitude Value Multiplier = "+section5.getAmplitudeValueMultiplier()+"\n");
strbuf.append("\tSample Time Interval = "+section5.getSampleTimeInterval()+"\n");
strbuf.append("\tDifference Data Used = "+section5.getDifferenceDataUsed()+"\n");
}
Section5Or6 section6 = getSection6();
if (section6 == null) {
strbuf.append("No rhythm or residual data\n");
}
else {
strbuf.append("Rhythm or residual data\n");
strbuf.append("\tAmplitude Value Multiplier = "+section6.getAmplitudeValueMultiplier()+"\n");
strbuf.append("\tSample Time Interval = "+section6.getSampleTimeInterval()+"\n");
strbuf.append("\tDifference Data Used = "+section6.getDifferenceDataUsed()+"\n");
strbuf.append("\tBimodal Compression Used = "+section6.getBimodalCompressionUsed()+"\n");
}
return strbuf.toString();
}
public Section1 getSection1() { return (Section1)(sections.get(new Integer(1))); }
public Section2 getSection2() { return (Section2)(sections.get(new Integer(2))); }
public Section3 getSection3() { return (Section3)(sections.get(new Integer(3))); }
public Section4 getSection4() { return (Section4)(sections.get(new Integer(4))); }
public Section5Or6 getSection5() { return (Section5Or6)(sections.get(new Integer(5))); }
public Section5Or6 getSection6() { return (Section5Or6)(sections.get(new Integer(6))); }
/* public Section7 getSection7() { return (Section7)(sections.get(new Integer(7))); }
public Section8Or11 getSection8() { return (Section8Or11)(sections.get(new Integer(8))); }
public Section10 getSection10() { return (Section10)(sections.get(new Integer(10))); }
public Section8Or11 getSection11() { return (Section8Or11)(sections.get(new Integer(11))); }
*/
/**
* <p>Get the number of leads.</p>
*
* @return the number of leads
*/
public int getNumberOfLeads() {
Section3 section3 = getSection3();
return section3 == null ? 0 : section3.getNumberOfLeads();
}
/**
* <p>Get the number of samples for each lead.</p>
*
* @return an array of the number of samples for each lead
*/
public long[] getNumbersOfSamples() {
Section3 section3 = getSection3();
return section3 == null ? null : section3.getNumbersOfSamples();
}
/**
* <p>Get the sample time interval that will be applicable to the decompressed rhythm data.</p>
*
* <p>Note that this is the value after undecimation if the rhythm data was
* decimated; specifically in such cases it will be the sample time interval
* of the reference beat.</p>
*
* @return the sample time interval in microSeconds
*/
public int getDecompressedRhythmDataSampleTimeInterval() {
int interval=0;
Section5Or6 section5 = getSection5();
if (section5 != null) {
interval=section5.getSampleTimeInterval(); // the reference beat interval will have been
// used once the compressed rhythm data was undecimated to match it
}
else {
Section5Or6 section6 = getSection6();
if (section6 != null) {
interval=section6.getSampleTimeInterval(); // no reference beat, therefore no decimation occurred ( ? :( )
}
}
return interval;
}
/**
* <p>Get Isocode.</p>
*
* @return the values as a <code>String</code>
*/
public void getIsoCode(){
//get encoding
String nameOfCode="";
int namberInInt=0;
StringTokenizer str=null;
Section1 section1 = getSection1();
if (section1!=null){
StringTokenizer adi=new StringTokenizer(section1.getConcatenatedStringValuesOfAllOccurencesOfNamedField("AcquiringDeviceIdentificationNumber"),",");
while (adi.hasMoreTokens()) {
String curr=adi.nextToken();
if (curr.startsWith(" languageSupport")){
// System.out.println(curr);
str =new StringTokenizer(curr,"=");
str.nextToken();
namberInInt=Integer.parseInt(str.nextToken().trim().substring(2),16);
}
}
switch (namberInInt) {
case 19:
nameOfCode="ISO8859_5";
break;
case 0:
nameOfCode="ASCII";
break;
case 3:
nameOfCode="ISO8859_2";
break;
case 11:
nameOfCode="ISO8859_4";
break;
case 27:
nameOfCode="ISO8859_6";
break;
case 35:
nameOfCode="ISO8859_7";
break;
case 43:
nameOfCode="ISO8859_8";
break;
case 51:
nameOfCode="ISO8859_11";
break;
default:
nameOfCode="ASCII";
break;
}
// System.out.println("-------------------------");
// System.out.println(nameOfCode);
}
setNameOfCodeFild(nameOfCode);
}
/**
* <p>Get the concatenated values of all the occurences of a named field from Section 1 as a <code>String</code>.</p>
*
* @return the values as a <code>String</code>
*/
public String getNamedField(String fieldName) {
Section1 section1 = getSection1();
return section1 == null ? null : section1.getConcatenatedStringValuesOfAllOccurencesOfNamedField(fieldName);
}
/**
* <p>Get the names of the leads from Section 3.</p>
*
* @return the names of the leads
*/
public String[] getLeadNames() {
Section3 section3 = getSection3();
return section3 == null ? null : section3.getLeadNames();
}
/**
* <p>Get the numbers of the leads from Section 3.</p>
*
* @return the numbers of the leads (using the codes in the standard and encoded in the data)
*/
public int[] getLeadNumbers() {
Section3 section3 = getSection3();
return section3 == null ? null : section3.getLeadNumbers();
}
/**
* <p>Get an <code>Iterator</code> over all the sections.</p>
*
* @return an iterator of objects of type {@link com.pixelmed.scpecg.Section Section}
*/
public Iterator getSectionIterator() { return sections == null ? null : sections.values().iterator(); }
/**
* <p>For testing.</p>
*
* @param arg one, two or three arguments, the input filename, optionally a output raw data filename, and optionally a text dump filename
*/
/* public static void main(String arg[]) {
boolean verbose = false;
try {
BinaryInputStream i = new BinaryInputStream(new BufferedInputStream(new FileInputStream(arg[0])),false); // SCP-ECG always little endian
SCPECG scpecg = new SCPECG(i,verbose);
//System.err.print(scpecg);
if (arg.length > 1) {
short[][] data = scpecg.getDecompressedRhythmData();
BinaryOutputStream o = new BinaryOutputStream(new BufferedOutputStream(new FileOutputStream(arg[1])),false); // SCP-ECG always little endian
// write interleaved raw little endian data
int numberOfChannels = data.length;
int nSamplesPerChannel = data[0].length; // assume all the same
for (int sample=0; sample<nSamplesPerChannel; ++sample) {
for (int lead=0;lead<numberOfChannels; ++lead) {
o.writeSigned16(data[lead][sample]);
}
}
o.close();
if (arg.length > 2) {
PrintWriter p = new PrintWriter(new BufferedOutputStream(new FileOutputStream(arg[2])));
// write signed short values non-interleaved (i.e. one lead at a time)
for (int lead=0;lead<numberOfChannels; ++lead) {
p.println("Lead: "+lead);
for (int sample=0; sample<nSamplesPerChannel; ++sample) {
p.println("["+sample+"]="+data[lead][sample]);
}
}
p.close();
}
}
{
ApplicationFrame af = new ApplicationFrame();
JScrollPane scrollPane = new JScrollPane();
SCPTreeBrowser browser = new SCPTreeBrowser(scpecg,scrollPane);
af.getContentPane().add(scrollPane);
af.pack();
af.show();
}
}
catch (Exception e) {
e.printStackTrace(System.err);
}
}*/
}