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)
Related
i have a List of Objects "Cell" which represent a current status.
List<Cell> currentCells = new ArrayList<Cell>();
I want to update the current status in a loop by calculating a future status (a new List of Cells) an then do replacing by "current status = future status" and so on. For that every Cell has a method which returns a new Cell Object representing its future. This method has simple math operations. There are no dependencies to other calculations. So, in theory, future Cells could be computet in parallel Threads.
My Problem is to find the fastet way of computing the future status cause the "List cells" is a very large ArrayList(4000000) and i want to reach up to 25 loops /second.
If i compute it sequentially my loop repeats 3 times / second. If i do high parallelisation by making each Cell a callable and putting them in an ExecuterService my loop repeats only with 0.5 per second.
List<Future<Cell>> futures = taskExecutor.invokeAll(cells);
I know that solving a problem in parallel always needs an overhead. In this case it speeds down my loop. What method can you recommend to speed up my loop?
There seems to be no difference in speed anyway if i change numberOfThreads from 1 up to 12 (i have maximum of 12 processors). The code of my loop is:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import view.ConsolePrinter;
public class Engine implements Runnable {
private int numberOfThreads = 8;
private boolean shouldRun = false;
private ExecutorService taskExecutor;
private Thread loopThread;
private List<Cell> cells;
private World world;
private int numberOfRunningTimeSteps = 0;
private int FPS = 30;
private double averageFPS;
public Engine(List<Cell> cells, World world) {
this.cells = cells;
taskExecutor = Executors.newFixedThreadPool(numberOfThreads);
this.world = world;
}
#Override
public void run() {
world.resetTime();
shouldRun = true;
long startTime;
long estimatedTimeMillis;
long waitTime;
long totalTime = 0;
long targetTime = 1000 / FPS;
int frameCount = 0;
int maxFrameCount = 1;
while (shouldRun) {
startTime = System.nanoTime();
try {
List<Future<Cell>> futures = taskExecutor.invokeAll(cells);
List<Cell> futureCells = new ArrayList<Cell>();
futures.forEach((future) -> {
try {
futureCells.add(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
});
world.setCells(futureCells);
} catch (InterruptedException e) {
e.printStackTrace();
}
world.timeProceeded();
if (numberOfRunningTimeSteps != 0 && world.getTime() == numberOfRunningTimeSteps) {
shouldRun = false;
}
estimatedTimeMillis = (System.nanoTime() - startTime) / 1000000;
waitTime = targetTime - estimatedTimeMillis;
try {
if (waitTime > 0.0) {
Thread.sleep(waitTime);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
totalTime += System.nanoTime() - startTime;
frameCount++;
if (frameCount == maxFrameCount) {
averageFPS = 1000.0 / ((totalTime / frameCount) / 1000000);
frameCount = 0;
totalTime = 0;
}
}
}
public void start(int n) {
loopThread = new Thread(this);
loopThread.start();
numberOfRunningTimeSteps = n;
}
public void stop() {
shouldRun = false;
}
}
I'm trying to add an AlarmManager to update a watch face on half minute intervals.
This is for a ternary clock.
This is my first time programming with Java or android studio.
I'm following a guide at https://developer.android.com/training/wearables/apps/always-on.html
The guide says to "declare the alarm manager and the pending intent in the onCreate() method of your activity"
Should I use
#Override
public Engine onCreateEngine() {
return new Engine();
}
or
#Override
public Engine onCreateEngine() {
return new Engine();
}
or should I start a new method or declare it elsewhere?
Currently I'm using
private class Engine extends CanvasWatchFaceService.Engine {
final Handler mUpdateTimeHandler = new EngineHandler(this);
for most of my initialization.
This is my code without the alarm manager. The issue is that it must update during half minutes, because as balanced ternary the time should be to the nearest minute.
public class ternary extends CanvasWatchFaceService {
private static final Typeface NORMAL_TYPEFACE =
Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
private static final int MSG_UPDATE_TIME = 0;
#Override
public Engine onCreateEngine() {
return new Engine();
}
private static class EngineHandler extends Handler {
private final WeakReference<ternary.Engine> mWeakReference;
public EngineHandler(ternary.Engine reference) {
mWeakReference = new WeakReference<>(reference);
}
#Override
public void handleMessage(Message msg) {
ternary.Engine engine = mWeakReference.get();
if (engine != null) {
switch (msg.what) {
case MSG_UPDATE_TIME:
engine.handleUpdateTimeMessage();
break;
}
}
}
}
private class Engine extends CanvasWatchFaceService.Engine {
final Handler mUpdateTimeHandler = new EngineHandler(this);
boolean mRegisteredTimeZoneReceiver = false;
Paint mBackgroundPaint;
Paint mTextPaint;
boolean mAmbient;
Time mTime;
final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
mTime.clear(intent.getStringExtra("time-zone"));
mTime.setToNow();
}
};
int mTapCount;
float mXOffset;
float mYOffset;
// adjust text size
float textRatio = (float)1; // 2/3;
// make adjusted offset for hours
float hrsIndent;
float hrsIndentAdjust = textRatio * 55;
// vertical offset for multiple lines
float ySpacer = textRatio * 65;
// first run.
boolean yesFirstRun = true;
// flag for seconds
boolean yesSecs;
// prior state of yesSecs
boolean wasSecs = true;
// flag for conservation mode (no seconds in ambient)
boolean yesConcerve = false;
// flag for allowing seconds
boolean allowSecs = true;
// for execution control
boolean openGate = false;
// counter for next draw
int c = 0;
// counter for time loops
int k;
boolean drawNow = true;
// strings for draw
String hrs = "";
String mns = "";
String sks = "";
// register for milliseconds
long millis = 0;
// float for calculating trits from time.
float tim = 0;
// ints for minute and hour offsets.
int minInt = 0;
int hourInt = 0;
// lists for time to trit for loop conversions.
int [] trits3 = {9, 3, 1};
int [] trits4 = {27, 9, 3, 1};
// absolute count for trouble shooting
// long x = 0;
/**
* Whether the display supports fewer bits for each color in ambient mode. When true, we
* disable anti-aliasing in ambient mode.
*/
boolean mLowBitAmbient;
#Override
public void onCreate(SurfaceHolder holder) {
super.onCreate(holder);
setWatchFaceStyle(new WatchFaceStyle.Builder(ternary.this)
.setCardPeekMode(WatchFaceStyle.PEEK_MODE_VARIABLE)
.setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
.setShowSystemUiTime(false)
.setAcceptsTapEvents(true)
.build());
Resources resources = ternary.this.getResources();
// shift y offset up
mYOffset = -30 + resources.getDimension(R.dimen.digital_y_offset);
mBackgroundPaint = new Paint();
mBackgroundPaint.setColor(resources.getColor(R.color.background));
mTextPaint = new Paint();
mTextPaint = createTextPaint(resources.getColor(R.color.digital_text));
mTime = new Time();
}
#Override
public void onDestroy() {
mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
super.onDestroy();
}
private Paint createTextPaint(int textColor) {
Paint paint = new Paint();
paint.setColor(textColor);
paint.setTypeface(NORMAL_TYPEFACE);
paint.setAntiAlias(true);
return paint;
}
#Override
public void onVisibilityChanged(boolean visible) {
super.onVisibilityChanged(visible);
if (visible) {
registerReceiver();
// Update time zone in case it changed while we weren't visible.
mTime.clear(TimeZone.getDefault().getID());
mTime.setToNow();
} else {
unregisterReceiver();
}
// Whether the timer should be running depends on whether we're visible (as well as
// whether we're in ambient mode), so we may need to start or stop the timer.
updateTimer();
}
private void registerReceiver() {
if (mRegisteredTimeZoneReceiver) {
return;
}
mRegisteredTimeZoneReceiver = true;
IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
ternary.this.registerReceiver(mTimeZoneReceiver, filter);
}
private void unregisterReceiver() {
if (!mRegisteredTimeZoneReceiver) {
return;
}
mRegisteredTimeZoneReceiver = false;
ternary.this.unregisterReceiver(mTimeZoneReceiver);
}
#Override
public void onApplyWindowInsets(WindowInsets insets) {
super.onApplyWindowInsets(insets);
// Load resources that have alternate values for round watches.
Resources resources = ternary.this.getResources();
boolean isRound = insets.isRound();
// shift offset 75 to the right
mXOffset = 75 + resources.getDimension(isRound
? R.dimen.digital_x_offset_round : R.dimen.digital_x_offset);
float textSize = resources.getDimension(isRound
? R.dimen.digital_text_size_round : R.dimen.digital_text_size);
// adjust hrs Indent to MXOffset
hrsIndent = hrsIndentAdjust + mXOffset;
// adjust size to textRatio
mTextPaint.setTextSize(textSize * textRatio );
}
#Override
public void onPropertiesChanged(Bundle properties) {
super.onPropertiesChanged(properties);
mLowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);
}
#Override
public void onTimeTick() {
super.onTimeTick();
invalidate();
}
#Override
public void onAmbientModeChanged(boolean inAmbientMode) {
super.onAmbientModeChanged(inAmbientMode);
if (mAmbient != inAmbientMode) {
mAmbient = inAmbientMode;
if (mLowBitAmbient) {
mTextPaint.setAntiAlias(!inAmbientMode);
}
invalidate();
}
// Whether the timer should be running depends on whether we're visible (as well as
// whether we're in ambient mode), so we may need to start or stop the timer.
updateTimer();
}
/**
* Captures tap event (and tap type) and toggles the background color if the user finishes
* a tap.
*/
#Override
public void onTapCommand(int tapType, int x, int y, long eventTime) {
Resources resources = ternary.this.getResources();
switch (tapType) {
case TAP_TYPE_TOUCH:
// The user has started touching the screen.
break;
case TAP_TYPE_TOUCH_CANCEL:
// The user has started a different gesture or otherwise cancelled the tap.
break;
case TAP_TYPE_TAP:
// The user has completed the tap gesture.
mTapCount++;
mBackgroundPaint.setColor(resources.getColor(mTapCount % 2 == 0 ?
R.color.background : R.color.background2));
break;
}
invalidate();
}
#Override
public void onDraw(Canvas canvas, Rect bounds) {
// Greebo counter
// x += 1;
// seconds handling
wasSecs = yesSecs;
yesSecs = allowSecs && !isInAmbientMode();
// for clearing seconds
if (!yesSecs && wasSecs) { sks = ""; }
// Draw at mid second
if (c == 0 && yesSecs) {
drawNow = true;
} else {
c = 0;
// mid minute
if (mTime.second == 30 || isInAmbientMode()) {
drawNow = true;
} else {
// mid hour
if (mTime.second == 0) {
if (mTime.minute == 30) {
drawNow = true;
} else {
// mid night
if (mTime.minute == 0) {
if (mTime.hour == 0) {
drawNow = true;
}
}
}
}
}
}
if (drawNow) {
drawNow = false;
mTime.setToNow();
millis = System.currentTimeMillis() % 1000;
// mid seconds
if (yesSecs) { if (millis > 499) { c = 1; } }
tim = (float)((mTime.minute * 60 + mTime.second) * 1000 + millis)/ 3600000;
// hours past noon
tim += mTime.hour - 12;
// find hrs 9s, 3s, 1s.
openGate = false;
if (yesFirstRun || mTime.minute == 30){ openGate = true; }
else { openGate = mTime.second == 0 && mTime.minute == 0 && mTime.hour == 0;}
if (openGate) {
hrs = "";
hourInt = 0;
// i is for item.
for (int i : trits3) {
if (tim > ((float) i / 2)) {
tim -= i;
hourInt -= i;
hrs = hrs + "1";
} else {
if (tim < ((float) i / -2)) {
tim += i;
hourInt += i;
hrs = hrs + "¬";
} else {
hrs = hrs + "0";
}
}
// add space
if (i > 1) {hrs += " "; }
}
} else { tim += hourInt; }
// minutes 27s, 9s, 3s, 1s
openGate = false;
if (yesFirstRun || mTime.second == 30 || isInAmbientMode()) {openGate = true; }
else { openGate = mTime.second == 0 && (mTime.minute == 30
|| (mTime.minute == 0 && mTime.hour == 0));}
if (openGate) {
mns = "";
tim *= 60;
minInt = 0;
// i is for item.
for (int i : trits4) {
if (tim > ((float) i / 2)) {
tim -= i;
if (yesSecs) {minInt -= i;}
mns = mns + "1";
} else {
if (tim < ((float) i / -2)) {
tim += i;
if (yesSecs) {minInt += i;}
mns = mns + "¬";
} else {
mns = mns + "0";
}
}
// add space
if (i > 1) {mns += " "; }
}
} else { if (yesSecs) { tim += minInt; tim *= 60; } }
// seconds 27s, 9s, 3s, 1s
if (yesSecs) {
sks = "";
tim *= 60;
for (int i : trits4) {
if (tim > ((float) i / 2)) {
tim -= i;
sks = sks + "1";
} else {
if (tim < ((float) i / -2)) {
tim += i;
sks = sks + "¬";
} else {
sks = sks + "0";
}
}
// add space
if (i > 1) {sks += " "; }
}
}
}
// Draw the background.
if (isInAmbientMode()) {
canvas.drawColor(Color.BLACK);
} else {
canvas.drawRect(0, 0, bounds.width(), bounds.height(), mBackgroundPaint);
}
// draw hours
canvas.drawText(hrs, hrsIndent, mYOffset - ySpacer, mTextPaint);
// draw minutes
canvas.drawText(mns, mXOffset, mYOffset, mTextPaint);
// draw or clear seconds
if (yesSecs || wasSecs) {canvas.drawText(sks, mXOffset, mYOffset + ySpacer , mTextPaint);}
// show count and millis for greebo reduction.
// canvas.drawText(String.format("%1$03d,%2$02d,%3$d", x % 1000, millis / 10, 0), mXOffset, mYOffset + 100, mTextPaint);
//canvas.drawText(String.format("%$02d:%2$02d:%3$02d", mTime.hour, mTime.minute,
// mTime.second), mXOffset, mYOffset + 100, mTextPaint);
}
/**
* Starts the {#link #mUpdateTimeHandler} timer if it should be running and isn't currently
* or stops it if it shouldn't be running but currently is.
*/
private void updateTimer() {
mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
if (shouldTimerBeRunning()) {
mUpdateTimeHandler.sendEmptyMessage(MSG_UPDATE_TIME);
}
}
/**
* Returns whether the {#link #mUpdateTimeHandler} timer should be running. The timer should
* only run when we're visible and in interactive mode.
*/
private boolean shouldTimerBeRunning() {
return isVisible() && !isInAmbientMode();
}
/**
* Handle updating the time periodically in interactive mode.
*/
private void handleUpdateTimeMessage() {
invalidate();
if (shouldTimerBeRunning()) {
long timeMs = System.currentTimeMillis();
long delayMs = INTERACTIVE_UPDATE_RATE_MS
- (timeMs % INTERACTIVE_UPDATE_RATE_MS);
mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
}
}
}
}
The "onCreate()" method seems to correlate with:
public class ternary extends CanvasWatchFaceService
Your declaration should be as follows:
public class ternary extends CanvasWatchFaceService {
private AlarmManager mAmbientStateAlarmManager;
private PendingIntent mAmbientStatePendingIntent;
And be sure to import android.app.AlarmManager and android.app.PendingIntent
.
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
When I was searching through google I found a java program created in netbeans to calculate the bpm of a song. It was working with a lot of JAR files.
I used the same code for my android app and it showed lot of errors due to the missing of only one JAR file. I added the JLayer1.0.1 jar file and all the errors were cleared.
Now the app is working good but the bpm calculation is creating some new problems. It gives the bpm of a song which is less than 1 min but the others songs are not always been in process for a hour. And the songs are not been played in the background.
When I checked with the Java program there it is calculating the bpm of all songs and the songs are been played and I can hear it.
What is the problem I am facing here? Is this all because of the JAR file, am I supposed to use any other JAR files? Please help me friends....
I am adding a part of my code
Player player = new Player(new FileInputStream("//sdcard//taxi.mp3"), output);
public class BPM2SampleProcessor implements SampleProcessor {
private Queue<Long> energyBuffer = new LinkedList<Long>();
private int bufferLength = 43;
private long sampleSize = 1024;
private long frequency = 44100;
private long samples = 0;
private long beats = 0;
private static int beatThreshold = 3;
private int beatTriggers = 0;
private List<Integer> bpmList = new LinkedList<Integer>();
public void process(long[] sample) {
energyBuffer.offer(sample[0]);
samples++;
if(energyBuffer.size() > bufferLength) {
energyBuffer.poll();
double averageEnergy = 0;
for(long l : energyBuffer)
averageEnergy += l;
averageEnergy /= bufferLength;
double C = 1.3; //a * variance + b;
boolean beat = sample[0] > C * averageEnergy;
if(beat)
{
if(++beatTriggers == beatThreshold)
beats ++;
}
else
{
beatTriggers = 0;
}
if(samples > frequency * 5 / sampleSize) {
bpmList.add(getInstantBPM());
beats = 0;
samples = 0;
}
}
}
public void init(int freq, int channels) {
frequency = freq;
}
public int getInstantBPM() {
return (int)((beats * frequency * 60) / (samples * sampleSize));
}
public int getBPM() {
Collections.sort(bpmList);
return bpmList.get(bpmList.size() / 2);
}
public long getSampleSize() {
return sampleSize;
}
public void setSampleSize(long sampleSize) {
this.sampleSize = sampleSize;
}
}
public class EnergyOutputAudioDevice extends BaseOutputAudioDevice {
private int averageLength = 1024; // number of samples over which the average is calculated
private Queue<Short> instantBuffer = new LinkedList<Short>();
public EnergyOutputAudioDevice(SampleProcessor processor) {
super(processor);
}
#Override
protected void outputImpl(short[] samples, int offs, int len) throws JavaLayerException {
for(int i=0; i<len; i++)
instantBuffer.offer(samples[i]);
while(instantBuffer.size()>averageLength*channels)
{
long energy = 0;
for(int i=0; i<averageLength*channels; i++)
energy += Math.pow(instantBuffer.poll(), 2);
if(processor != null)
processor.process(new long[] { energy });
}
}
public int getAverageLength() {
return averageLength;
}
public void setAverageLength(int averageLength) {
this.averageLength = averageLength;
}
}
public class SnowflakeWallpaper extends WallpaperService {
// Limit of snowflakes per snowflake type; 4 types * 4 snowflake = 16 total
// Should keep memory usage at a minimal
static int SNOWFLAKE_AMOUNT = 4;
Drawable drawWall;
Rect wallBounds;
// Draw all snowflakes off screen due to not knowing size of canvas at creation
static int SNOW_START = -90;
ArrayList<Snowflakes> snow = new ArrayList<Snowflakes>();
private final Handler mHandler = new Handler();
#Override
public void onCreate() {
super.onCreate();
//WallpaperManager to pull current wallpaper
WallpaperManager wManager = WallpaperManager.getInstance(this);
drawWall = wManager.getFastDrawable();
wallBounds = drawWall.copyBounds();
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public Engine onCreateEngine() {
return new SnowEngine();
}
class SnowEngine extends Engine {
private final Runnable mDrawSnow = new Runnable() {
public void run() {
drawFrame();
}
};
private boolean mVisible;
SnowEngine() {
if(snow.size() < 16){
//Back snowflakes
for(int i = 0; i < SNOWFLAKE_AMOUNT; i++){
snow.add(new Snowflakes(
BitmapFactory.decodeResource(getResources(),
R.drawable.snowflakeback),
SNOW_START,
SNOW_START,
((float)(Math.random() * 2) + 1)) // Fall speed initial setup, back slowest to front fastest potentially
);
}
//MidBack snowflakes
for(int i = 0; i < SNOWFLAKE_AMOUNT; i++){
snow.add(new Snowflakes(
BitmapFactory.decodeResource(getResources(),
R.drawable.snowflakemid),
SNOW_START,
SNOW_START,
((float)(Math.random() * 4) + 1)
));
}
// Mid snowflakes
for(int i = 0; i < SNOWFLAKE_AMOUNT; i++){
snow.add(new Snowflakes(
BitmapFactory.decodeResource(getResources(),
R.drawable.snowflakemidfront),
SNOW_START,
SNOW_START,
((float)(Math.random() * 8) + 1))
);
}
// Front snowflakes
for(int i = 0; i < SNOWFLAKE_AMOUNT; i++){
snow.add(new Snowflakes(
BitmapFactory.decodeResource(getResources(),
R.drawable.snowflake),
SNOW_START,
SNOW_START,
((float)(Math.random() * 16) + 1))
);
}
}
}
#Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
}
#Override
public void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(mDrawSnow);
}
#Override
public void onVisibilityChanged(boolean visible) {
mVisible = visible;
if (visible) {
drawFrame();
} else {
mHandler.removeCallbacks(mDrawSnow);
}
}
#Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
drawFrame();
}
#Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
}
#Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
mVisible = false;
mHandler.removeCallbacks(mDrawSnow);
}
/*
* Update the screen with a new frame
*/
void drawFrame() {
final SurfaceHolder holder = getSurfaceHolder();
/*
* if the snow goes too low or too right, reset;
*/
for(int i = 0; i < snow.size(); i++){
if(snow.get(i).getX() > holder.getSurfaceFrame().width()){
snow.get(i).setX(-65);
}
if(snow.get(i).getY() > holder.getSurfaceFrame().height()){
snow.get(i).setY(-69);
}
}
// Test if the array was just create; true - randomly populate snowflakes on screen
if(snow.get(1).getX() < -70){
for(int i = 0; i < snow.size(); i++){
snow.get(i).setX((int)(Math.random() * getSurfaceHolder().getSurfaceFrame().width() +1));
snow.get(i).setY((int)(Math.random() * getSurfaceHolder().getSurfaceFrame().height() + 1));
}
}
// Change snowflake x & y
for(int i = 0; i < snow.size(); i++){
snow.get(i).delta();
}
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
// call to draw new snow position
drawSnow(c);
}
} finally {
if (c != null) holder.unlockCanvasAndPost(c);
}
// Reschedule the next redraw
mHandler.removeCallbacks(mDrawSnow);
if (mVisible) {
mHandler.postDelayed(mDrawSnow, 1000 / 100);
}
}
/*
* Draw the snowflakes
*/
void drawSnow(Canvas c) {
c.save();
// Draw bg
//********** add code to pull current bg and draw that instead of black. Maybe set this in config?
if(drawWall == null){
c.drawColor(Color.BLACK);
}else{
drawWall.copyBounds(wallBounds);
drawWall.draw(c);
}
/*
* draw up the snow
*/
for(int i = 0; i < snow.size(); i++){
c.drawBitmap(snow.get(i).getImage(), snow.get(i).getX(), snow.get(i).getY(), null);
}
c.restore();
}
}
}
Same question as Gabe - what's the problem?
Some general thoughts:
* You should avoid doing lots of work in a constructor. Your constructor does a ton of work that should (imho) be in some init/setup method instead. Easier to benchmark / profile there independently from instance creation.
You're using Math.random in many places - I assume you are singly threaded, but Math.random is synchronized. Per the javadocs: " if many threads need to generate pseudorandom numbers at a great rate, it may reduce contention for each thread to have its own pseudorandom-number generator"
You're using Math.random which gets you a double, then multiplying, then adding, then casting. This looks wasteful. Any way to get fewer ops?
You seem to have some division - see line "mHandler.postDelayed(mDrawSnow, 1000 / 100);". Sure, that's probably compiled or JIT'd away, but you should avoid division in performance critical code (it's far slower than multiplying). So any div by a constant can be replaced by multiplying by 1 / C as a static.
You have lots of repeat accessor method calls (in some cases nearly all are repeat). See snippit:
for(int i = 0; i < snow.size(); i++){
if(snow.get(i).getX() > holder.getSurfaceFrame().width()){
snow.get(i).setX(-65);
}
if(snow.get(i).getY() > holder.getSurfaceFrame().height()){
snow.get(i).setY(-69);
}
}
You should store "holder.getSurfaceFrame().width() in a temporary / local var (perhaps once per draw loop assuming your surface is resizable by the user). You might also store snow.get(i) in a local var. Better yet (style) you can use the enhanced for loop as snow is an ArrayList. So use
for (Snow mySnow : snow) {
// Do something with mySnow
}
Hope this helps. Good luck!