I'm working with a basic example of FFT with Apache Commons library. I have two classes:
public class Fourier {
private static XYSeries data;
private static XYSeriesCollection collection;
Fourier(){
collection =new XYSeriesCollection();
createSquare();
createFourier();
showGraph();
}
private static void createSquare(){
data=new XYSeries("Dati");
for(double i=-8;i<8;i+=1d/128){
data.add(i,((i<-4||(i<4&&i>0)?1:0)));
//data.add(i,(i<0?i+1:-i+1));
}
collection.addSeries(data);
}
private static void createFourier(){
double[] arrayFourier= new double[data.getItemCount()];
for(int i=0;i<data.getItemCount();i++){
arrayFourier[i]=data.getDataItem(i).getYValue();
}
FastFourierTransformer transformer=new FastFourierTransformer(DftNormalization.STANDARD);
Complex[] coeff=transformer.transform(arrayFourier, TransformType.INVERSE);
double norm = 0;
for(Complex Z: coeff){
System.out.println(Z.abs()+"\t"+Z.toString());
norm+=(Z.abs())*(Z.abs());
}
System.out.println(norm);
XYSeries fourier=new XYSeries("Fourier");
FourierSeries series=new FourierSeries(coeff,8);
for(double i=data.getMinX();i<data.getMaxX();i+=0.05){
fourier.add(i,series.getSeries(i));
}
collection.addSeries(fourier);
}
private static void showGraph(){
JFreeChart chart = ChartFactory.createXYLineChart("Fourier", "x", "f(x)", collection, PlotOrientation.VERTICAL, true, false, false);
ChartFrame window=new ChartFrame("Fourier", chart, false);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.pack();
window.setVisible(true);
}
public static void main(String[] args) {
Thread t=new Thread(new Runnable() {
#Override
public void run() {
new Fourier();
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.exit(0);
}
});
t.start();
}
}
and another one
public class FourierSeries {
Complex[] coeff;
double T;
public FourierSeries(Complex[] coeff, double T) {
this.coeff=coeff;
this.T=T;
}
public double getSeries(double x){
double k=Math.PI/T;
double value=0; //primo coefficiente
int i=0;
/*for(Complex iter:coeff){
if (i!=0&&i<coeff.length/2.) {
if (i % 2 == 0) {
value += iter.abs() * Math.cos(k * i * x);
System.out.println(iter.abs()+"cos"+i);
} else {
value += iter.abs() * Math.sin(k * i * x); //npari (i dispari) modulo*cos()
System.out.println(iter.abs()+"sin"+i);
}
}
i++;
}*/
for (Complex iter : coeff) {
if(i<coeff.length/2.)
value+=iter.getReal()*Math.cos(x*k*i)+iter.getImaginary()*Math.sin(x*k*i);
i++;
}
return value;
}
}
I introduce in my createSquare() method a function (square wave).
In order I do the following actions:
I perform the FFT.
Can you explain me how the array returned by transform.transform(array, TransformType) is composed?
Why the Fourier series is not correctly drawn?
The inverse series is correctly a square wave with same frequency as initial wave, but not correctly scaled on y-axsis. What is wrong? This is the output:
In transforming the complex form of the Fourier series to the real form, you have to treat the constant term differently. Or in the case of the code in question, you have to correct the non-constant coefficients by a factor of 2. So that you get 0.5 +- 0.5 as result, not 0.5 +- 0.25 as is currently the case.
You are using the inverse transform, where the resulting coefficients can then be interpreted as
f(x) approx sum(m = -N/2+1, N/2-1) c[k]*exp(-i*m*k*x)
For index m>=1 you combine two complex conjugate pairs of terms, the resulting real terms are, using c[k]=a+ib,
(a+i*b)*exp(-i*m*k*x) + (a-i*b)*exp(+i*m*k*x)
= 2*a*cos(m*k*x) + 2*b*sin(m*k*x)
Your Fourier series computation should thus look like
value = coeff[0].getReal();
for (int m=1; m<coeff.length/2; m++) {
value += 2*coeff[m].getReal()*Math.cos(x*k*m)
+ 2*coeff[m].getImaginary()*Math.sin(x*k*m);
}
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I have two linecharts in my application. One is with linear axis and the other is with logarithmic axis.
When I want so view only one series in the charts I set the other series and their data not visible so i can see only that series and i use the same method for visualize again all series too.
I've tried with threads but my problem is still there: in the chart with linear axis I don't have any problem but the logarithmic one doesn't update well the data.
Some nodes remain or are not shown, for example, it seems like the chart freeze while adding or removing visibility of data. Everything goes well only if I do a resize of the window and I don't understand why it is correlated. Here is my method to show only series with a certain name:
new Thread(() -> {
for (Series<Number, Number> series : lineChart.getData()) {
Platform.runLater(() -> {
if (series.getName().equals(name)) {
series.getNode().setVisible(!series.getNode().isVisible());
series.getData().forEach(data -> data.getNode().setVisible(series.getNode().isVisible()));
}
});
}
}).start();
Here is the class i use for the logarithmic axis:
public class LogarithmicAxis extends ValueAxis<Number> {
private Object currentAnimationID;
private final ChartLayoutAnimator animator = new ChartLayoutAnimator(this);
private final DoubleProperty logUpperBound = new SimpleDoubleProperty();
private final DoubleProperty logLowerBound = new SimpleDoubleProperty();
public LogarithmicAxis() {
super(0.0001, 1000);
bindLogBoundsToDefaultBounds();
}
public LogarithmicAxis(double lowerBound, double upperBound) {
super(lowerBound, upperBound);
validateBounds(lowerBound, upperBound);
bindLogBoundsToDefaultBounds();
}
public void setLogarithmicUpperBound(double d) {
double nd = Math.pow(10, Math.ceil(Math.log10(d)));
setUpperBound(nd == d ? nd * 10 : nd);
}
/**
* Binds logarithmic bounds with the super class bounds, consider the
* base 10 logarithmic scale.
*/
private void bindLogBoundsToDefaultBounds() {
logLowerBound.bind(new DoubleBinding() {
{
super.bind(lowerBoundProperty());
}
#Override
protected double computeValue() {
return Math.log10(lowerBoundProperty().get());
}
});
logUpperBound.bind(new DoubleBinding() {
{
super.bind(upperBoundProperty());
}
#Override
protected double computeValue() {
return Math.log10(upperBoundProperty().get());
}
});
}
/**
* Validates the bounds by throwing an exception if the values are not
* conform to the mathematics log interval: [0,Double.MAX_VALUE]
*
*/
private void validateBounds(double lowerBound, double upperBound) throws IllegalLogarithmicRangeException {
if (lowerBound < 0 || upperBound < 0 || lowerBound > upperBound) {
throw new IllegalLogarithmicRangeException(
"The logarithmic range should be in [0,Double.MAX_VALUE] and the lowerBound should be less than the upperBound");
}
}
/**
* It is used to get the list of minor tick marks position to display on the axis.
* It's based on the number of minor tick and the logarithmic formula.
*
*/
#Override
protected List<Number> calculateMinorTickMarks() {
List<Number> minorTickMarksPositions = new ArrayList<>();
return minorTickMarksPositions;
}
//Then, the calculateTickValues method
/**
* It is used to calculate a list of all the data values for each tick mark in range,
* represented by the second parameter. Displays one tick each power of 10.
*
*/
#Override
protected List<Number> calculateTickValues(double length, Object range) {
LinkedList<Number> tickPositions = new LinkedList<>();
if (range != null) {
double lowerBound = ((double[]) range)[0];
double upperBound = ((double[]) range)[1];
for (double i = Math.log10(lowerBound); i <= Math.log10(upperBound); i++) {
tickPositions.add(Math.pow(10, i));
}
if (!tickPositions.isEmpty()) {
if (tickPositions.getLast().doubleValue() != upperBound) {
tickPositions.add(upperBound);
}
}
}
return tickPositions;
}
/**
* The getRange provides the current range of the axis. A basic
* implementation is to return an array of the lowerBound and upperBound
* properties defined into the ValueAxis class.
*
*/
#Override
protected double[] getRange() {
return new double[]{
getLowerBound(),
getUpperBound()
};
}
/**
* The getTickMarkLabel is only used to convert the number value to a string
* that will be displayed under the tickMark.
*
*/
#Override
protected String getTickMarkLabel(Number value) {
NumberFormat formatter = NumberFormat.getInstance();
formatter.setMaximumIntegerDigits(10);
formatter.setMinimumIntegerDigits(1);
return formatter.format(value);
}
/**
* Updates the range when data are added into the chart.
* There is two possibilities, the axis is animated or not. The
* simplest case is to set the lower and upper bound properties directly
* with the new values.
*
*/
#Override
protected void setRange(Object range, boolean animate) {
if (range != null) {
final double[] rangeProps = (double[]) range;
final double lowerBound = rangeProps[0];
final double upperBound = rangeProps[1];
final double oldLowerBound = getLowerBound();
setLowerBound(lowerBound);
setUpperBound(upperBound);
if (animate) {
animator.stop(currentAnimationID);
currentAnimationID = animator.animate(
new KeyFrame(Duration.ZERO,
new KeyValue(currentLowerBound, oldLowerBound)
),
new KeyFrame(Duration.millis(700),
new KeyValue(currentLowerBound, lowerBound)
)
);
} else {
currentLowerBound.set(lowerBound);
}
}
}
#Override
public Number getValueForDisplay(double displayPosition) {
double delta = logUpperBound.get() - logLowerBound.get();
if (getSide().isVertical()) {
return Math.pow(10, (((displayPosition - getHeight()) / -getHeight()) * delta) + logLowerBound.get());
} else {
return Math.pow(10, (((displayPosition / getWidth()) * delta) + logLowerBound.get()));
}
}
#Override
public double getDisplayPosition(Number value) {
double delta = logUpperBound.get() - logLowerBound.get();
double deltaV = Math.log10(value.doubleValue()) - logLowerBound.get();
if (getSide().isVertical()) {
return (1. - ((deltaV) / delta)) * getHeight();
} else {
return ((deltaV) / delta) * getWidth();
}
}
/**
* Exception to be thrown when a bound value isn't supported by the
* logarithmic axis<br>
*
*/
public static class IllegalLogarithmicRangeException extends RuntimeException {
public IllegalLogarithmicRangeException(String message) {
super(message);
}
}
}
view only one series in the charts
One way to do this is to place one series in the chart and remove all others.
To do that, don't try to hide the nodes in other series, but, instead, remove other series from the data set, the charts will update automatically.
Turn off animation on the chart if you want an instant update instead of an animated one.
lineChart.setAnimated(false);
I've tried with threads
I don't advise using other threads unless you really need to.
Never access data associated with the active scene graph off of the JavaFX thread. This would include the line graph and its data.
To allow that to happen, run the logic which gets the lineChart data within the Platform.runlater() call, rather than accessing the lineChart.getData() call in your own thread as you have in your question.
new Thread(() -> {
Platform.runLater(() -> {
for (Series<Number, Number> series : lineChart.getData()) {
if (series.getName().equals(name)) {
series.getNode().setVisible(!series.getNode().isVisible());
series.getData().forEach(data -> data.getNode().setVisible(series.getNode().isVisible()));
}
}
});
}).start();
But then, if you do that, the thread and runLater calls seem pointless because you could just do everything inline:
for (Series<Number, Number> series : lineChart.getData()) {
if (series.getName().equals(name)) {
series.getNode().setVisible(!series.getNode().isVisible());
series.getData().forEach(data -> data.getNode().setVisible(series.getNode().isVisible()));
}
}
Example Code
I tried implementing a kind of "only include displayed series" strategy.
After implementing, I think that perhaps your original idea of hiding series by changing their visibility might be better.
It was a little bit trickier than I expected because when you only display a single series, it is colored by the default coloring scheme, where the colors are assigned by the sequential position of the series in the data. So, when you only display one series instead of many, the color of the series displayed actually changes, unless you override the default color scheme.
You can override that in a CSS style sheet, but then you need to change the style sheets for each selected series so that the series color stays constant, which is a pain.
There may be a nicer way of handling this. I thought you might just be able to set the DEFAULT_COLOR_ looked up color via setStyle in code, but I couldn't get that to work, so I just went with the pretty ugly style sheet code.
I also didn't integrate the logarithmic axis, because (as far as I can tell) the axis type should really have no bearing on this.
Another trick to this was that animated must be turned off for it to work, otherwise, the observer for the animated data will be fired while resetting the series data. It will think there is duplicated data series being added (which there isn't really, so that is a strange implementation quirk of the chart animation logic).
Anyway, for what it's worth, I provided the code I came up with.
If no toggles are set, it shows all series. If a toggle is set, it only shows the series corresponding to that toggle.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.chart.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.util.Random;
public class SeriesSelectionApp extends Application {
private static final int NUM_SERIES = 3;
private static final int NUM_DATA_PER_SERIES = 10;
private static final int DATA_MIN_VALUE = 5;
private static final int DATA_MAX_VALUE = 10;
private static final String[] seriesColors = new String[] {
"red", "green", "blue"
};
private static final Random random = new Random(42);
#Override
public void start(Stage stage) {
ObservableList<XYChart.Series<Number, Number>> data = generateData();
LineChart<Number, Number> lineChart = new LineChart<>(
new NumberAxis(0, NUM_DATA_PER_SERIES, 1),
new NumberAxis(0, DATA_MAX_VALUE, 1)
);
lineChart.getData().setAll(data);
lineChart.setAnimated(false);
setDefaultChartSeriesColors(lineChart);
HBox controls = new HBox(10);
ToggleGroup seriesSelectionToggleGroup = new ToggleGroup();
for (int i = 0; i < NUM_SERIES; i++) {
ToggleButton showSeriesToggleButton = new ToggleButton("Series " + (i+1));
showSeriesToggleButton.setToggleGroup(seriesSelectionToggleGroup);
showSeriesToggleButton.setUserData(i+1);
controls.getChildren().add(showSeriesToggleButton);
}
seriesSelectionToggleGroup.selectedToggleProperty().addListener((observable, oldValue, selectedToggle) -> {
lineChart.getData().clear();
if (selectedToggle == null) {
lineChart.getData().addAll(data);
setDefaultChartSeriesColors(lineChart);
} else {
int selectedSeriesNum = (int) selectedToggle.getUserData();
lineChart.getData().add(data.get(selectedSeriesNum - 1));
setSpecificChartSeriesColor(lineChart, selectedSeriesNum);
}
});
VBox layout = new VBox(10, controls, lineChart);
layout.setPadding(new Insets(10));
Scene scene = new Scene(layout);
stage.setScene(scene);
stage.show();
}
private void setDefaultChartSeriesColors(LineChart<Number, Number> lineChart) {
lineChart.getStylesheets().setAll(
"""
data:text/css,
.default-color0.chart-line-symbol { -fx-background-color: red, white; }
.default-color1.chart-line-symbol { -fx-background-color: green, white; }
.default-color2.chart-line-symbol { -fx-background-color: blue, white; }
.default-color0.chart-series-line { -fx-stroke: red; }
.default-color1.chart-series-line { -fx-stroke: green; }
.default-color2.chart-series-line { -fx-stroke: blue; }
"""
);
}
private void setSpecificChartSeriesColor(LineChart<Number, Number> lineChart, int seriesNum) {
lineChart.getStylesheets().setAll(
"""
data:text/css,
.default-color0.chart-line-symbol { -fx-background-color: MY_COLOR, white; }
.default-color0.chart-series-line { -fx-stroke: MY_COLOR; }
""".replaceAll("MY_COLOR", getSeriesColor(seriesNum))
);
}
private String getSeriesColor(int seriesNum) {
return seriesColors[(seriesNum - 1) % seriesColors.length];
}
private ObservableList<XYChart.Series<Number, Number>> generateData() {
ObservableList<XYChart.Series<Number, Number>> allData = FXCollections.observableArrayList();
for (int seriesNum = 0; seriesNum < NUM_SERIES; seriesNum++) {
ObservableList<XYChart.Data<Number, Number>> seriesData = FXCollections.observableArrayList();
for (int x = 0; x < NUM_DATA_PER_SERIES; x++) {
int y = random.nextInt(DATA_MAX_VALUE - DATA_MIN_VALUE) + DATA_MIN_VALUE;
XYChart.Data<Number, Number> dataItem = new XYChart.Data<>(x, y);
seriesData.add(dataItem);
}
XYChart.Series<Number, Number> series = new XYChart.Series<>("Series " + (seriesNum + 1), seriesData);
allData.add(series);
}
return allData;
}
public static void main(String[] args) {
launch(args);
}
}
I'm trying to implement a stream handling with RxJava.
I want to handle different steps.
I start with a publish subject where sensors data are pushed
I want to group these sensors data by their sensor type (an identifier)
For each group I want to buffer these sensors data
When the buffer is full or times out, I want to compute an average from all sensors value in that group
And finally I want to re-join all these groups into one single output stream
So far with the code example below, I have a buffer that is shared for all sensors data. I don't understand how to create a buffer for each group and then do my computation. As I'm new to RxJava, I don't understand all the concepts and I'm stuck with my issue.
import io.reactivex.Observable;
import io.reactivex.subjects.PublishSubject;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class Main {
private static final int SENSOR_TEMPERATURE = 1;
private static final int SENSOR_HUMIDITY = 2;
private PublishSubject<Sensor> publishSubject = PublishSubject.create();
static class Sensor {
int type;
float value;
Sensor(int type, float value) {
this.type = type;
this.value = value;
}
}
private PublishSubject<Sensor> listenSensors() {
return publishSubject;
}
private static Sensor getValueAverage(List<Sensor> sensors) {
int count = sensors.size();
float total = sensors.stream().map(sensor -> sensor.value).reduce(Float::sum).orElse(0f);
float avg = total / count;
return new Sensor(sensors.get(0).type, avg);
}
//Map type
private static String getStringType(int type) {
if (type == SENSOR_HUMIDITY) {
return "HUMIDITY";
}
else if (type == SENSOR_TEMPERATURE) {
return "TEMPERATURE";
}
return "OTHER";
}
private static void emitRandomValue(PublishSubject<Sensor> sensorPublishSubject) throws InterruptedException {
new Thread(() -> {
int randomDelay = 0;
while (true) {
int randomType = (int) ((Math.random() * 10 % 2) + 1);
randomDelay = (int) (Math.random() * 3000);
float randomValue = (float) (Math.random() * 100);
System.out.println("EMIT: " + getStringType(randomType) + " " + randomValue);
sensorPublishSubject.onNext(new Sensor(randomType, randomValue));
try {
Thread.sleep(randomDelay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
static Observable<List<Sensor>> flatpMapSensor(List<Sensor> sensors) {
return Observable
.fromIterable(sensors)
.groupBy(s -> s.type)
.flatMapSingle(Observable::toList);
}
// Testing code
static public void main(String args[]) throws InterruptedException {
Main main = new Main();
main.listenSensors()
.publish(p -> p
.buffer(20, TimeUnit.SECONDS, 10)
.filter(list -> !list.isEmpty()))
.flatMap(Main::flatpMapSensor)
.map(Main::getValueAverage)
.subscribe(sensor -> System.out.println("AVG " + getStringType(sensor.type) + " " + sensor.value));
emitRandomValue(main.publishSubject);
Thread.sleep(90000);
}
}
So my question is : How can I have a separate buffer for each sensor type ?
What if you shift the buffer() and groupBy() calls?
static public void main(String args[]) throws InterruptedException {
Main main = new Main();
main.listenSensors()
.groupBy(s -> s.type) // group by type
.flatMap(l -> l.buffer(20, SECONDS, 10).map(Main::getValueAverage)) // buffer groups by type and compute the average
.subscribe(sensor -> System.out.println("AVG " + getStringType(sensor.type) + " " + sensor.value));
emitRandomValue(main.publishSubject);
Thread.sleep(90000);
}
I am writing Java program which allows me to get the geometric center of random-generated 2-dimensional points. I want the calculations to be done by custom number of threads. I want to suspend/continue calculations at any time. Unfortunately, my code doesn't work, It seems like run() is never executed. Here is what I got:
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
System.out.println("running... " + Thread.currentThread().getName());
PointInterface p = pg.getPoint(); // getting random point(x,y)
pointCount++;
int[] pos = p.getPositions(); // getting (x,y)
System.out.println(pos[0] + ", " + pos[1] + " k");
sumxy[0] += pos[0];
sumxy[1] += pos[1];
geometricCenter[0] = (double) sumxy[0] / pointCount;
geometricCenter[1] = (double) sumxy[1] / pointCount;
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("exception caught in run()");
return;
}
}
}
Setting number of threads:
public void setNumberOfThreads(int threads) {
threadsList.clear();
for (int i = 0; i < threads; i++) {
threadsList.add(new Thread());
}
}
Starting the calculations:
public void start() {
try {
for (Thread t : threadsList) {
t.start();
}
} catch (Exception e) {
System.out.println("start() exception caught");
}
}
Suspending calculations:
public void suspendCalculations() {
try {
Thread.sleep(1200);
} catch (InterruptedException e) {
System.out.println("SuspendCalculations() exception caught");
}
}
Resuming calculations:
I don't exactly know what I'm supposed to do here. Should I create new set of Threads like that?
public void continueCalculations() {
int x = threadsList.size();
threadsList.clear();
for (int i = 0; i < x; i++) {
threadsList.add(new Thread());
threadsList.get(i).start();
}
}
How I run my program:
public static void main(String[] args) {
ParallelCalculations pc = new ParallelCalculations(); // My main class object where run() and all the methods above are declared
PointGenerator g = new PointGenerator(); // class that allows me to generate points
PointInterface a = g.getPoint(); // getting random point
pc.setNumberOfThreads(3);
pc.setPointGenerator(g);
pc.start();
pc.suspendCalculations();
System.out.println(pc.getGeometricCenter()[0] + ", " + pc.getGeometricCenter()[1]);
pc.continueCalculations();
pc.suspendCalculations();
System.out.println(pc.getGeometricCenter()[0] + ", " + pc.getGeometricCenter()[1]);
}
If needed:
Point:
class Point {
public static final int MAX_POSITION = 16;
private int[] positions = new int[2];
Point(int _x, int _y) {
this.positions[0] = _x;
this.positions[1] = _y;
}
public int[] getPositions() {
return positions;
}
}
Point Generator:
class PointGenerator {
private int x = (int) (Math.random() * (Point.MAX_POSITION + 1));
private int y = (int) (Math.random() * (Point.MAX_POSITION + 1));
public PointInterface getPoint() { // Can be called by many Threads at the same time.
return new Point(x, y);
}
}
Your run() should be executed and should do it's thing.
Though there is a far bigger cause for random behaviour in this code: All the threads write to sumxy, geometricCenter and pointCount at he same time without any syncronisation-locks, causing more or less random behaviour. You at least need to implement some kind of synchronisation to prevent simultanious writes.
Maybe start here (Java synchonized tutorial by Oracle) if you have no clue.
But simply adding synchronisation to everything will probably just make it slower than a single thread, you will need some kind of buffer for each thread to work independently and than collect the results when they are suspended.
And now general problems:
A) Your suspendCalculations() doesn't do anything (for 1200ms to be percise). To break the calcualtion you would need to interrupt all the worker-threads since they terminate upon interruption. Call threadsList.get(i).Interrupt() to do so.
B) If you want to to be able to change the number of threads while suspended, this is a way to go. If this is not necessarry, it would be more efficient to create a constant
public static final Object LOCK = new Object();
make all the threads LOCK.wait() on that object, so resuming them is just a call to LOCK.notifyAll().
C) Instead of using your own implementaion of Point you can use java.awt.Point.
D) Returning the coordinates of a point simply in an array is really bad for readability of your code, java.awt.Point has getX() and getY() functions.
I am using androidplot that loops the showing of a pulse (essentially a relatively short sequence of points) n times per minute and a flat value the rest of the time. There is an erase bar at the start that removes the 50 oldest points. But what I can't figure out how to have that graph update at a specific interval (the delay in run()) so that the series scans at 25mm/sec.
private class PulseXYSeries implements XYSeries {
private ArrayList<Integer> values;
private String title;
public PulseXYSeries(String title, int size) {
values = new ArrayList<Integer>(size);
for(int i = 0; i < size;i++) {
values.add(null);
}
this.title = title;
}
#Override
public String getTitle() {
return title;
}
public void remove(int idx) {
values.set(idx, null);
}
public void setY(int val, int idx) {
values.set(idx, val);
}
#Override
public Number getX(int idx) {
return idx;
}
#Override
public Number getY(int idx) {
if(idx >= values.size())
return null;
return values.get(idx);
}
#Override
public int size() {
return values.size();
}
}
private class MonitorDataSource implements Runnable {
private final int SAMPLE_SIZE = 1000;
private boolean keepRunning = false;
private List<Integer> queue;
private int flat;
private Thread rd;
MonitorDataSource(View rootView) {
queue = getSelectedPointData(rootView);
flat = queue.get(0);
rd = new Thread(/** runnable that calls dynamicPlot.redraw() at 30Hz **/);
rd.start();
}
public void stopThread() {
keepRunning = false;
rd.interrupt();
}
public void run() {
try {
Log.i(TAG,"Running pulse thread");
keepRunning = true;
int i=0;
boolean pulsing = true;
long lastPulse = SystemClock.elapsedRealtime();
long pulseDelay = 1000*60/mHeartRatePicker.getValue();
int position = 0;
// we need to scan at 25mm/sec
long delay = 10;
DisplayMetrics dp = getResources().getDisplayMetrics();
float plotWidth = dynamicPlot.getGraphWidget().getWidgetDimensions().canvasRect.width();
float plotWidthMm = plotWidth / dp.xdpi * 25.4f;
float widthPerTickInMm = plotWidthMm/(float)SAMPLE_SIZE;
Log.i(TAG,"Width per tick: "+widthPerTickInMm+" plot width px="+plotWidth+" in mm="+plotWidthMm+" xdpi="+dp.xdpi+" xdpmm="+(dp.xdpi*(1.0f/25.4f)));
long currTime,loopStart = SystemClock.elapsedRealtimeNanos();
while (keepRunning) {
// plot 4 points at a time
for (int j = 0; j < 3; j++) {
if(pulsing) {
mMovingWaveSeries.setY(queue.get(i),position);
if(++i == queue.size()-1) {
pulsing = false;
i=0;
}
} else {
mMovingWaveSeries.setY(flat,position);
currTime = SystemClock.elapsedRealtime();
if(currTime - lastPulse >= pulseDelay) {
pulsing = true;
lastPulse = currTime;
}
}
mMovingWaveSeries.remove(((position + 50) % SAMPLE_SIZE));
position = (position+1) % SAMPLE_SIZE;
if(position +1 >= SAMPLE_SIZE) {
float diff = (SystemClock.elapsedRealtimeNanos() - loopStart )/ 1000000000f;
loopStart = SystemClock.elapsedRealtimeNanos();
Log.i(TAG,"Looped through "+plotWidthMm+"mm in "+diff+"s = "+ (plotWidthMm/diff) +"mm/s");
}
}
Thread.sleep(delay);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
What seems to be lacking in your code is an instantaneous measurement of the current scan rate, in mm. You can use this value to adjust the scale of your plot's domain to get the desired effect. This is done via XYPlot.setDomainBoundaries(...). Domain scale and sample frequency (seemingly represented by "delay" in your code) can be adjusted to compensate for each other, so if you need to maintain a particular domain scale then modulate your sampling frequency accordingly. If done properly, rendering frequency should not matter at all and can be allowed to float...in fact modulating refresh rate to compensate for sample rate will usually result in buffer overrun/underrun issues.
UPDATE (response to below comment)
Appears that you're actually throttling the datasource (sample rate), not the plot (refresh rate), which is fine. The first thing you'll need to do is determine the loop frequency required to achieve 25mm/sec based on widthPerTickInMm and the number of points you are drawing in each loop:
Frequency(Hz) = 25 / (widthPerTickInMm * pointsPerLoop)
Use this value to modulate your datasource update loop. Here's an example of how you can dynamically modulate an arbitrary loop at a given frequency:
float hz = 5; // modulate at 5hz
long budget = (long) ((1/hz) * 1000f);
long loopDurationMs = 0;
long loopStartMs = 0;
while(true) {
// calculate how long this loop took:
long now = System.currentTimeMillis();
loopDurationMs = now - loopStartMs;
long sleepTime = budget - loopDurationMs;
loopStartMs = now;
if(sleepTime > 0) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
Just a warning - I've not tried compiling or running the code but the concept is there. (This only works if your potential loop frequency is > desired frequency...probably obvious but just in case)
I am trying to write a method that collects accelerometer sensor values over a specific time period and returns the average of the sensor readings for that period.
It should be a synchronous i.e. blocking method that once it is called will block the calling thread for sometime and then will return the sensor average
I did check the below similar questions but does not seem to have a proper working solution for my case:
SensorEventListener in separate thread
Android - how to run your sensor ( service, thread, activity )?
Android sensors and thread
A method for waiting for sensor data
I've also tried to use Executors similar to this question, but could not get it to work as I want.
Below is my code skeleton, where the method sensorAverage is a blocking method that will calculate the accelerometer sensor average over a period equals to the timeout parameter
Average average = new Average(); // Some class to calculate the mean
double sensorAverage(long timeout){
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
sensorManager.registerListener(this, sensor,SensorManager.SENSOR_DELAY_NORMAL);
// This does not work
Thread.sleep(timeout);
sensorManager.unregisterListener(this);
return average.value();
}
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION) {
double x2 = Math.pow(event.values[0], 2);
double y2 = Math.pow(event.values[1], 2);
double z2 = Math.pow(event.values[2], 2);
average.add(Math.sqrt((x2 + y2 + z2)));
}
}
Edit:
I am aware that I need another thread, but the problem that I need to run it for a specific period only and so far I cannot find a proper working solution. Because when I use another thread I get the sensor average always 0
I managed to implement a solution which exactly does what I want.
A blocking method that collects sensor values for specific period and returns the statistics of all sensor readings i.e. mean and variance.
It is possible to simply store all the sensor's values and then calculate the mean and variance; however you might run out of memory in case of collecting high frequency sensor over extended period of time.
I found a better solution to calculate the mean and variance for a stream of data in real-time (i.e. without storing the sensor values) using the below RunningStat class
Example code:
// Calculate statistics of accelerometer values over 300 ms (a blocking method)
RunningStat[] stats = SensorUtils.sensorStats(context,
Sensor.TYPE_ACCELEROMETER, 300)
double xMean = stats[0].mean();
double xVar = stats[0].variance();
Full class code:
public class SensorUtils {
// Collect sensors data for specific period and return statistics of
// sensor values e.g. mean and variance for x, y and z-axis
public static RunningStat[] sensorStats(Context context, int sensorType,
long timeout) throws Exception {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<RunningStat[]> future = executor.submit(new SensorTask(context,
sensorType, timeout));
RunningStat[] stats = future.get();
return stats;
}
private static class SensorTask implements Callable<RunningStat[]> {
private final Context context;
private final long timeout;
private final int sensorType;
// We need a dedicated handler for the onSensorChanged
HandlerThread handler = new HandlerThread("SensorHandlerThread");
public SensorTask(Context context, int sensorType, long timeout) {
this.context = context;
this.timeout = timeout;
this.sensorType = sensorType;
}
#Override
public RunningStat[] call() throws Exception {
final SensorCollector collector = new SensorCollector(context);
handler.start();
Thread sensorThread = new Thread() {
public void run() {
collector.start(sensorType,
new Handler(handler.getLooper()));
};
};
sensorThread.start();
Thread.sleep(timeout);
return collector.finishWithResult();
}
}
private static class SensorCollector implements SensorEventListener {
protected Context context;
protected RunningStat[] runningStat;
protected SensorManager sensorManager;
protected int sensorType;
public SensorCollector(Context context) {
this.context = context;
}
protected void start(int sensorType, Handler handle) {
if (runningStat == null) {
runningStat = new RunningStat[3];
runningStat[0] = new RunningStat(3);
runningStat[1] = new RunningStat(3);
runningStat[2] = new RunningStat(3);
} else {
runningStat[0].clear();
runningStat[1].clear();
runningStat[2].clear();
}
this.sensorType = sensorType;
sensorManager = (SensorManager) context
.getSystemService(Context.SENSOR_SERVICE);
Sensor sensor = sensorManager.getDefaultSensor(sensorType);
sensorManager.registerListener(this, sensor,
SensorManager.SENSOR_DELAY_NORMAL, handle);
}
public RunningStat[] finishWithResult() {
if (sensorManager != null) {
sensorManager.unregisterListener(this);
}
return runningStat;
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == sensorType) {
runningStat[0].push(event.values[0]);
runningStat[1].push(event.values[1]);
runningStat[2].push(event.values[2]);
}
}
}
}
Here is the RunningStat code, which is a very handy class to calculate the mean and variance for stream of data without storing the data itself (perfect for calculating statistics of high frequency sensors with very small memory footprint)
//See Knuth TAOCP vol 2, 3rd edition, page 232
public class RunningStat {
private int n;
private double oldM, newM, oldS, newS;
private int precision = -1;
// An estimate for the t-value (can be read from the t-distribution table)
private static final double T_THRESHOLD = 1.68;
public RunningStat(int precision) {
this.precision = precision;
}
public RunningStat() {
}
public void clear() {
n = 0;
}
public void push(double x) {
n++;
if (n == 1) {
oldM = newM = x;
oldS = 0.0;
} else {
newM = oldM + (x - oldM) / n;
newS = oldS + (x - oldM) * (x - newM);
// set up for next iteration
oldM = newM;
oldS = newS;
}
}
public int count() {
return n;
}
public double mean() {
double mean = (n > 0) ? newM : 0.0;
if (precision > 0) {
return round(mean, precision);
}
return mean;
}
// The upper bound of the mean confidence interval
public double meanUpper() {
double mean = (n > 0) ? newM : 0.0;
double stdError = stdDeviation() / Math.sqrt(n);
double upperMean = mean + T_THRESHOLD * stdError;
if (precision > 0) {
return round((n > 0) ? upperMean : 0.0, precision);
}
return upperMean;
}
// The lower bound of the mean confidence interval
public double meanLower() {
double mean = (n > 0) ? newM : 0.0;
double stdError = stdDeviation() / Math.sqrt(n);
double lowerMean = mean - T_THRESHOLD * stdError;
if (precision > 0) {
return round((n > 0) ? lowerMean : 0.0, precision);
}
return lowerMean;
}
public double variance() {
if (precision > 0) {
return round(((n > 1) ? newS / (n - 1) : 0.0), precision);
}
return ((n > 1) ? newS / (n - 1) : 0.0);
}
public double stdDeviation() {
if (precision > 0) {
return round(Math.sqrt(variance()), precision);
}
return Math.sqrt(variance());
}
public void setPrecision(int precision) {
this.precision = precision;
}
public static double round(double value, int precision) {
BigDecimal num = new BigDecimal(value);
num = num.round(new MathContext(precision, RoundingMode.HALF_UP));
return num.doubleValue();
}
// A small test case
public static void main(String[] args) {
int n = 100;
RunningStat runningStat = new RunningStat();
double[] data = new double[n];
double sum = 0.0;
for (int i = 0; i < n; i++) {
data[i] = i * i;
sum += data[i];
runningStat.push(data[i]);
System.out.println(runningStat.mean() + " - "
+ runningStat.variance() + " - "
+ runningStat.stdDeviation());
}
double mean = sum / n;
double sum2 = 0.0;
for (int i = 0; i < n; i++) {
sum2 = sum2 + (data[i] - mean) * (data[i] - mean);
}
double variance = sum2 / (n - 1);
System.out.println("\n\n" + mean + " - " + variance + " - "
+ Math.sqrt(variance));
}
}
You are essentially asking for a shake detector functionality, you can't block the main thread because you are highly likely to run accross ANR errors
You could try using the java class from Jake Wharton of Action Bar Sherlock fame https://github.com/square/seismic/tree/master/library/src/main/java/com/squareup/seismic
which will do pretty much what you are asking for, you would just need to adapt it slightly to meet your requirements, You could add onStart and onStop listeners and fire them from the start and stop methods and tie them up to your activity
It's not entirely clear what you are wishing to do exactly so it;s difficult to advise further but I am suire that what you want can be achieved without too much effort and achieved asynchronously thereby avoiding ANR's with a bit of thought using the shake detector as a base for what you want to do.
The isShaking method is probably where you might want to start making your amendments by taking a look at the sampleCount and acceleratingCount variables to see how they might help you.
boolean isShaking() {
return newest != null
&& oldest != null
&& newest.timestamp - oldest.timestamp >= MIN_WINDOW_SIZE
&& acceleratingCount >= (sampleCount >> 1) + (sampleCount >> 2);
}
You could adjust thse values to determine how many samples or how long you want to detect movement for.
There is already a sample list that you can use to make your calculations with, just
pass it back to the onStop listener
/** Copies the samples into a list, with the oldest entry at index 0. */
List<Sample> asList() {
List<Sample> list = new ArrayList<Sample>();
Sample s = oldest;
while (s != null) {
list.add(s);
s = s.next;
}
return list;
}
Update in response to comment
You could determine if there is no movement simply by using a flag which is set to false in the onStop listener call back and you have complete control over how long "still" and comparing against a time stamp since the last stop to determine if the device has been still enough for long enough to meet your requirements