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
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.
To substitute my usage of native apps that allow to keep track of my position, I wanted to generate a PWA using the HTML5 Goelocation API.
The result I have been able to achieve so far seems to point a inferior functionality of the HTML5 Goelocation API compared to native Android API.
Below is the code I have used and the issue is, that the PWA/ website application only receives infrequent updates. Additionally the app only receives position while the screen is not off.
This puts a huge obstacle into having a PWA being to track for instance my bike tour, since I cannot keep the screen and browser in the foreground, while ideed I wished the PWA would simply continues running even when the screen is off.
Now I am aware that in most cases a device user and privacy aware person would benefit from the useragent/browser to cut the waste of resources and limit privacy loss by disabling the very feature I search.
In essence however I have looked over the MDN documentation and besides the PositionOptions I was yet unable to find any clue about the guarantees of the API.
Find below the way I have sought to make it work as a code.
Does + Should HTML5 Goelocation API work when screen of on a mobile?
Is there a concrete information about if and how much geolocation information is returedn? like frequency/delay of update and like geofencing imprecision ?
Could for instance google maps navigation work in the browser itself?
My platform is Gecko on Android. If better results could be achieved in Chromium on Android I would be happy to hear about that too.
On Android I use firefox. In it I wanted to have my website provide a page that keeps track of my movements via the geolocation API, in order to replace my strave.
window.addEventListener("load",function(){
var a= document.createElement("div");
var textarea = document.createElement("textarea");
//textarea.value="aaaa"
textarea.style.display="block"
textarea.style.minHeight="5cm"
textarea.style.width="100%"
document.body.appendChild(a);
document.body.appendChild(textarea);
if(confirm("reset data?")){
localStorage.clear() ;
}
localStorage.setItem("start"+Date.now(),startInfo);
var startInfo = Object.keys(localStorage).length+ " " +Date.now()+ " " + (Date.now() % 864000);
var lastTime=0,lastLat=0,lastLon=0,count=0,lastDistance=0;
var startTime = Date.now();
var distance = 0;
if('geolocation' in navigator) {
a.innerHTML="<h2>super wir haben geolocation!</h2>";
setInterval(()=>{
navigator.geolocation.getCurrentPosition((position) => {
// var a = document.createElement("div");
count++;
a.innerHTML="<h1>"+(((Date.now()-startTime)/1000)|0)+" "+((distance*100|0)/100)+"</h1><h3>"+startInfo+"</h3><h2>date="+(new Date()).toString()+"<br>lang="+position.coords.latitude+" long="+ position.coords.longitude+"</h2>";
var lat = ((position.coords.latitude * 10000000) | 0)
var lon = ((position.coords.longitude * 10000000) | 0)
var time = Date.now();
var deltaTime = time - lastTime;
var deltaLat = lat - lastLat;
var deltaLon = lon - lastLon;
if(Math.abs(deltaLat)>100000 || Math.abs(deltaLon) > 100000)
{
} else{
distance += Math.sqrt(deltaLat*deltaLat+deltaLon*deltaLon);
}
var deltaDistance = distance - lastDistance;
lastLat=lat;
lastLon=lon;
lastTime=time;
lastDistance=distance;
newline = (((Date.now()-startTime)/1000)|0) + " dist=" + distance + "("+deltaDistance+") lat=" + lat + "("+deltaLat+") lon=" + lon + "("+deltaLon+") ";
textarea.value = newline + "\n" + textarea.value;
localStorage.setItem("P"+(Date.now()%864000),deltaLat+" "+deltaLon+" "+deltaTime);
},function(){},{timeout:900});
},1000);
} else {
a.innerHTML="<h2> shit</h2>";
}
});
I'm trying to get use the magnetometer from this Sensor API but I'm not sure if I'm doing so correctly.
I copied and edited the example code from the their site into my test site;
let sensor = new Magnetometer();
sensor.start();
sensor.onreading = () => {
console.log("Magnetic field along the X-axis " + sensor.x);
console.log("Magnetic field along the Y-axis " + sensor.y);
console.log("Magnetic field along the Z-axis " + sensor.z);
document.getElementById("x").innerHTML = "X = " + sensor.x;
document.getElementById("y").innerHTML = "Y = " + sensor.y;
document.getElementById("z").innerHTML = "Z = " + sensor.z;
};
sensor.onerror = event => console.log(event.error.name, event.error.message);
But when I load the page it doesn't give me any readings. Checking the site on my laptop brings back this error message;
Uncaught ReferenceError: Magnetometer is not defined
at magnetometer.js:1
Any insight into this would be greatly appreciated.
I found the answer. After looking around I found that you need to go to chrome://flags/#enable-generic-sensor-extra-classes and enable Generic Sensor Extra Classes.
I'm not sure why this is the case but I am now getting the readings I was after.
When the android text to speech functionality translates audio waves to text, is it possible to determine the 'confidence levels' of spoken text? So for example, if someone speaks too far away from the mic and the android device picks up distorted sounds, would it both output translated text along with a low confidence interval to state it isn't sure how accurate that particular translation was.
if you are implementing the RecognitionListener examine this code clip from my onResults method.
#Override
public void onResults(Bundle results) {
String LOG = "SpeechRecognizerActivity"
Log.d(LOG, "onResults");
ArrayList<String> strlist = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
float [] confidence = results.getFloatArray(SpeechRecognizer.CONFIDENCE_SCORES);
for (int i = 0; i < strlist.size(); i++) {
Log.d(LOG, "result=" + strlist.get(i));
}
Log.d(LOG + " result", strlist.get(0));
if (confidence != null){
if (confidence.length > 0){
Log.d(LOG + " confidence", String.valueOf(confidence[0]));
} else {
Log.d(LOG + " confidence score not available", "unknown confidence");
}
} else {
Log.d(LOG, "confidence not found");
}
}
You won't see anything unless you add this to your recognizer intent:
iSpeechIntent.putExtra(RecognizerIntent.EXTRA_CONFIDENCE_SCORES, true);
Yes. In the returned Bundle, there's a float array called CONFIDENCE_SCORES. From the docs:
Key used to retrieve a float array from the Bundle passed to the onResults(Bundle) and onPartialResults(Bundle) methods. The array should be the same size as the ArrayList provided in RESULTS_RECOGNITION, and should contain values ranging from 0.0 to 1.0, or -1 to represent an unavailable confidence score.
Confidence values close to 1.0 indicate high confidence (the speech recognizer is confident that the recognition result is correct), while values close to 0.0 indicate low confidence.
This value is optional and might not be provided.
Please note that it is not guaranteed to be there. Check for it and use if present. Gamble if not.
Is there any way that I can, over a period of say a week, see how much CPU each app uses? The only way that I can think of is to repeatedly parse top output but that doesn't seem like the most efficient way to do it. Is there a better way?
To add some context, basically the traceview functionality of the SDK on the phone itself: http://developer.android.com/guide/developing/debugging/debugging-tracing.html
We did it parsing top output, couldn't think of a better way. We needed to monitor all applications, and trace is only good if you need information about your own as you have to add trace code to your application in order to use it.
The app consumes 5% of CPU when running once a sec, aggregating values, writing values to log file, and displaying results on screen.
Unfortunately code is not opened (yet).
One of the problems you'd face monitoring processes over that period of time is that they'll almost certainly be destroyed and created MANY times in that period - this makes it hard to gain useful figures.
If you're not a "reinventing the wheel" type - the free version of the popular SystemPanel App shows how much CPU time each process has used (and over what period) - the full version apparently offers a detailed history of both CPU and memory usage.
You can use the Debug class to monitor the processes
or use this function to calculate CPU usage
Use /proc/stat/ for the total CPU usage (for all CPU's) . For per process usage
private float readCpuUsage(int pid) {
try {
RandomAccessFile reader = new RandomAccessFile("/proc/" + pid + "/stat", "r");
String load = reader.readLine();
String[] toks = load.split(" ");
long idle1 = Long.parseLong(toks[5]);
long cpu1 = Long.parseLong(toks[2]) + Long.parseLong(toks[3]) + Long.parseLong(toks[4])
+ Long.parseLong(toks[6]) + Long.parseLong(toks[7]) + Long.parseLong(toks[8]);
try {
Thread.sleep(360);
} catch (Exception e) {}
reader.seek(0);
load = reader.readLine();
reader.close();
toks = load.split(" ");
long idle2 = Long.parseLong(toks[5]);
long cpu2 = Long.parseLong(toks[2]) + Long.parseLong(toks[3]) + Long.parseLong(toks[4])
+ Long.parseLong(toks[6]) + Long.parseLong(toks[7]) + Long.parseLong(toks[8]);
return (float)(cpu2 - cpu1) / ((cpu2 + idle2) - (cpu1 + idle1));
} catch (IOException ex) {
ex.printStackTrace();
}
return 0;
}
source
To get the list of running processes
ActivityManager mgr = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> allTasks = mgr.getRunningTasks(showLimit);
/* Loop through all tasks returned. */
for (ActivityManager.RunningTaskInfo aTask : allTasks)
{
Log.i("MyApp", "Task: " + aTask.baseActivity.getClassName());
}