├── 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 |
5 |
6 |
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 |
5 |
6 |
7 |
8 |
9 |
10 |
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 |
7 |
8 |
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 |
--------------------------------------------------------------------------------