I'm trying to utilize the max fifo size of Accelerometer on Nexus 6
SensorManager sensorManager =
(SensorManager) getSystemService(Context.SENSOR_SERVICE);
Sensor sensor = sensorManager.getDefaultSensor(typeAccelerometer);
Log.e("test",
"Max delay: " + sensor.getMaxDelay() + " - Fifo count" + sensor.getFifoReservedEventCount()); // prints 1000000 (1 second) - Fifo count 10000
// Register the listener for this sensor in batch mode.
// Following code reports every 190ms when screen is ON, and every 10 seconds when screen is OFF. I always want every 10 seconds.
final boolean batchMode = sensorManager.registerListener(
mListener, sensor, 1000000 /* 1 second */, 10000000 /* 10 seconds */);
private final SensorEventListener mListener = new SensorEventListener() {
long lastTimeStamp;
#Override
public void onSensorChanged(SensorEvent event) {
long current = System.currentTimeMillis();
long time = current - lastTimeStamp;
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
Log.e("test",
"New ACCELERO "+ " -> "+ time + "ms -> " + (int)event.values[0] + " -> "+ (int)event.values[1] +" -> "+ (int)event.values[2]);
lastTimeStamp = current;
}
}
#Override
public void onAccuracyChanged(Sensor s, int accuracy) {
}
};
When Screen is ON, I get the events every 190ms. However, when screen is off, it obeys 10 seconds.
How do I utilize the full batching FIFO (with delayed listeners) when screen is ON, for minimal CPU impact.
Code above is fine. On Nexus 6, accelerometer doesnt batch as long as screen is ON. All other sensors do fine.
sensor.getFifoReservedEventCount() returns the amount of values it can store, i dont think its time in ms
Beware of the third argument to registerListener. It is not in seconds like you wrote:
final boolean batchMode = sensorManager.registerListener(
mListener, sensor, 1000000 /* WRONG */, 10000000);
It should be one of the following constants:
SENSOR_DELAY_NORMAL
SENSOR_DELAY_UI
SENSOR_DELAY_GAME
SENSOR_DELAY_FASTEST.
Source: https://developer.android.com/reference/android/hardware/SensorManager.html#registerListener(android.hardware.SensorEventListener,%20android.hardware.Sensor,%20int,%20int)
Related
eg. I have a 1.5 GB data pack. It gives the total sum of 2.0 GB or more than that .
any idea about how to get correct speed every second.
TrafficStats.getTotalRxBytes() does not return your data pack value. It refers to the total received bytes (either wifi/mobile) since the last boot (turning ON phone). For mobile data, it will be TrafficStats.getMobileRxBytes(). More importantly, these values get reset in every reboot of device.
I have a 1.5 GB data pack. It gives the total sum of 2.0 GB or more
than that .
The android system does not know anything about your data pack. You are adding it again and again. When you call TrafficStats.getMobileRxBytes() at a moment, it returns total mobile data received upto this moment since last boot. Following is an explanation. Hope this helps.
// Suppose, you have just rebooted your device, then received 400 bytes and transmitted 300 bytes of mobile data
// After reboot, so far 'totalReceiveCount' bytes have been received by your device over mobile data.
// After reboot, so far 'totalTransmitCount' bytes have been sent from your device over mobile data.
// Hence after reboot, so far 'totalDataUsed' bytes used actually.
long totalReceiveCount = TrafficStats.getMobileRxBytes();
long totalTransmitCount = TrafficStats.getMobileTxBytes();
long totalDataUsed = totalReceiveCount + totalTransmitCount;
Log.d("Data Used", "" + totalDataUsed + " bytes"); // This will log 700 bytes
// After sometime passed, another 200 bytes have been transmitted from your device over mobile data.
totalDataUsed = TrafficStats.getMobileRxBytes() + TrafficStats.getMobileTxBytes();
Log.d("Data Used", "" + totalDataUsed + " bytes"); // Now this will log 900 bytes
any idea about how to get correct speed every second.
You cannot get actual speed this way. You can only calculate and show how much bytes have been received/transmitted in a second. All the speed meters in android do the same I think. Something like the following:
class SpeedMeter {
private long uptoNow = 0;
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private ScheduledFuture futureHandle;
public void startMeter() {
final Runnable meter = new Runnable() {
public void run() {
long now = TrafficStats.getMobileRxBytes() + TrafficStats.getMobileTxBytes();
System.out.println("Speed=" + (now - uptoNow)); // Prints value for current second
uptoNow = now;
}
};
uptoNow = TrafficStats.getMobileRxBytes() + TrafficStats.getMobileTxBytes();
futureHandle = scheduler.scheduleAtFixedRate(meter, 1, 1, SECONDS);
}
public void stopMeter() {
futureHandle.cancel(true);
}
}
And use like this:
SpeedMeter meter = new SpeedMeter();
meter.startMeter();
Although this code is not perfect, however it will suit your needs.
I am making a location-based app, that has a compass which is pointing to some Long and Lat coordinates. I need to access my own coordinates, to be able to calculate the rotation my compass needs, but the problem is - my unity location service is timing out. This apps location sensitivity is 3 meters.
I created a second app, which is just showing you your Long and Lat coordinates with location sensitivity of 5 meters and it works almost instantly. Here is the code of my first project:
IEnumerator Start()
{
// First, check if user has location service enabled
if (!Input.location.isEnabledByUser)
{
ErrorHandler.ShowMessageError("Location service not enabled");
isLoopAllowed = false;
yield break;
}
// Start service before querying location
Input.location.Start(Storage.LOCATION_SENSITIVITY, Storage.LOCATION_SENSITIVITY);
// Wait until service initializes
int maxWait = 20;
while (Input.location.status == LocationServiceStatus.Initializing && maxWait > 0)
{
yield return new WaitForSeconds(1);
maxWait--;
}
// Service didn't initialize in 20 seconds
if (maxWait < 1)
{
ErrorHandler.ShowMessageError("Timed out");
isLoopAllowed = false;
yield break;
}
// Connection has failed
if (Input.location.status == LocationServiceStatus.Failed)
{
ErrorHandler.ShowMessageError("Can't locate your device");
isLoopAllowed = false;
yield break;
}
else
{
// Access granted and location value could be retrieved
//print("Location: " + Input.location.lastData.latitude + " " + Input.location.lastData.longitude + " " + Input.location.lastData.altitude + " " + Input.location.lastData.horizontalAccuracy + " " + Input.location.lastData.timestamp);
Storage.USER_LATITUDE = Input.location.lastData.latitude;
Storage.USER_LONGITUDE = Input.location.lastData.longitude;
Storage.LOCATION_READY = true;
}
}
Some intro into the script:
isLoopKeyAllowed variable is for stopping the loop in case of an error.
Storage class is just a class with global variables.
ErrorHandler class has a function called ShowMessageError() thats just outputting the error on the screen.
LOCATION_READY is a varible that is being used by the compass script.
and thats it. I don't know whats the problem. Maybe it is caused because the sensitivity is too high?
(EDIT): The problem is, that the Timed Out error is shown and the loop has ended.
I have an app that stores sensor data into a database every 10ms, but the signal itself has some very sharp peaks:
So it doesn't look like the signal is necessarily noisy, its just around the peaks there aren't enough data points surrounding the peak itself to smooth the peak out
I know I can apply a filter to smooth the signal out, but I'm wondering whether there is anything that I'm doing wrong when polling the sensors/storing into the database that is causing this behaviour
I would have thought that by using a short polling rate like 10ms would give me more data around the peaks to help smooth the peaks out, but reducing that value POLL_FREQUENCY doesn't seem to give me smooth data and I'm not sure why?
I'm collecting the data in a service:
public void onSensorChanged(SensorEvent event) {
sensor = event.sensor;
int i = sensor.getType();
if (i == MainActivity.TYPE_ACCELEROMETER) {
accelerometerMatrix = event.values;
} else if (i == MainActivity.TYPE_GYROSCOPE) {
gyroscopeMatrix = event.values;
} else if (i == MainActivity.TYPE_GRAVITY) {
gravityMatrix = event.values;
} else if (i == MainActivity.TYPE_MAGNETIC) {
magneticMatrix = event.values;
}
long curTime = System.currentTimeMillis();
long diffTime = (curTime - lastUpdate);
// only allow one update every POLL_FREQUENCY (10ms)
if(diffTime > POLL_FREQUENCY) {
lastUpdate = curTime;
//insert into database in background thread
try{
//this simply takes the value from accelerometerMatrix
// and the runnable just inserts into the database as a new row
executor.execute(insertHandler);
} catch (SQLException e) {
Log.e(TAG, "insertData: " + e.getMessage(), e);
}
}
}
So all this code does is wait for a sensor change event, detect the type of event it is (accelerometer, gyro etc), store the value into the appropriate matrix, and if more than 10ms has elapsed, store the matrix values into the database
Can anyone suggest why the peaks are sharp despite using a short poll frequency (10ms)?
I am struggling with the accelerometer sampling rate of my app.
So I decided to develope a little app just for testing the sampling rate.
Are there any mistakes in my code? The sampling rate is pretty much changing :-/
private PowerManager.WakeLock mWakeLock;
private TextView infotext;
private SensorManager sensorManager;
private long nanoNow;
private long nanoOld;
private long nanodiffsum=0;
private int i=0;
private int values=2000;
private long[] nanodiff=new long[values];
DescriptiveStatistics myDescrStat = new DescriptiveStatistics();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int tid=android.os.Process.myTid()
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
infotext = (TextView) findViewById(R.id.textView);
sensorManager=(SensorManager)getSystemService(SENSOR_SERVICE);
sensorManager.registerListener(this,sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),SensorManager.SENSOR_DELAY_FASTEST);
final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag");
mWakeLock.acquire();
}
public void onSensorChanged(SensorEvent event){
if(event.sensor.getType()==Sensor.TYPE_ACCELEROMETER){
if (i==0){
nanoOld=event.timestamp;
}
if (i == values) {
for (int i=1; i<nanodiff.length; i++){
myDescrStat.addValue(nanodiff[i]);
nanodiffsum+=nanodiff[i];
}
infotext.setText(
"Average:" + nanodiffsum / values + "ms\n" + " = " + (double) 1000 / (nanodiffsum /values) + "Hz" + "\n" +
"mean: " + 1000 / myDescrStat.getMean() + "\n" +
"std:" + 1000 / myDescrStat.getStandardDeviation() + "\n" +
"max:" + 1000 / myDescrStat.getMin() + "\n" +
"min:" + 1000 / myDescrStat.getMax() + "\n");
mWakeLock.release();
sensorManager.unregisterListener(this);
}else{
nanoNow=event.timestamp;
nanodiff[i]=(nanoNow-nanoOld)/1000000;
if (i%20==0)
infotext.setText("Aktuell: " + nanodiff[i] + "ms" + " = " + Math.round((double)1000/ nanodiff[i]) + "Hz" );
nanoOld=nanoNow;
i++;
}
}
}
Kindly regards,
Arthur
After a cursory look at your code I cannot see anything wrong with it (except perhaps the overzealous update of the thread priority, which makes sense in a testing app but I wouldn't use without a good reason in actual production code).
As for the irregular rate of the the SensorManager's data (spoiler alert: the same behavior applies to other sensor streams ;-) ), it is meant to be that way, and the frequency specified with SensorManager.SENSOR_DELAY_FASTEST is meant to be only a hint (which is the reason why actually you receive the time of the measurement with such accuracy at each event). This is stated in the official documentation:
rateUs : The rate sensor events are delivered at. This is only a hint to the system.
Events may be received faster or slower than the specified rate. Usually events are received faster. Source: registerListener reference page
For one part, keep in mind that Android is Linux at the core, and linux is not a real-time system (and therefore can't provide data with realtime accuracy); on the other side, Android has to cope with a lot of different hardware implementations (and sensor brands), therefore the Android developers team has probably done a wise choice to avoid any kind of commitment about the ratio of the sensor data.
Those are my 50 cents, if you want to get serious about Android Sensors programming I would suggest this book:
Professional Android Sensor Programming (Greg Milette, Adam Stroud)
I read about 1/4 of it and so far, so good (I'm in no way related to the publisher or the author).
Hope this helps
I have an ArrayList of LinkedList to store my sensors captured data in a thread ( using synchronized on the ArrayList object)
upon capture, sensor events are added, I can see the linkedlist increasing, but the first and last element are ALWAYS the last capture event .. something is wrong somewhere ... ?
// main thread ('samplingFifoQueues' passed as an arg to be synchronized ...)
public volatile LinkedList<SensorEvent> accelFifoQueue = new LinkedList<SensorEvent>();
public volatile LinkedList<SensorEvent> magnetFifoQueue = new
.......
public volatile ArrayList<LinkedList<SensorEvent>> samplingFifoQueues = new ArrayList<LinkedList<SensorEvent>>();
In my capture thread , I add the sensor events
LinkedList<SensorEvent> accelFifoQueue;
LinkedList<SensorEvent> magnetFifoQueue;
......
ArrayList<LinkedList<SensorEvent>> samplingFifoQueues;
.....
public void onSensorChanged(SensorEvent sensorEvent) {
...
synchronized (samplingFifoQueues) {
switch (sensorEvent.sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
Log.d(TAG, "new accel sensor event at: " + sensorEvent.timestamp );
accelFifoQueue.add(sensorEvent);
SensorEvent nse = accelFifoQueue.getLast();
SensorEvent lse = accelFifoQueue.getFirst();
Log.d(TAG, "accel: " + accelFifoQueue.size() + " elements, last at: " + nse.timestamp + " , first at: " + lse.timestamp);
break;
....
The log output indicate first and last timestamp after each new event , and the first is ALWAYS = to the last, even if the size is increasing :
new accel sensor event at: 1391793870096061895
accel: 1 elements, last at: 1391793870096061895 , first at: 1391793870096061895
new accel sensor event at: 1391793870117302130
accel: 2 elements, last at: 1391793870117302130 , first at: 1391793870117302130
new accel sensor event at: 1391793870121208380
accel: 3 elements, last at: 1391793870121208380 , first at: 1391793870121208380
new accel sensor event at: 1391793870129020880
accel: 4 elements, last at: 1391793870129020880 , first at: 1391793870129020880
as per answer in post : How to make a copy of the Android Sensor SensorEvent Object
the sensorEvent is overwritten... it's a system class so I need to do my own clone object...
This is because you are using ArrayList, and ArrayList was stored sensor event objet's reference, so, all you added to this array list may be the same object's reference, this is why elements in array list are ALWAYS the last capture event.