Skip to content

Commit aba3806

Browse files
authored
feat: added Automatic Measurements (#2494)
1 parent fd795ee commit aba3806

File tree

10 files changed

+291
-8
lines changed

10 files changed

+291
-8
lines changed

app/src/main/java/io/pslab/activity/OscilloscopeActivity.java

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
import androidx.appcompat.app.ActionBar;
3535
import androidx.appcompat.widget.Toolbar;
3636
import androidx.fragment.app.Fragment;
37+
import androidx.recyclerview.widget.DefaultItemAnimator;
38+
import androidx.recyclerview.widget.LinearLayoutManager;
39+
import androidx.recyclerview.widget.RecyclerView;
3740

3841
import com.github.mikephil.charting.charts.LineChart;
3942
import com.github.mikephil.charting.components.Legend;
@@ -61,6 +64,7 @@
6164
import butterknife.ButterKnife;
6265
import io.pslab.R;
6366
import io.pslab.activity.guide.GuideActivity;
67+
import io.pslab.adapters.OscilloscopeMeasurementsAdapter;
6468
import io.pslab.communication.AnalyticsClass;
6569
import io.pslab.communication.ScienceLab;
6670
import io.pslab.fragment.ChannelParametersFragment;
@@ -76,6 +80,7 @@
7680
import io.pslab.others.CustomSnackBar;
7781
import io.pslab.others.GPSLogger;
7882
import io.pslab.others.LocalDataLog;
83+
import io.pslab.others.OscilloscopeMeasurements;
7984
import io.pslab.others.Plot2D;
8085
import io.pslab.others.ScienceLabCommon;
8186
import io.realm.Realm;
@@ -164,6 +169,8 @@ public class OscilloscopeActivity extends GuideActivity implements View.OnClickL
164169
TextView xyPlotTextView;
165170
@BindView(R.id.parent_layout)
166171
View parentLayout;
172+
@BindView(R.id.recyclerView)
173+
RecyclerView measurementsList;
167174
private Fragment channelParametersFragment;
168175
private Fragment timebaseTriggerFragment;
169176
private Fragment dataAnalysisFragment;
@@ -184,6 +191,7 @@ public class OscilloscopeActivity extends GuideActivity implements View.OnClickL
184191
private double maxAmp, maxFreq;
185192
private boolean isRecording = false;
186193
private boolean isRunning = true;
194+
private boolean isMeasurementsChecked = false;
187195
private Realm realm;
188196
public RealmResults<OscilloscopeData> recordedOscilloscopeData;
189197
private CSVLogger csvLogger;
@@ -201,7 +209,7 @@ public class OscilloscopeActivity extends GuideActivity implements View.OnClickL
201209
private double lon;
202210
public boolean isPlaybackFourierChecked = false;
203211
private HashMap<String, Integer> channelIndexMap;
204-
private final Integer[] channelColors = {Color.CYAN, Color.GREEN, Color.WHITE, Color.MAGENTA};
212+
public static final Integer[] channelColors = {Color.CYAN, Color.GREEN, Color.WHITE, Color.MAGENTA};
205213
private final String[] loggingYdata = new String[4];
206214
public String xyPlotAxis1 = "CH1";
207215
public String xyPlotAxis2 = "CH2";
@@ -211,10 +219,12 @@ public class OscilloscopeActivity extends GuideActivity implements View.OnClickL
211219
private ArrayList<ArrayList<Entry>> dataEntries = new ArrayList<>();
212220
private String[] dataParamsChannels;
213221

214-
private enum CHANNEL {CH1, CH2, CH3, MIC}
222+
public enum CHANNEL {CH1, CH2, CH3, MIC}
215223

216224
private enum MODE {RISING, FALLING, DUAL}
217225

226+
public enum ChannelMeasurements {FREQUENCY, PERIOD, AMPLITUDE, POSITIVE_PEAK, NEGATIVE_PEAK}
227+
218228
public OscilloscopeActivity() {
219229
super(R.layout.activity_oscilloscope);
220230
}
@@ -325,6 +335,8 @@ public void onSystemUiVisibilityChange(int i) {
325335
dataAnalysisTextView.setOnClickListener(this);
326336
xyPlotTextView.setOnClickListener(this);
327337

338+
measurementsList = findViewById(R.id.recyclerView);
339+
328340
chartInit();
329341

330342
final Runnable runnable = new Runnable() {
@@ -624,6 +636,16 @@ public void onClick(View view) {
624636
autoScale();
625637
}
626638
break;
639+
case R.id.measurements:
640+
if (!isMeasurementsChecked) {
641+
isMeasurementsChecked = true;
642+
item.setChecked(true);
643+
measurementsList.setVisibility(View.VISIBLE);
644+
} else {
645+
isMeasurementsChecked = false;
646+
item.setChecked(false);
647+
measurementsList.setVisibility(View.INVISIBLE);
648+
}
627649
default:
628650
break;
629651
}
@@ -1214,6 +1236,43 @@ protected void onPostExecute(Void aVoid) {
12141236
}
12151237
}
12161238
}
1239+
1240+
if (!isFourierTransformSelected) {
1241+
for (int i = 0; i < Math.min(entries.size(), paramsChannels.length); i++) {
1242+
CHANNEL channel = CHANNEL.valueOf(paramsChannels[i]);
1243+
double minY = Double.MAX_VALUE;
1244+
double maxY = -1 * Double.MIN_VALUE;
1245+
double yRange;
1246+
double[] voltage = new double[512];
1247+
ArrayList<Entry> entryArrayList = dataEntries.get(i);
1248+
for (int j = 0; j < entryArrayList.size(); j++) {
1249+
Entry entry = entryArrayList.get(j);
1250+
if (j < voltage.length - 1) {
1251+
voltage[j] = entry.getY();
1252+
}
1253+
if (entry.getY() > maxY) {
1254+
maxY = entry.getY();
1255+
}
1256+
if (entry.getY() < minY) {
1257+
minY = entry.getY();
1258+
}
1259+
}
1260+
final double frequency;
1261+
if (Objects.equals(dataParamsChannels[i], CHANNEL.MIC.toString())) {
1262+
frequency = analyticsClass.findFrequency(voltage, ((double) 1 / SAMPLING_RATE));
1263+
} else {
1264+
frequency = analyticsClass.findFrequency(voltage, timeGap / 1000000.0);
1265+
}
1266+
double period = (1 / frequency) * 1000.0;
1267+
yRange = maxY - minY;
1268+
OscilloscopeMeasurements.channel.get(channel).put(ChannelMeasurements.FREQUENCY, frequency);
1269+
OscilloscopeMeasurements.channel.get(channel).put(ChannelMeasurements.PERIOD, period);
1270+
OscilloscopeMeasurements.channel.get(channel).put(ChannelMeasurements.AMPLITUDE, yRange);
1271+
OscilloscopeMeasurements.channel.get(channel).put(ChannelMeasurements.POSITIVE_PEAK, maxY);
1272+
OscilloscopeMeasurements.channel.get(channel).put(ChannelMeasurements.NEGATIVE_PEAK, minY);
1273+
}
1274+
}
1275+
12171276
for (int i = 0; i < Math.min(entries.size(), paramsChannels.length); i++) {
12181277
LineDataSet dataSet;
12191278
dataSet = new LineDataSet(entries.get(i), paramsChannels[i]);
@@ -1239,6 +1298,13 @@ protected void onPostExecute(Void aVoid) {
12391298
setLeftYAxisScale(yAxisScale, -1 * yAxisScale);
12401299
setRightYAxisScale(yAxisScale, -1 * yAxisScale);
12411300
}
1301+
if (isMeasurementsChecked) {
1302+
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(OscilloscopeActivity.this);
1303+
measurementsList.setItemAnimator(new DefaultItemAnimator());
1304+
measurementsList.setLayoutManager(layoutManager);
1305+
OscilloscopeMeasurementsAdapter adapter = new OscilloscopeMeasurementsAdapter(dataParamsChannels, channelColors);
1306+
measurementsList.setAdapter(adapter);
1307+
}
12421308
mChart.setData(data);
12431309
mChart.notifyDataSetChanged();
12441310
mChart.invalidate();
@@ -1272,9 +1338,9 @@ public void autoScale() {
12721338
}
12731339
final double frequency;
12741340
if (Objects.equals(dataParamsChannels[i], CHANNEL.MIC.toString())) {
1275-
frequency = analyticsClass.findFrequency(voltage, ((double) 1 / SAMPLING_RATE));
1341+
frequency = analyticsClass.findSignalFrequency(voltage, ((double) 1 / SAMPLING_RATE));
12761342
} else {
1277-
frequency = analyticsClass.findFrequency(voltage, timeGap / 1000000.0);
1343+
frequency = analyticsClass.findSignalFrequency(voltage, timeGap / 1000000.0);
12781344
}
12791345
double period = (1 / frequency) * 1000.0;
12801346
if (period > maxPeriod) {
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package io.pslab.adapters;
2+
3+
import android.view.LayoutInflater;
4+
import android.view.View;
5+
import android.view.ViewGroup;
6+
import android.widget.TextView;
7+
8+
import androidx.annotation.NonNull;
9+
import androidx.recyclerview.widget.RecyclerView;
10+
11+
import java.math.RoundingMode;
12+
import java.text.DecimalFormat;
13+
14+
import io.pslab.R;
15+
import io.pslab.activity.OscilloscopeActivity.ChannelMeasurements;
16+
import io.pslab.activity.OscilloscopeActivity.CHANNEL;
17+
import io.pslab.others.OscilloscopeMeasurements;
18+
19+
public class OscilloscopeMeasurementsAdapter extends RecyclerView.Adapter<OscilloscopeMeasurementsAdapter.ViewHolder> {
20+
21+
private final String[] channels;
22+
private final Integer[] channelColors;
23+
24+
public static class ViewHolder extends RecyclerView.ViewHolder {
25+
26+
private final TextView measurementsView;
27+
28+
public ViewHolder(@NonNull View itemView) {
29+
super(itemView);
30+
this.measurementsView = itemView.findViewById(R.id.textview_measurements);
31+
}
32+
33+
public void setMeasurements(@NonNull String channelString, @NonNull Integer channelColor) {
34+
CHANNEL channel = CHANNEL.valueOf(channelString);
35+
double frequency = OscilloscopeMeasurements.channel.get(channel).get(ChannelMeasurements.FREQUENCY);
36+
double amplitude = OscilloscopeMeasurements.channel.get(channel).get(ChannelMeasurements.AMPLITUDE);
37+
double period = OscilloscopeMeasurements.channel.get(channel).get(ChannelMeasurements.PERIOD);
38+
double positivePeak = OscilloscopeMeasurements.channel.get(channel).get(ChannelMeasurements.POSITIVE_PEAK);
39+
double negativePeak = OscilloscopeMeasurements.channel.get(channel).get(ChannelMeasurements.NEGATIVE_PEAK);
40+
DecimalFormat df = new DecimalFormat("#.##");
41+
df.setRoundingMode(RoundingMode.CEILING);
42+
if (frequency >= 1000) {
43+
frequency /= 1000;
44+
String string = "Vpp: " + df.format(amplitude) + " V\nVp+: " + df.format(positivePeak) + " V Vp-: " + df.format(negativePeak) + " V\nf: " + df.format(frequency) + " kHz P: " + df.format(period) + " ms";
45+
measurementsView.setTextColor(channelColor);
46+
measurementsView.setText(string);
47+
} else {
48+
String string = "Vpp: " + df.format(amplitude) + " V\nVp+: " + df.format(positivePeak) + " V Vp-: " + df.format(negativePeak) + " V\nf: " + df.format(frequency) + " Hz P: " + df.format(period) + " ms";
49+
measurementsView.setTextColor(channelColor);
50+
measurementsView.setText(string);
51+
}
52+
}
53+
}
54+
55+
public OscilloscopeMeasurementsAdapter(@NonNull String[] channels, @NonNull Integer[] channelColors) {
56+
this.channels = channels;
57+
this.channelColors = channelColors;
58+
}
59+
60+
@NonNull
61+
@Override
62+
public OscilloscopeMeasurementsAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
63+
View itemView = LayoutInflater.from(parent.getContext())
64+
.inflate(R.layout.measurement_item, parent, false);
65+
return new ViewHolder(itemView);
66+
}
67+
68+
@Override
69+
public void onBindViewHolder(@NonNull OscilloscopeMeasurementsAdapter.ViewHolder holder, int position) {
70+
holder.setMeasurements(channels[position], channelColors[position]);
71+
}
72+
73+
@Override
74+
public int getItemCount() {
75+
return channels.length;
76+
}
77+
}

app/src/main/java/io/pslab/communication/AnalyticsClass.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ public double[] squareFit(double[] xReal, double[] yReal) {
307307
return new double[]{returnAmplitude, returnFrequency, returnPhase, returnDC, returnOffset};
308308
}
309309

310-
public double findFrequency(double[] voltage, double samplingInterval) {
310+
public double findSignalFrequency(double[] voltage, double samplingInterval) {
311311
int voltageLength = voltage.length;
312312
double[] frequency;
313313
double[] amplitude;
@@ -339,6 +339,33 @@ public double findFrequency(double[] voltage, double samplingInterval) {
339339
}
340340
}
341341

342+
public double findFrequency(double[] voltage, double samplingInterval) {
343+
int voltageLength = voltage.length;
344+
double[] frequency;
345+
double[] amplitude;
346+
int index = 0;
347+
double max = 0;
348+
Complex[] complex;
349+
DescriptiveStatistics stats = new DescriptiveStatistics();
350+
for (int i = 0; i < voltageLength; i++)
351+
stats.addValue(voltage[i]);
352+
double voltageMean = stats.getMean();
353+
for (int i = 0; i < voltageLength; i++)
354+
voltage[i] = voltage[i] - voltageMean; // remove DC component
355+
frequency = Arrays.copyOfRange(fftFrequency(voltageLength, samplingInterval), 0, voltageLength / 2); // take only the +ive half of the frequncy array
356+
FastFourierTransformer fastFourierTransformer = new FastFourierTransformer(DftNormalization.STANDARD);
357+
complex = fastFourierTransformer.transform(voltage, TransformType.FORWARD);
358+
amplitude = new double[complex.length / 2];
359+
for (int i = 0; i < complex.length / 2; i++) { // take only the +ive half of the fft result
360+
amplitude[i] = complex[i].abs() / voltageLength;
361+
if (amplitude[i] > max) { // search for the tallest peak, the fundamental
362+
max = amplitude[i];
363+
index = i;
364+
}
365+
}
366+
return frequency[index];
367+
}
368+
342369
public ArrayList<double[]> amplitudeSpectrum(double[] voltage, int samplingInterval, int nHarmonics) {
343370
int voltageLength = voltage.length;
344371
Complex[] complex;

app/src/main/java/io/pslab/fragment/TimebaseTriggerFragment.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
124124
((OscilloscopeActivity) getActivity()).xAxisScale = 38.40;
125125
((OscilloscopeActivity) getActivity()).setXAxisScale(38.40);
126126
((OscilloscopeActivity) getActivity()).timebase = 38400;
127-
((OscilloscopeActivity) getActivity()).timebase = 1024;
127+
((OscilloscopeActivity) getActivity()).samples = 1024;
128128
((OscilloscopeActivity) getActivity()).timeGap = (2 * ((OscilloscopeActivity) getActivity()).timebase) / ((OscilloscopeActivity) getActivity()).samples;
129129
break;
130130
default:
@@ -203,7 +203,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
203203
((OscilloscopeActivity) getActivity()).xAxisScale = 38.40;
204204
((OscilloscopeActivity) getActivity()).setXAxisScale(38.40);
205205
((OscilloscopeActivity) getActivity()).timebase = 38400;
206-
((OscilloscopeActivity) getActivity()).timebase = 1024;
206+
((OscilloscopeActivity) getActivity()).samples = 1024;
207207
((OscilloscopeActivity) getActivity()).timeGap = (2 * ((OscilloscopeActivity) getActivity()).timebase) / ((OscilloscopeActivity) getActivity()).samples;
208208
break;
209209
case 7:
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package io.pslab.others;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
import io.pslab.activity.OscilloscopeActivity.ChannelMeasurements;
7+
import io.pslab.activity.OscilloscopeActivity.CHANNEL;
8+
9+
10+
public class OscilloscopeMeasurements {
11+
12+
public final static Map<CHANNEL, HashMap<ChannelMeasurements, Double>> channel = new HashMap<>();
13+
14+
static {
15+
channel.put(CHANNEL.CH1, new HashMap<ChannelMeasurements, Double>() {{
16+
put(ChannelMeasurements.FREQUENCY, 0.00);
17+
put(ChannelMeasurements.PERIOD, 0.00);
18+
put(ChannelMeasurements.AMPLITUDE, 0.00);
19+
put(ChannelMeasurements.POSITIVE_PEAK, 0.00);
20+
put(ChannelMeasurements.NEGATIVE_PEAK, 0.00);
21+
}});
22+
23+
channel.put(CHANNEL.CH2, new HashMap<ChannelMeasurements, Double>() {{
24+
put(ChannelMeasurements.FREQUENCY, 0.00);
25+
put(ChannelMeasurements.PERIOD, 0.00);
26+
put(ChannelMeasurements.AMPLITUDE, 0.00);
27+
put(ChannelMeasurements.POSITIVE_PEAK, 0.00);
28+
put(ChannelMeasurements.NEGATIVE_PEAK, 0.00);
29+
}});
30+
31+
channel.put(CHANNEL.CH3, new HashMap<ChannelMeasurements, Double>() {{
32+
put(ChannelMeasurements.FREQUENCY, 0.00);
33+
put(ChannelMeasurements.PERIOD, 0.00);
34+
put(ChannelMeasurements.AMPLITUDE, 0.00);
35+
put(ChannelMeasurements.POSITIVE_PEAK, 0.00);
36+
put(ChannelMeasurements.NEGATIVE_PEAK, 0.00);
37+
}});
38+
39+
channel.put(CHANNEL.MIC, new HashMap<ChannelMeasurements, Double>() {{
40+
put(ChannelMeasurements.FREQUENCY, 0.00);
41+
put(ChannelMeasurements.PERIOD, 0.00);
42+
put(ChannelMeasurements.AMPLITUDE, 0.00);
43+
put(ChannelMeasurements.POSITIVE_PEAK, 0.00);
44+
put(ChannelMeasurements.NEGATIVE_PEAK, 0.00);
45+
}});
46+
}
47+
}

app/src/main/res/layout/activity_oscilloscope.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,15 @@
316316
android:layout_marginBottom="@dimen/osc_main_margin"
317317
android:layout_toStartOf="@+id/layout_dock_os1" />
318318

319+
<androidx.recyclerview.widget.RecyclerView
320+
android:id="@+id/recyclerView"
321+
android:layout_width="wrap_content"
322+
android:layout_height="wrap_content"
323+
android:layout_alignTop="@id/layout_chart_os"
324+
android:layout_alignEnd="@id/layout_chart_os"
325+
android:layout_marginEnd="@dimen/osc_recyclerview_padding"
326+
android:background="@color/black" />
327+
319328
</RelativeLayout>
320329

321330
<View

0 commit comments

Comments
 (0)