├── app ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── values │ │ │ ├── strings.xml │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ └── styles.xml │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ │ └── dimens.xml │ │ └── layout │ │ │ └── activity_main.xml │ │ ├── java │ │ └── io │ │ │ └── medicaldev │ │ │ └── pantompkinstesting │ │ │ ├── FixedQueue.java │ │ │ ├── FindPeaks.java │ │ │ ├── SampleMulti.java │ │ │ ├── Filter.java │ │ │ ├── MainActivity.java │ │ │ └── HeartyFilter.java │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── .idea ├── encodings.xml ├── vcs.xml ├── workspace.xml ├── modules.xml ├── PanTompkins-Android.iml ├── gradle.xml └── runConfigurations.xml ├── gradle └── wrapper │ └── gradle-wrapper.properties ├── local.properties └── PanTompkins-Android.iml /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | PanTompkinsTesting 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-adtechs/PanTompkins-Android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-adtechs/PanTompkins-Android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-adtechs/PanTompkins-Android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-adtechs/PanTompkins-Android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinav-adtechs/PanTompkins-Android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #4CAF50 4 | #388E3C 5 | #FFC107 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue May 28 14:49:37 IST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/PanTompkins-Android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /local.properties: -------------------------------------------------------------------------------- 1 | ## This file must *NOT* be checked into Version Control Systems, 2 | # as it contains information specific to your local configuration. 3 | # 4 | # Location of the SDK. This is only used by Gradle. 5 | # For customization when using a Version Control System, please read the 6 | # header note. 7 | #Tue May 28 14:41:06 IST 2019 8 | sdk.dir=/Users/abhinavdas/Library/Android/sdk 9 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/io/medicaldev/pantompkinstesting/FixedQueue.java: -------------------------------------------------------------------------------- 1 | package io.medicaldev.pantompkinstesting; 2 | 3 | 4 | import java.util.LinkedList; 5 | 6 | public class FixedQueue extends LinkedList { 7 | 8 | private int limit; 9 | 10 | public FixedQueue(int limit) { 11 | this.limit = limit; 12 | } 13 | 14 | @Override 15 | public boolean add(E o) { 16 | boolean added = super.add(o); 17 | while (added && size() > limit) { 18 | super.remove(); 19 | } 20 | return added; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/java/io/medicaldev/pantompkinstesting/FindPeaks.java: -------------------------------------------------------------------------------- 1 | package io.medicaldev.pantompkinstesting; 2 | 3 | 4 | import android.content.Context; 5 | 6 | public class FindPeaks { 7 | 8 | private Context mContext ; 9 | private int[] peakArray ; 10 | private static final int windowWidth = 44 ; 11 | private FixedQueue peakFindingQueue = new FixedQueue<>(windowWidth) ; 12 | private int currentMaxima ; 13 | 14 | 15 | public FindPeaks(Context mContext) { 16 | this.mContext = mContext; 17 | } 18 | 19 | public void calculatePeak(){ 20 | 21 | } 22 | 23 | public int[] getPeakArray() { 24 | return peakArray; 25 | } 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/abhinav/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | 6 | defaultConfig { 7 | applicationId "io.medicaldev.pantompkinstesting" 8 | minSdkVersion 19 9 | targetSdkVersion 28 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | repositories { 20 | maven { url "https://jitpack.io" } 21 | } 22 | } 23 | 24 | dependencies { 25 | compile fileTree(dir: 'libs', include: ['*.jar']) 26 | testCompile 'junit:junit:4.12' 27 | compile 'com.android.support:appcompat-v7:28.0.0' 28 | compile group: 'com.google.guava', name: 'guava', version: '18.0' 29 | compile 'com.github.PhilJay:MPAndroidChart:v2.2.5' 30 | } 31 | -------------------------------------------------------------------------------- /PanTompkins-Android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/io/medicaldev/pantompkinstesting/SampleMulti.java: -------------------------------------------------------------------------------- 1 | package io.medicaldev.pantompkinstesting; 2 | 3 | import android.graphics.Color; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.AppCompatActivity; 7 | 8 | import com.github.mikephil.charting.charts.LineChart; 9 | import com.github.mikephil.charting.components.YAxis; 10 | import com.github.mikephil.charting.data.Entry; 11 | import com.github.mikephil.charting.data.LineData; 12 | import com.github.mikephil.charting.data.LineDataSet; 13 | import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Random; 17 | 18 | 19 | public class SampleMulti extends AppCompatActivity { 20 | 21 | LineChart lineChart ; 22 | 23 | @Override 24 | protected void onCreate(@Nullable Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | 27 | setContentView(R.layout.activity_main); 28 | 29 | init() ; 30 | setChart() ; 31 | 32 | } 33 | 34 | private void init() { 35 | lineChart = (LineChart) findViewById(R.id.main_lineChart) ; 36 | } 37 | 38 | private void setChart() { 39 | lineChart.setViewPortOffsets(0,20,0,0); 40 | 41 | lineChart.setData(new LineData()); 42 | lineChart.setBackgroundColor(Color.BLACK); 43 | lineChart.setDrawGridBackground(false); 44 | lineChart.animateXY(1000,1000) ; 45 | feedData() ; 46 | 47 | } 48 | 49 | private void feedData() { 50 | 51 | new Thread(new Runnable() { 52 | @Override 53 | public void run() { 54 | runOnUiThread(new Runnable() { 55 | @Override 56 | public void run() { 57 | addEntry() ; 58 | } 59 | }); 60 | 61 | try { 62 | Thread.sleep(1000); 63 | }catch (InterruptedException e) { 64 | e.printStackTrace(); 65 | } 66 | } 67 | }).start(); 68 | 69 | } 70 | 71 | ArrayList colorList = new ArrayList<>() ; 72 | 73 | 74 | private void addEntry() { 75 | LineData data = lineChart.getData() ; 76 | 77 | colorList.add(0, Color.RED); 78 | colorList.add(1, Color.BLUE); 79 | colorList.add(2, Color.YELLOW); 80 | 81 | if(data.getDataSetCount() < 3){ 82 | for (int i = data.getDataSetCount(); i < 3; i++) { 83 | ILineDataSet currentSet ; 84 | LineDataSet dataSet = new LineDataSet(null, "Row" + i) ; 85 | dataSet.setAxisDependency(YAxis.AxisDependency.LEFT); 86 | dataSet.setColor(colorList.get(i)); 87 | currentSet = dataSet ; 88 | data.addDataSet(currentSet); 89 | 90 | for (int j = 0; j < 5; j++) { 91 | data.getDataSetByIndex(i).addEntry(new Entry((float)Math.random(), j )) ; 92 | } 93 | 94 | } 95 | } 96 | 97 | 98 | lineChart.notifyDataSetChanged(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /app/src/main/java/io/medicaldev/pantompkinstesting/Filter.java: -------------------------------------------------------------------------------- 1 | package io.medicaldev.pantompkinstesting; 2 | 3 | import android.util.Log; 4 | 5 | 6 | public class Filter { 7 | 8 | private static final String TAG = "Debugging TAG"; 9 | private double[] currentECGReading; 10 | private double currentECGVal ; 11 | private int samplingRate = 250 ; 12 | private static final double[] lowPassCoeff = { 1.0000, 2.0000, 1.0000, 1.0000, -1.4755, 0.5869} ; 13 | private static final double[] highPassCoeff = { 1.0000, -2.0000, 1.0000, 1.0000, -1.8227, 0.8372} ; 14 | private static final double[] diffCoeff = {-0.1250, -0.2500, 0, 0.2500, 0.1250} ; 15 | private static final int windowWidth = 80 ; 16 | private FixedQueue movingWindowQueue = new FixedQueue<>(windowWidth) ; 17 | private double movingWindowSum = 0; 18 | 19 | private double[] tempArrayX = new double[diffCoeff.length] ; 20 | 21 | private double[] lowPass_temp = new double[lowPassCoeff.length] ; 22 | private double[] highPass_temp = new double[highPassCoeff.length] ; 23 | 24 | private int i,n=12; 25 | private double y0=0,y1=0,y2=0, x[] = new double[26]; 26 | 27 | 28 | /////// 29 | private double highy0=0,highy1=0, highx[] = new double[66]; 30 | private int Highn=32; 31 | 32 | 33 | 34 | 35 | public Filter(double[] ecg_vals) { 36 | currentECGReading = ecg_vals ; 37 | } 38 | 39 | public Filter() { 40 | System.arraycopy(diffCoeff, 0, tempArrayX, 1, diffCoeff.length-1); 41 | } 42 | 43 | 44 | 45 | public void setCurrentECGReading(double[] currentECGReading) { 46 | this.currentECGReading = currentECGReading; 47 | 48 | } 49 | 50 | public void staticFilter(){ 51 | double tempVar ; 52 | for (int i = 0; i < currentECGReading.length; i++) { 53 | tempVar = 0 ; 54 | 55 | tempVar = lowPassNext(currentECGReading[i]) ; 56 | tempVar = highPassNext(tempVar) ; 57 | tempVar = diffFilterNext(tempVar) ; 58 | tempVar = squareNext(tempVar) ; 59 | 60 | //or// 61 | //tempVar = squareNext(diffFilterNext(highPassNext(lowPassNext(currentECGReading[i])))) ; 62 | } 63 | } 64 | 65 | public void movingWindowhandler(){ 66 | 67 | 68 | } 69 | 70 | public void queueInit(){ 71 | 72 | } 73 | 74 | public double lowPassNext(double upVal){ 75 | double mod = 0 ; 76 | for (int i = 0; i < lowPassCoeff.length; i++) { 77 | mod = mod + upVal* lowPassCoeff[i] ; 78 | } 79 | return mod ; 80 | } 81 | 82 | public double lowPassNext(String upValString){ 83 | 84 | double mod = 0, upVal ; 85 | upVal = Double.parseDouble(upValString) ; 86 | 87 | for (int i = 0; i < lowPassCoeff.length; i++) { 88 | mod = mod + upVal* lowPassCoeff[i] ; 89 | } 90 | return mod ; 91 | } 92 | 93 | 94 | double LowPassFilter(double val){ 95 | 96 | x[n] = x[n + 13] = val; 97 | y0 = (y1*2) - y2 + x[n] - (x[n +6]*2) + x[n +12]; 98 | y2 = y1; 99 | y1 = y0; 100 | y0 = y0/32; 101 | if(--n < 0) 102 | n = 12; 103 | 104 | return y0 ; 105 | 106 | } 107 | 108 | double HighPassFilter(double val){ 109 | 110 | highx[Highn] = highx[Highn + 33] = val; 111 | highy0 = highy1 + highx[Highn] - highx[Highn + 32]; 112 | highy1 = highy0; 113 | if(--Highn < 0) 114 | Highn = 32; 115 | 116 | return highx[Highn + 16] - (highy0/32); 117 | } 118 | 119 | public double highPassNext(double upVal){ 120 | double mod = 0 ; 121 | 122 | for (int i = 0; i < highPassCoeff.length; i++) { 123 | mod = mod + upVal* highPassCoeff[i] ; 124 | } 125 | return mod ; 126 | } 127 | 128 | public double diffFilterNext(double upVal){ 129 | tempArrayX[0] = upVal ; 130 | 131 | double mod = 0 ; 132 | for (int i = 0; i < diffCoeff.length; i++) { 133 | mod = mod + tempArrayX[i] * diffCoeff[i] ; 134 | } 135 | return mod ; 136 | } 137 | 138 | public double squareNext(double upVal){ 139 | return Math.pow(upVal, 2) ; 140 | } 141 | 142 | 143 | 144 | public double movingWindowNext(double upVal){ 145 | double mod ; 146 | movingWindowQueue.add(upVal) ; 147 | movingWindowSum = 0 ; 148 | for (int i = 0; i < movingWindowQueue.size(); i++) { 149 | movingWindowSum = movingWindowSum + movingWindowQueue.get(i) ; 150 | } 151 | mod = movingWindowSum/movingWindowQueue.size() ; 152 | return mod; 153 | } 154 | 155 | 156 | 157 | } 158 | -------------------------------------------------------------------------------- /app/src/main/java/io/medicaldev/pantompkinstesting/MainActivity.java: -------------------------------------------------------------------------------- 1 | package io.medicaldev.pantompkinstesting; 2 | 3 | import android.graphics.Color; 4 | import android.os.Environment; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.os.Bundle; 7 | import android.util.Log; 8 | 9 | import com.github.mikephil.charting.charts.LineChart; 10 | import com.github.mikephil.charting.components.YAxis; 11 | import com.github.mikephil.charting.data.Entry; 12 | import com.github.mikephil.charting.data.LineData; 13 | import com.github.mikephil.charting.data.LineDataSet; 14 | import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; 15 | import com.github.mikephil.charting.utils.ColorTemplate; 16 | 17 | import java.io.BufferedReader; 18 | import java.io.FileNotFoundException; 19 | import java.io.FileReader; 20 | import java.io.IOException; 21 | import java.util.ArrayList; 22 | 23 | 24 | public class MainActivity extends AppCompatActivity { 25 | 26 | private static final String TAG = "Debug tag"; 27 | LineChart lineChart ; 28 | double lowVal, highVal, diffVal, sqVal, movingWindVal ; 29 | 30 | static int line_num = 0; 31 | 32 | Filter filter = new Filter() ; 33 | 34 | @Override 35 | protected void onCreate(Bundle savedInstanceState) { 36 | super.onCreate(savedInstanceState); 37 | setContentView(R.layout.activity_main); 38 | 39 | init() ; 40 | 41 | } 42 | 43 | private void init() { 44 | 45 | lineChart = (LineChart) findViewById(R.id.main_lineChart) ; 46 | lineChart.setViewPortOffsets(0, 20, 0, 0); 47 | 48 | lineChart.setData(new LineData()); 49 | lineChart.setData(new LineData()); 50 | lineChart.setBackgroundColor(Color.BLACK); 51 | lineChart.setDrawGridBackground(false); 52 | lineChart.getAxisLeft().setDrawGridLines(false); 53 | lineChart.getXAxis().setDrawGridLines(false); 54 | lineChart.getAxisRight().setDrawGridLines(false); 55 | YAxis yAxis = new YAxis() ; 56 | 57 | lineChart.getAxisLeft().setAxisMinValue(-20); 58 | 59 | lineChart.getAxis(yAxis.getAxisDependency()).setDrawGridLines(false); 60 | //lineChart.moveViewToX(1000); 61 | 62 | //lineChart.animateXY(100, 100) ; 63 | 64 | 65 | dataInit() ; 66 | 67 | feedMultiple(); 68 | 69 | } 70 | 71 | private void dataInit() { 72 | LineData data = lineChart.getData(); 73 | 74 | if (data != null) { 75 | 76 | ILineDataSet set = data.getDataSetByIndex(0); 77 | // set.addEntryA(...); // can be called as well 78 | 79 | if (set == null) { 80 | set = createSet(); 81 | data.addDataSet(set); 82 | } 83 | 84 | // add a new x-value first 85 | for (int i = 0; i < 601; i++) { 86 | data.addXValue(String.valueOf(data.getXValCount())); 87 | data.addEntry(new Entry(0, set.getEntryCount()), 0); 88 | } 89 | //Log.d(TAG, "addEntryA: " + val); 90 | 91 | 92 | lineChart.notifyDataSetChanged(); 93 | 94 | lineChart.setVisibleXRangeMaximum(600); 95 | lineChart.moveViewToX(data.getXValCount() - 601); 96 | } 97 | } 98 | 99 | private void feedMultiple() { 100 | 101 | new Thread(new Runnable() { 102 | 103 | @Override 104 | public void run() { 105 | readCSV(Environment.getExternalStorageDirectory() + "/data.csv"); 106 | } 107 | }).start(); 108 | } 109 | 110 | public void readCSV(String csvPath) { 111 | 112 | 113 | BufferedReader reader; 114 | try { 115 | reader = new BufferedReader(new FileReader(csvPath)); 116 | //reader.readLine();// 117 | String line = null;// 118 | 119 | 120 | while ((line = reader.readLine()) != null) { 121 | 122 | String item[] = line.split(","); 123 | 124 | line_num++; 125 | if(line_num%5 == 0){ 126 | continue ; 127 | } 128 | lowVal = filter.LowPassFilter(Double.parseDouble(item[0])) ; 129 | highVal = filter.HighPassFilter(lowVal) ; 130 | 131 | /*addEntryA(highVal);*/ 132 | /*highVal = filter.highPassNext(lowVal) ; */ 133 | diffVal = filter.diffFilterNext(highVal) ; 134 | sqVal = filter.squareNext(diffVal) ; 135 | movingWindVal = filter.movingWindowNext(sqVal) ; 136 | 137 | //tempEntryInit() ; 138 | //addEntry(movingWindVal, 0); 139 | //addEntry(highVal, 1); 140 | //addEntryA(item[0]); 141 | //addEntryA(movingWindVal, 1); 142 | addCustomEntry(item[0], movingWindVal); 143 | 144 | Log.d(TAG, "readCSV: " + "Line: " + line_num + " Value: " + item[0] + " lowVal: " + lowVal + " highVal: " + highVal + " diffVal: " + diffVal + " sqVal: " + sqVal + " movingWindowVal: " + movingWindVal); 145 | 146 | 147 | 148 | try { 149 | Thread.sleep(10); 150 | }catch (InterruptedException e) { 151 | e.printStackTrace(); 152 | } 153 | 154 | //System.out.println(dataAL.get(line_num)); 155 | } 156 | 157 | } catch (FileNotFoundException e) { 158 | // TODO Auto-generated catch block 159 | e.printStackTrace(); 160 | } catch (IOException e) { 161 | // TODO Auto-generated catch block 162 | e.printStackTrace(); 163 | } 164 | 165 | } 166 | 167 | 168 | private void tempEntryInit() { 169 | LineData data = lineChart.getData() ; 170 | 171 | 172 | 173 | if(data.getDataSetCount() < 2){ 174 | for (int i = data.getDataSetCount(); i < 2; i++) { 175 | ILineDataSet currentSet ; 176 | LineDataSet dataSet = new LineDataSet(null, "Row" + i) ; 177 | dataSet.setAxisDependency(YAxis.AxisDependency.LEFT); 178 | dataSet.setColor(getResources().getColor(R.color.colorPrimaryDark)); 179 | currentSet = dataSet ; 180 | data.addDataSet(currentSet); 181 | } 182 | } 183 | 184 | 185 | } 186 | 187 | private void addEntry(double val, int type){ 188 | LineData data = lineChart.getData() ; 189 | ILineDataSet set = data.getDataSetByIndex(type) ; 190 | 191 | Log.d(TAG, "addEntry: " + val + " Type: " + type + " EntryCount:" + set.getEntryCount()); 192 | 193 | 194 | data.addEntry(new Entry((float) val, set.getEntryCount()), type); 195 | 196 | lineChart.notifyDataSetChanged(); 197 | 198 | lineChart.setVisibleXRangeMaximum(600); 199 | lineChart.moveViewToX(data.getXValCount() - 601); 200 | } 201 | 202 | 203 | 204 | 205 | 206 | /** 207 | * ORIGINAL CODE TO BE USED 208 | * */ 209 | 210 | private void addEntryA(String val) { 211 | 212 | LineData data = lineChart.getData(); 213 | 214 | if (data != null) { 215 | 216 | ILineDataSet set = data.getDataSetByIndex(0); 217 | // set.addEntryA(...); // can be called as well 218 | 219 | if (set == null) { 220 | set = createSet(); 221 | data.addDataSet(set); 222 | } 223 | 224 | // add a new x-value first 225 | data.addXValue(String.valueOf(data.getXValCount())); 226 | //Log.d(TAG, "addEntryA: " + val); 227 | data.addEntry(new Entry(Float.parseFloat(val), set.getEntryCount()), 0); 228 | 229 | 230 | lineChart.notifyDataSetChanged(); 231 | 232 | lineChart.setVisibleXRangeMaximum(600); 233 | lineChart.moveViewToX(data.getXValCount() - 601); 234 | } 235 | } 236 | 237 | 238 | private void addEntryA(double val) { 239 | 240 | LineData data = lineChart.getData(); 241 | 242 | if (data != null) { 243 | 244 | ILineDataSet set = data.getDataSetByIndex(0); 245 | // set.addEntryA(...); // can be called as well 246 | 247 | if (set == null) { 248 | set = createSet(); 249 | data.addDataSet(set); 250 | } 251 | 252 | // add a new x-value first 253 | data.addXValue(String.valueOf(data.getXValCount())); 254 | //Log.d(TAG, "addEntryA: " + val); 255 | data.addEntry(new Entry((float)val, set.getEntryCount()), 0); 256 | 257 | 258 | lineChart.notifyDataSetChanged(); 259 | 260 | lineChart.setVisibleXRangeMaximum(600); 261 | lineChart.moveViewToX(data.getXValCount() - 601); 262 | } 263 | } 264 | 265 | private void addCustomEntry(String ecgVal, double movingVal) { 266 | 267 | LineData data = lineChart.getData(); 268 | 269 | if (data != null) { 270 | 271 | ILineDataSet set = data.getDataSetByIndex(0); 272 | ILineDataSet set2 = data.getDataSetByIndex(1) ; 273 | // set.addEntryA(...); // can be called as well 274 | 275 | if (set == null) { 276 | set = createSet(); 277 | data.addDataSet(set); 278 | } 279 | if (set2 == null) { 280 | set2 = createSetB() ; 281 | data.addDataSet(set2); 282 | } 283 | 284 | // add a new x-value first 285 | data.addXValue(String.valueOf(data.getXValCount())); 286 | Log.d(TAG, "addEntryA: " + ecgVal + " movWindVal: " + movingWindVal*200); 287 | data.addEntry(new Entry(Float.parseFloat(ecgVal), set.getEntryCount()), 0); 288 | data.addEntry(new Entry((float) movingVal, set.getEntryCount()), 1); 289 | 290 | 291 | lineChart.notifyDataSetChanged(); 292 | 293 | lineChart.setVisibleXRangeMaximum(600); 294 | lineChart.moveViewToX(data.getXValCount() - 601); 295 | } 296 | } 297 | 298 | 299 | private void addEntryA(double val, int type) { 300 | 301 | LineData data = lineChart.getData(); 302 | 303 | if (data != null) { 304 | 305 | ILineDataSet set = data.getDataSetByIndex(type); 306 | // set.addEntryA(...); // can be called as well 307 | 308 | if (set == null) { 309 | set = createSetB(); 310 | data.addDataSet(set); 311 | } 312 | 313 | // add a new x-value first 314 | data.addXValue(String.valueOf(data.getXValCount())); 315 | Log.d(TAG, "addEntryA: " + val*200 ); 316 | if(val*200 > 1000){ 317 | data.addEntry(new Entry((float) val*200, set.getEntryCount()), type); 318 | }else { 319 | data.addEntry(new Entry( 0, set.getEntryCount()), type); 320 | } 321 | 322 | 323 | lineChart.notifyDataSetChanged(); 324 | 325 | lineChart.setVisibleXRangeMaximum(600); 326 | lineChart.moveViewToX(data.getXValCount() - 1201); 327 | } 328 | } 329 | 330 | private void addEntryB(double val) { 331 | 332 | LineData data = lineChart.getData(); 333 | 334 | if (data != null) { 335 | 336 | ILineDataSet set = data.getDataSetByIndex(1); 337 | // set.addEntryA(...); // can be called as well 338 | 339 | if (set == null) { 340 | set = createSetB(); 341 | data.addDataSet(set); 342 | } 343 | 344 | // add a new x-value first 345 | data.addXValue(String.valueOf(data.getXValCount())); 346 | data.addEntry(new Entry((float) val, set.getEntryCount()), 0); 347 | 348 | 349 | lineChart.notifyDataSetChanged(); 350 | 351 | lineChart.setVisibleXRangeMaximum(600); 352 | lineChart.moveViewToX(data.getXValCount() - 601); 353 | } 354 | } 355 | 356 | 357 | 358 | private LineDataSet createSet() { 359 | 360 | LineDataSet set = new LineDataSet(null, "Dynamic Data"); 361 | set.setAxisDependency(YAxis.AxisDependency.LEFT); 362 | set.setColor(getResources().getColor(R.color.colorPrimaryDark)); 363 | set.setCircleColor(Color.WHITE); 364 | set.setLineWidth(2f); 365 | set.setCircleRadius(0.1f); 366 | set.setFillAlpha(65); 367 | set.setFillColor(ColorTemplate.getHoloBlue()); 368 | set.setHighLightColor(Color.rgb(244, 117, 117)); 369 | set.setValueTextSize(9f); 370 | set.setDrawValues(false); 371 | return set; 372 | } 373 | 374 | private LineDataSet createSetB(){ 375 | LineDataSet setB = new LineDataSet(null, "Next Level Vals"); 376 | setB.setAxisDependency(YAxis.AxisDependency.LEFT); 377 | setB.setColor(getResources().getColor(R.color.colorAccent)); 378 | setB.setCircleColor(Color.BLACK); 379 | setB.setLineWidth(2f); 380 | setB.setCircleRadius(0.1f); 381 | setB.setFillAlpha(65); 382 | setB.setFillColor(ColorTemplate.getHoloBlue()); 383 | setB.setHighLightColor(Color.rgb(244, 117, 117)); 384 | setB.setValueTextSize(9f); 385 | setB.setDrawValues(false); 386 | return setB; 387 | } 388 | } 389 | -------------------------------------------------------------------------------- /app/src/main/java/io/medicaldev/pantompkinstesting/HeartyFilter.java: -------------------------------------------------------------------------------- 1 | package io.medicaldev.pantompkinstesting; 2 | 3 | /** 4 | * HeartyFilter.java 5 | * Copyright (C) 2012 Pattern Recognition Lab, University Erlangen-Nuremberg. 6 | * 7 | * Licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * To view a copy of this License, visit http://creativecommons.org/licenses/by-nc-sa/3.0/. 10 | * 11 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 12 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 13 | * specific language governing permissions and limitations under the License. 14 | * 15 | * 16 | * This file is part of the "Hearty" Android Application. It was released as supplementary material related to the publication [1]: 17 | * [1] S. Gradl, P. Kugler, C. Lohm¸ller, and B. Eskofier, ìReal-time ECG monitoring and arrhythmia detection using Android-based mobile devices,î in 34th Annual International Conference of the IEEE EMBS, 2012, pp. 2452ñ2455. 18 | * 19 | * It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 20 | * If you reuse this code you have to keep or cite this comment. 21 | * 22 | */ 23 | 24 | import java.security.InvalidParameterException; 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | /** 29 | * Implementation of a filtering algorithm using numerator and denominator 30 | * coefficients. 31 | * 32 | * @author Stefan Gradl 33 | */ 34 | public class HeartyFilter { 35 | protected double a[] = null; 36 | protected double b[] = null; 37 | public double y[] = null; 38 | public double x[] = null; 39 | 40 | protected HeartyFilter() { 41 | } 42 | 43 | /** 44 | * @param b_taps 45 | * numerator coefficients 46 | * @param a_taps 47 | * denominator coefficients, can be null. if not null, a[0] must 48 | * not be 0 or an {@link InvalidParameterException} will be 49 | * thrown. 50 | */ 51 | public HeartyFilter(double[] b_taps, double[] a_taps) { 52 | // make sure the coefficients are valid 53 | if (b_taps == null || b_taps.length < 1 54 | || (b_taps.length == 1 && b_taps[0] == 0) 55 | || (a_taps != null && a_taps[0] == 0)) { 56 | throw new InvalidParameterException(); 57 | } 58 | 59 | // copy denominators 60 | if (a_taps == null) { 61 | a = new double[1]; 62 | a[0] = 1d; 63 | } else { 64 | a = new double[a_taps.length]; 65 | System.arraycopy(a_taps, 0, a, 0, a_taps.length); 66 | } 67 | 68 | // copy numerators 69 | b = new double[b_taps.length]; 70 | System.arraycopy(b_taps, 0, b, 0, b_taps.length); 71 | 72 | // create x & y arrays 73 | y = new double[a_taps.length]; 74 | x = new double[b_taps.length]; 75 | } 76 | 77 | public HeartyFilter(double b0, double b1, double b2, double b3, double b4, 78 | double a0, double a1, double a2) { 79 | if (a0 == 0f) { 80 | throw new InvalidParameterException(); 81 | } 82 | 83 | a = new double[3]; 84 | a[0] = a0; 85 | a[1] = a1; 86 | a[2] = a2; 87 | 88 | b = new double[5]; 89 | b[0] = b0; 90 | b[1] = b1; 91 | b[2] = b2; 92 | b[3] = b3; 93 | b[4] = b4; 94 | 95 | // create x & y arrays 96 | y = new double[a.length]; 97 | x = new double[b.length]; 98 | } 99 | 100 | private transient int t_iter = 0; 101 | 102 | /** 103 | * Performs the filtering operation for the next x value. 104 | * 105 | * @param xnow 106 | * x[n] 107 | * @return y[n] 108 | */ 109 | public double next(double xnow) { 110 | if (b.length > 1) 111 | System.arraycopy(x, 0, x, 1, b.length - 1); 112 | x[0] = xnow; 113 | 114 | // shift y 115 | if (a.length > 1) 116 | System.arraycopy(y, 0, y, 1, a.length - 1); 117 | y[0] = 0d; 118 | 119 | // sum( b[n] * x[N-n] ) 120 | for (t_iter = 0; t_iter < b.length; ++t_iter) { 121 | y[0] += b[t_iter] * x[t_iter]; 122 | } 123 | 124 | // sum( a[n] * y[N-n] ) 125 | for (t_iter = 1; t_iter < a.length; ++t_iter) { 126 | y[0] += a[t_iter] * y[t_iter]; 127 | } 128 | 129 | // a0 130 | if (a[0] != 1d) { 131 | y[0] /= a[0]; 132 | } 133 | 134 | return y[0]; 135 | } 136 | 137 | /** 138 | * @return The current y[0] value from last calculation step 139 | */ 140 | public double current() { 141 | return y[0]; 142 | } 143 | 144 | /** 145 | * Implements running mean filter. 146 | * 147 | * y[n] = 1/(N+1) * ( y[n-1] * N + x[n] ) 148 | * 149 | * @author sistgrad 150 | * 151 | */ 152 | public static class MeanFilter extends HeartyFilter { 153 | public int num = 0; 154 | public int maxNum = 0; 155 | 156 | public MeanFilter() { 157 | a = new double[2]; 158 | a[0] = 0f; 159 | 160 | b = new double[2]; 161 | 162 | // create x & y arrays 163 | y = new double[a.length]; 164 | x = new double[b.length]; 165 | } 166 | 167 | /** 168 | * Stop increasing the num counter at maxNum values. 169 | * 170 | * @param maxNum 171 | */ 172 | public MeanFilter(int maxNum) { 173 | a = new double[2]; 174 | a[0] = 0f; 175 | 176 | b = new double[2]; 177 | 178 | // create x & y arrays 179 | y = new double[a.length]; 180 | x = new double[b.length]; 181 | 182 | this.maxNum = maxNum; 183 | } 184 | 185 | /* 186 | * (non-Javadoc) 187 | * 188 | * @see de.lme.plotview.HeartyFilter#next(double) 189 | */ 190 | @Override 191 | public double next(double xnow) { 192 | y[1] = y[0]; 193 | y[0] = (y[1] * num + xnow) / (num + 1); 194 | if (maxNum == 0 || num < maxNum) 195 | ++num; 196 | return y[0]; 197 | } 198 | } 199 | 200 | /** 201 | * Represents a statistical object tp keep track of running mean, min and 202 | * max values. 203 | * 204 | * @author sistgrad 205 | * 206 | */ 207 | public static class StatFilter extends HeartyFilter { 208 | protected MeanFilter meanFilter = null; 209 | 210 | public double mean = 0; 211 | public double min = Double.MAX_VALUE; 212 | public double max = Double.MIN_VALUE; 213 | public double range = 0; 214 | public double value = 0; 215 | 216 | public StatFilter() { 217 | meanFilter = new MeanFilter(16); 218 | } 219 | 220 | /** 221 | * Stop increasing the num counter at maxNum values. 222 | * 223 | * @param maxNum 224 | */ 225 | public StatFilter(int maxNum) { 226 | meanFilter = new MeanFilter(maxNum); 227 | } 228 | 229 | /* 230 | * (non-Javadoc) 231 | * 232 | * @see de.lme.plotview.HeartyFilter#next(double) 233 | */ 234 | @Override 235 | public double next(double xnow) { 236 | mean = meanFilter.next(xnow); 237 | value = xnow; 238 | 239 | if (xnow > max) { 240 | max = xnow; 241 | range = max - min; 242 | } 243 | 244 | if (xnow < min) { 245 | min = xnow; 246 | range = max - min; 247 | } 248 | 249 | return value; 250 | } 251 | 252 | public String formatValue() { 253 | return String.format("%.0f", value); 254 | } 255 | 256 | public String formatMean() { 257 | return String.format("%.0f", mean); 258 | } 259 | 260 | public String formatMin() { 261 | return String.format("%.0f", min); 262 | } 263 | 264 | public String formatMax() { 265 | return String.format("%.0f", max); 266 | } 267 | } 268 | 269 | /** 270 | * Implements the von-Hann filter using the last 3 values. 271 | * 272 | * y[n] = 1/4 * ( x[n] + 2 * x[n-1] + x[n-2] ) 273 | * 274 | * @author sistgrad 275 | * 276 | */ 277 | public static class HannFilter extends HeartyFilter { 278 | public HannFilter() { 279 | a = new double[1]; 280 | a[0] = 1f; 281 | 282 | b = new double[3]; 283 | b[0] = b[2] = 0.25f; 284 | b[1] = 0.5f; 285 | 286 | // create x & y arrays 287 | y = new double[a.length]; 288 | x = new double[b.length]; 289 | } 290 | 291 | /* 292 | * (non-Javadoc) 293 | * 294 | * @see de.lme.plotview.HeartyFilter#next(double) 295 | */ 296 | @Override 297 | public double next(double xnow) { 298 | // performance override 299 | x[2] = x[1]; 300 | x[1] = x[0]; 301 | x[0] = xnow; 302 | return (0.25f * x[0] + 0.5f * x[1] + 0.25f * x[2]); 303 | } 304 | } 305 | 306 | /** 307 | * Implements a peak detection filter. 308 | * 309 | * next() will always return the peak-decision for the central value of 310 | * minRange. If minRange == 3 then the third call to next() returns the 311 | * peak-decision for the previous value. The very first and second call ever 312 | * made to next() after creating the object will, in this exemplary case, 313 | * always return Double.NaN. 314 | * 315 | * 316 | * @author sistgrad 317 | * 318 | */ 319 | public static class PeakDetectionFilter extends HeartyFilter { 320 | protected int minRange; 321 | protected double minDiff; 322 | public int peakIdx; 323 | public double peakValue = Double.NaN; 324 | protected int block = 1; 325 | 326 | /** 327 | * @param minRange 328 | * range of surrounding values to test against for peak 329 | * evaluation (must be >= 1) 330 | * @param minDiff 331 | * minimal (absolute) difference between two values to be 332 | * considered different 333 | */ 334 | public PeakDetectionFilter(int minRange, double minDiff) { 335 | if (minRange > 0) 336 | this.minRange = minRange; 337 | else 338 | this.minRange = 1; 339 | 340 | this.minDiff = Math.abs(minDiff); 341 | 342 | // create x & y arrays 343 | y = new double[1]; 344 | x = new double[minRange << 1 + 1]; 345 | 346 | peakIdx = minRange; 347 | 348 | block = x.length; 349 | } 350 | 351 | /** 352 | * 353 | */ 354 | public PeakDetectionFilter() { 355 | this.minRange = 1; 356 | this.minDiff = 0f; 357 | 358 | // create x & y arrays 359 | y = new double[1]; 360 | x = new double[3]; 361 | 362 | peakIdx = 1; 363 | block = 3; 364 | } 365 | 366 | /** 367 | * resets blocking to reuse the filter 368 | */ 369 | public void reset() { 370 | block = x.length; 371 | } 372 | 373 | protected transient int _i; 374 | 375 | /** 376 | * @return Double.NaN, if not part of a peak, peakIdx will also be set 377 | * to -1. Or the value of the peak, if it IS part of a peak. 378 | * this.peakIdx will contain the current index of said peak. 379 | */ 380 | @Override 381 | public double next(double xnow) { 382 | System.arraycopy(x, 0, x, 1, x.length - 1); 383 | x[0] = xnow; 384 | 385 | peakValue = Double.NaN; 386 | peakIdx = -1; 387 | 388 | // block until the buffer is filled entirely 389 | if (block > 0) { 390 | --block; 391 | return Double.NaN; 392 | } 393 | 394 | for (_i = 1; _i <= minRange; ++_i) { 395 | // values before mid-value 396 | if (x[minRange] - minDiff <= x[minRange + _i]) 397 | return Double.NaN; 398 | 399 | // values after mid-value 400 | if (x[minRange] - minDiff < x[minRange - _i]) 401 | return Double.NaN; 402 | } 403 | 404 | // value IS part of a peak 405 | peakValue = x[minRange]; 406 | peakIdx = minRange; 407 | 408 | return peakValue; 409 | } 410 | } 411 | 412 | /** 413 | * Implements a minimum detection filter. 414 | * 415 | * next() will always return the peak-decision for the central value of 416 | * minRange. If minRange == 3 then the third call to next() returns the 417 | * peak-decision for the previous value. The very first and second call ever 418 | * made to next() after creating the object will, in this exemplary case, 419 | * always return Double.NaN. 420 | * 421 | * 422 | * @author sistgrad 423 | * 424 | */ 425 | public static class MinDetectionFilter extends PeakDetectionFilter { 426 | 427 | /** 428 | * 429 | */ 430 | public MinDetectionFilter() { 431 | super(); 432 | // TODO Auto-generated constructor stub 433 | } 434 | 435 | /** 436 | * @param minRange 437 | * @param minDiff 438 | */ 439 | public MinDetectionFilter(int minRange, double minDiff) { 440 | super(minRange, minDiff); 441 | // TODO Auto-generated constructor stub 442 | } 443 | 444 | @Override 445 | public double next(double xnow) { 446 | System.arraycopy(x, 0, x, 1, x.length - 1); 447 | x[0] = xnow; 448 | 449 | peakValue = Double.NaN; 450 | peakIdx = -1; 451 | 452 | // block until the buffer is filled entirely 453 | if (block > 0) { 454 | --block; 455 | return Double.NaN; 456 | } 457 | 458 | for (_i = 1; _i <= minRange; ++_i) { 459 | // values before mid-value 460 | if (x[minRange] - minDiff >= x[minRange + _i]) 461 | return Double.NaN; 462 | 463 | // values after mid-value 464 | if (x[minRange] - minDiff > x[minRange - _i]) 465 | return Double.NaN; 466 | } 467 | 468 | // value IS part of a peak 469 | peakValue = x[minRange]; 470 | peakIdx = minRange; 471 | 472 | return peakValue; 473 | } 474 | } 475 | 476 | /** 477 | * Implements the Savitzky-Golay filter using 5 points. 478 | * 479 | * y[n] = ... 480 | * 481 | * @author sistgrad 482 | * 483 | */ 484 | public static class SavGolayFilter extends HeartyFilter { 485 | /** 486 | * @param sg_order 487 | * The Savitzky-Golay order to use. 488 | */ 489 | public SavGolayFilter(int sg_order) { 490 | a = new double[1]; 491 | a[0] = 1f; 492 | 493 | if (sg_order <= 1) { 494 | b = new double[5]; 495 | b[0] = -0.0857f; 496 | b[1] = 0.3429f; 497 | b[2] = 0.4857f; 498 | b[3] = 0.3429f; 499 | b[4] = -0.0857f; 500 | } else if (sg_order == 2) { 501 | b = new double[7]; 502 | b[0] = -0.095238f; 503 | b[1] = 0.1428571f; 504 | b[2] = 0.285714f; 505 | b[3] = 0.33333f; 506 | b[4] = 0.285714f; 507 | b[5] = 0.1428571f; 508 | b[6] = -0.095238f; 509 | } else 510 | throw new InvalidParameterException(); 511 | 512 | // create x & y arrays 513 | y = new double[a.length]; 514 | x = new double[b.length]; 515 | } 516 | 517 | /* 518 | * (non-Javadoc) 519 | * 520 | * @see de.lme.plotview.HeartyFilter#next(double) 521 | */ 522 | @Override 523 | public double next(double xnow) { 524 | // performance override 525 | if (b.length == 7) { 526 | // TODO: check at what point System.arraycopy is worth the call! 527 | // Is it inlined? 528 | x[6] = x[5]; 529 | x[5] = x[4]; 530 | x[4] = x[3]; 531 | x[3] = x[2]; 532 | x[2] = x[1]; 533 | x[1] = x[0]; 534 | x[0] = xnow; 535 | y[0] = (b[0] * x[0] + b[1] * x[1] + b[2] * x[2] + b[3] * x[3] 536 | + b[4] * x[4] + b[5] * x[5] + b[6] * x[6]); 537 | return y[0]; 538 | } else { 539 | x[4] = x[3]; 540 | x[3] = x[2]; 541 | x[2] = x[1]; 542 | x[1] = x[0]; 543 | x[0] = xnow; 544 | y[0] = (b[0] * x[0] + b[1] * x[1] + b[2] * x[2] + b[3] * x[3] + b[4] 545 | * x[4]); 546 | return y[0]; 547 | } 548 | } 549 | } 550 | 551 | /** 552 | * Implements a moving window integrator filter. 553 | * 554 | * y[n] = 1/N * ( x[n] + x[n-1] + x[n-2] + ... ) 555 | * 556 | * @author sistgrad 557 | * 558 | */ 559 | public static class WndIntFilter extends HeartyFilter { 560 | protected List m_int = null; 561 | 562 | public WndIntFilter(int wndLength) { 563 | a = new double[1]; 564 | a[0] = wndLength; 565 | 566 | b = new double[1]; 567 | 568 | // create x & y arrays 569 | y = new double[a.length]; 570 | x = new double[b.length]; 571 | 572 | m_int = new ArrayList(); 573 | } 574 | 575 | /* 576 | * (non-Javadoc) 577 | * 578 | * @see de.lme.plotview.HeartyFilter#next(double) 579 | */ 580 | @Override 581 | public double next(double xnow) { 582 | m_int.add((float) xnow); 583 | y[0] = getMean(m_int); 584 | return y[0]; 585 | } 586 | 587 | private float getMean(List m_int) { 588 | float mod = 0 ; 589 | for (int i = 0; i < m_int.size(); i++) { 590 | mod = mod + Float.parseFloat(m_int.get(i).toString()) ; 591 | } 592 | return mod; 593 | } 594 | } 595 | 596 | /** 597 | * Implements a moving window accumulation filter. 598 | * 599 | * y[n] = ( x[n] + x[n-1] + x[n-2] + ... ) 600 | * 601 | * @author sistgrad 602 | * 603 | */ 604 | public static class AccuFilter extends WndIntFilter { 605 | /** 606 | * @param wndLength 607 | */ 608 | public AccuFilter(int wndLength) { 609 | super(wndLength); 610 | } 611 | 612 | /* 613 | * (non-Javadoc) 614 | * 615 | * @see de.lme.plotview.HeartyFilter#next(double) 616 | */ 617 | @Override 618 | public double next(double xnow) { 619 | m_int.add((float) xnow); 620 | 621 | y[0] = sumVal(m_int); 622 | 623 | return y[0]; 624 | } 625 | 626 | private double sumVal(List m_int) { 627 | double sum = 0 ; 628 | for (int i = 0; i < m_int.size(); i++) { 629 | sum += m_int.get(i) ; 630 | } 631 | return sum ; 632 | } 633 | } 634 | 635 | /** 636 | * Implements the first order derivative filter. 637 | * 638 | * y[n] = 1/T * ( x[n] - x[n-1] ) 639 | * 640 | * @author sistgrad 641 | * 642 | */ 643 | public static class FirstDerivativeFilter extends HeartyFilter { 644 | public FirstDerivativeFilter(double T) { 645 | a = new double[1]; 646 | a[0] = 1 / T; 647 | 648 | b = new double[2]; 649 | b[0] = 1f; 650 | b[1] = -1f; 651 | 652 | // create x & y arrays 653 | y = new double[a.length]; 654 | x = new double[b.length]; 655 | } 656 | 657 | /* 658 | * (non-Javadoc) 659 | * 660 | * @see de.lme.plotview.HeartyFilter#next(double) 661 | */ 662 | @Override 663 | public double next(double xnow) { 664 | // performance override 665 | x[1] = x[0]; 666 | x[0] = xnow; 667 | 668 | return a[0] * (x[0] - x[1]); 669 | } 670 | } 671 | 672 | /** 673 | * Implements the second order derivative filter. 674 | * 675 | * y[n] = 1/T≤ * ( x[n] - 2 * x[n-1] + x[n-2] ) 676 | * 677 | * @author sistgrad 678 | * 679 | */ 680 | public static class SecondDerivativeFilter extends HeartyFilter { 681 | public SecondDerivativeFilter(double T) { 682 | a = new double[1]; 683 | a[0] = 1 / (T * T); 684 | 685 | b = new double[3]; 686 | b[0] = 1f; 687 | b[1] = -2f; 688 | b[2] = 1f; 689 | 690 | // create x & y arrays 691 | y = new double[a.length]; 692 | x = new double[b.length]; 693 | } 694 | 695 | /* 696 | * (non-Javadoc) 697 | * 698 | * @see de.lme.plotview.HeartyFilter#next(double) 699 | */ 700 | @Override 701 | public double next(double xnow) { 702 | // performance override 703 | x[2] = x[1]; 704 | x[1] = x[0]; 705 | x[0] = xnow; 706 | return a[0] * (x[0] + 2 * x[1] + x[2]); 707 | } 708 | } 709 | 710 | /** 711 | * Implements the three point central difference filter. 712 | * 713 | * y[n] = 1/2T * ( x[n] - x[n-2] ) 714 | * 715 | * @author sistgrad 716 | * 717 | */ 718 | public static class TpcdFilter extends HeartyFilter { 719 | public TpcdFilter(double T) { 720 | a = new double[1]; 721 | a[0] = 1 / (2 * T); 722 | 723 | b = new double[3]; 724 | b[0] = 1f; 725 | b[1] = 0f; 726 | b[2] = -1f; 727 | 728 | // create x & y arrays 729 | y = new double[a.length]; 730 | x = new double[b.length]; 731 | } 732 | 733 | /* 734 | * (non-Javadoc) 735 | * 736 | * @see de.lme.plotview.HeartyFilter#next(double) 737 | */ 738 | @Override 739 | public double next(double xnow) { 740 | // performance override 741 | x[2] = x[1]; 742 | x[1] = x[0]; 743 | x[0] = xnow; 744 | 745 | return a[0] * (x[0] - x[2]); 746 | } 747 | } 748 | 749 | /** 750 | * Implements an improved derivative filter. 751 | * 752 | * y[n] = 1/T * ( x[n] - x[n-1] ) + (1 - T) * y[n-1] 753 | * 754 | * @author sistgrad 755 | * 756 | */ 757 | public static class ImpDerivativeFilter extends HeartyFilter { 758 | public ImpDerivativeFilter(double T, Double initValue) { 759 | a = new double[2]; 760 | a[0] = 1 / T; 761 | a[1] = 1 - T; 762 | 763 | b = new double[2]; 764 | b[0] = b[1] = 1f; 765 | 766 | // create x & y arrays 767 | y = new double[a.length]; 768 | x = new double[b.length]; 769 | 770 | if (initValue != null) { 771 | x[0] = initValue.floatValue(); 772 | } 773 | } 774 | 775 | /* 776 | * (non-Javadoc) 777 | * 778 | * @see de.lme.plotview.HeartyFilter#next(double) 779 | */ 780 | @Override 781 | public double next(double xnow) { 782 | // performance override 783 | x[1] = x[0]; 784 | x[0] = xnow; 785 | 786 | y[1] = y[0]; 787 | y[0] = a[0] * (x[0] - x[1]) + a[1] * y[1]; 788 | 789 | return y[0]; 790 | } 791 | } 792 | 793 | /** 794 | * Implements the Butterworth filter. 795 | * 796 | * y[n] = sum( b[k] * x[n-k] ) - sum( a[k] * y[n-k] ) 797 | * 798 | * @author sistgrad 799 | * 800 | */ 801 | public static class ButterworthFilter extends HeartyFilter { 802 | // double b[] = { 0.046582f, 0.186332f, 0.279497f, 0.186332f, 0.046583f 803 | // }; 804 | // double a[] = { 1f, -0.776740f, 0.672706f, -0.180517f, 0.029763f }; 805 | } 806 | 807 | } 808 | 809 | --------------------------------------------------------------------------------