I am currently writting a a spectrum analyzer for android for university and part of this involves plotting the FFT of sound. However, I am having an issue with plotting the frequencies. The freq values start off correct, but as i move to higher frequencies the error is becoming greater and greater (at 3000Hz, the graph will show ~3750). I feel as though there is an error in the way I am calculating the x-axis or freq values. This is a manually drawn graph for speed purposes.
If more info/code is needed just let me know, but my guess is that it is something simple that I have overlooked. Thanks
xVal is the frequency value. and the scale value is to scale it according to the real graph dimensions.
int length = currentWaveDataDouble.length;
int pow2 = Integer.highestOneBit(length) << 1;
int sampleRate = 44100;
...
//actual plot part
for(int i =0; i<p2.length; i++) {
float xVal = (float)(i * scaleX.ScaleValue(((double) sampleRate / (pow2 >> 1))));
if (xVal < maxFreqPlus1) {
xVal += axisWidth + yAxisMargin;
float yVal = (float) scaleY.ScaleValue(p2[i]);
yVal += axisWidth + xAxisMargin;
canvas.drawPoint(xVal,yVal, marker);
if(yVal > yMax)
{
yMax = yVal;
xMax = xVal;
}
}
}
Freq generator set to 4000 Hz
Freq generator set to 1000 Hz (value is 1250Hz)
Found the issue. it was in the scaler.
ValueScaler scaleY = new ValueScaler(0,maxAmpPlus1 - yAxisMargin,0,baseY);
ValueScaler scaleX = new ValueScaler(0,maxFreqPlus1 - xAxisMargin,0,baseX);
i wasn't taking into account the x and y margin when scaling the numbers.
I've been assigned to make a simple android app game in which the user controls a ball on the screen by tilting the phone. The app utilizes a custom view to draw all the objects and it uses a Runnable object to animate the ball. Note that x and y represent the ball's postilion, vx and vy represent the ball's velocity and fx and fy represent the forces being applied to it. ix and iy represent the phone's tilt; I set these two to dummy values in order to test the app without worrying about setting up the sensor manager for the time being :
private Runnable animator = new Runnable() {
#Override
public void run() {
boolean needNewFrame = false;
long now = AnimationUtils.currentAnimationTimeMillis();
float dt = Math.min(now - lastTime, 50) / 1000f;
fx = - alpha * vx + beta * ix;
fy = - alpha * vy + beta * iy;
vx = fx * dt;
vy = fx * dt;
if(x<xMin||x>xMax-ball_radius||y<yMin||y>yMax-ball_radius)
bounce();
x = x + vx * dt;
y = y + vy * dt;
lastTime = now;
needNewFrame = true;
postDelayed(this, 20);
invalidate();
}
};
I've set up the animator like so but it won't run. I tried calling post(animator) on the views initialization but that didn't work. How do I fix this?
Also, how do I set the ix and iy variables from the phone's tilt? From what I understand, the Sensor Manager is meant to be setup from an activity class.
Actually, the animation does play successfully. It's just that the animation was playing very slowly.
I have a circular overlay that can change according to the user's preferences: they have a circle of radius 'r' around them and a slider can change 'r' accordingly. So far it works perfectly well.
My problem is I don't know how to find the proper conversion from the circle's radius to the map's metric. As an example, given the circle below, what is the distance covered with the given radius?
You must know what is the scale of the map you are showing. Having scale of the map you could convert pixels size to metric size. And having this proportion you can convert radius given in pixels to meters.
So here's how you do it...
Jedil is correct you have to find your screen width/height, the dpi of the screen, etc. So, looking through this old osmdroid source I sorta figured out this:
//Get the x and y dpi
this.xdpi = this.context.getResources().getDisplayMetrics().xdpi;
this.ydpi = this.context.getResources().getDisplayMetrics().ydpi;
//Get the screen width/height
this.screenWidth = this.context.getResources().getDisplayMetrics().widthPixels;
this.screenHeight = this.context.getResources().getDisplayMetrics().heightPixels;
// DPI corrections for specific models
String manufacturer = null;
try {
final Field field = android.os.Build.class.getField("MANUFACTURER");
manufacturer = (String) field.get(null);
} catch (final Exception ignore) {
}
if ("motorola".equals(manufacturer) && "DROIDX".equals(android.os.Build.MODEL)) {
// If the screen is rotated, flip the x and y dpi values
WindowManager windowManager = (WindowManager) this.context
.getSystemService(Context.WINDOW_SERVICE);
if (windowManager.getDefaultDisplay().getOrientation() > 0) {
this.xdpi = (float) (this.screenWidth / 3.75);
this.ydpi = (float) (this.screenHeight / 2.1);
} else {
this.xdpi = (float) (this.screenWidth / 2.1);
this.ydpi = (float) (this.screenHeight / 3.75);
}
} else if ("motorola".equals(manufacturer) && "Droid".equals(android.os.Build.MODEL)) {
// http://www.mail-archive.com/android-developers#googlegroups.com/msg109497.html
this.xdpi = 264;
this.ydpi = 264;
}
// set default max length to 1 inch
maxLength = 2.54f;
That's how you get the 'constants' (at least in the device's eyes). To convert...
// calculate dots per centimeter
int xdpcm = (int) ((float) xdpi / 2.54);
int ydpcm = (int) ((float) ydpi / 2.54);
// get length in pixel
int xLen = (int) (maxLength * xdpcm);
int yLen = (int) (maxLength * ydpcm);
// Two points, xLen apart, at scale bar screen location
IGeoPoint p1 = projection.fromPixels((screenWidth / 2) - (xLen / 2), yOffset);
IGeoPoint p2 = projection.fromPixels((screenWidth / 2) + (xLen / 2), yOffset);
// get distance in meters between points
final int xMeters = ((GeoPoint) p1).distanceTo(p2);
...and it's a similar, if not near-identical, matter to get the yMeters (hint: use screenHeight).
Admittedly, I'm not totally sure of what the lines IGeoPoint... are doing but I realize the conversion is there. Hoping this helps someone out in the future. For a better understanding of the above code, please see the link I've posted.
I think what you're looking for is a meters-to-pixels calculation. You can get this from the projection:
// Get projection
Projection proj = mMapView.getProjection();
// How many pixels in 100 meters for this zoom level
float pixels = proj.metersToPixels(100);
// How many meters in 100 pixels for this zoom level
float meters = 1 / proj.metersToPixels(1 / 100);
// You could also get a raw meters-per-pixels value by using TileSystem.GroundResolution()
Two things to remember - this value will change not only based on what zoom level but based on what latitude you are at on the maps.
i am building an app that counts the steps using accelerometer for android devices.(android version< .4.4)
to do this i calculate the gravity (G) using x,y and z. if the value of G is larger than STEP_THRESHOLD then an step has been occured:
#Override
public void onSensorChanged(SensorEvent event) {
synchronized (this) {
if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) {
return;
}
final float x = event.values[0];
final float y = event.values[1];
final float z = event.values[2];
final float g = Math.abs((x * x + y * y + z * z) / (SensorManager.GRAVITY_EARTH * SensorManager.GRAVITY_EARTH));
if (g > StepUtil.GRAVITY_THRESHOLD) {
/*
* check step times and other checkings
*/
.
.
.
stepCounter++;
}
}
}
the problem is:
since accelerometer hardwares is varies in each device, there is not a certain value for STEP_THRESHOLD.
it seems that the STEP_THRESHOLD value should be dynamic for each device!
is there any criterion to change STEP_THRESHOLD value base on accelerometer accuracy?
any help will be appreciated.
since accelerometer hardwares is varies in each deviceŁ there is not a certain value for STEP_THRESHOLD
Having that said, what your app is apparently missing is simple calibration, which would allow your users to set the threshold level on their device. Tell them to do couple of steps, take sensor values, take mean/median/average (whatever will work for you better) and you got your STEP_THRESHOLD. You can even send this value back to your server so you can build kind of database and come with some more/less universtal starting value of STEP_AVERAGE.
I'm implementing a kind of a "compass arrow" that follows destination depending on physical orientation of the device using magnetic field sensor. Suddenly I faced with a little problem.
Obtaining bearing and azimuth is OK, but performing a realistic animation turned into a really hard task. I tried to use different interpolators to make animation more "physical" (i. e. as in real compass, which arrow oscillate after hairpin rotation, accelerate and decelerate during movement etc).
Now I'm using interpolator.accelerate_decelerate and everything is quite good until updates start arriving quickly. That makes animations overlap each other and the arrow becomes twitchy and nervous. I want to avoid this. I tried to implement a queue to make every next animation wait until previous ends, or drop updates that come very quickly. That made animation look smooth, but arrow's behavior turned into absolutely illogical.
So I have 2 questions:
1) is there some way to make animated transitions more smooth in the cases when animations overlap each other?
2) is there a way to stop animation that is currently processing and get intermediate position of an object?
My code is below. An UpdateRotation() method handles orientation and bearing updates and executes animation of external viewArrow view.
public class DirectionArrow {
// View that represents the arrow
final View viewArrow;
// speed of rotation of the arrow, degrees/sec
final double rotationSpeed;
// current values of bearing and azimuth
float bearingCurrent = 0;
float azimuthCurrent = 0;
/*******************************************************************************/
/**
* Basic constructor
*
* #param view View representing an arrow that should be rotated
* #param rotationSpeed Speed of rotation in deg/sec. Recommended from 50 (slow) to 500 (fast)
*/
public DirectionArrow(View view, double rotationSpeed) {
this.viewArrow = view;
this.rotationSpeed = rotationSpeed;
}
/**
* Extended constructor
*
* #param viewArrow View representing an arrow that should be rotated
* #param rotationSpeed Speed of rotation in deg/sec. Recommended from 50 (slow) to 500 (fast)
* #param bearing Initial bearing
* #param azimuth Initial azimuth
*/
public DirectionArrow(View viewArrow, double rotationSpeed, float bearing, float azimuth){
this.viewArrow = viewArrow;
this.rotationSpeed = rotationSpeed;
UpdateRotation(bearing, azimuth);
}
/**
* Invoke this to update orientation and animate the arrow
*
* #param bearingNew New bearing value, set >180 or <-180 if you don't need to update it
* #param azimuthNew New azimuth value, set >360 or <0 if you don't need to update it
*/
public void UpdateRotation(float bearingNew, float azimuthNew){
// look if any parameter shouldn't be updated
if (bearingNew < -180 || bearingNew > 180){
bearingNew = bearingCurrent;
}
if (azimuthNew < 0 || azimuthNew > 360){
azimuthNew = azimuthCurrent;
}
// log
Log.println(Log.DEBUG, "compass", "Setting rotation: B=" + bearingNew + " A=" + azimuthNew);
// calculate rotation value
float rotationFrom = bearingCurrent - azimuthCurrent;
float rotationTo = bearingNew - azimuthNew;
// correct rotation angles
if (rotationFrom < -180) {
rotationFrom += 360;
}
while (rotationTo - rotationFrom < -180) {
rotationTo += 360;
}
while (rotationTo - rotationFrom > 180) {
rotationTo -= 360;
}
// log again
Log.println(Log.DEBUG, "compass", "Start Rotation to " + rotationTo);
// create an animation object
RotateAnimation rotateAnimation = new RotateAnimation(rotationFrom, rotationTo,
Animation.RELATIVE_TO_SELF, (float) 0.5, Animation.RELATIVE_TO_SELF, (float) 0.5);
// set up an interpolator
rotateAnimation.setInterpolator(viewArrow.getContext(), interpolator.accelerate_decelerate);
// force view to remember its position after animation
rotateAnimation.setFillAfter(true);
// set duration depending on speed
rotateAnimation.setDuration((long) (Math.abs(rotationFrom - rotationTo) / rotationSpeed * 1000));
// start animation
viewArrow.startAnimation(rotateAnimation);
// update cureent rotation
bearingCurrent = bearingNew;
azimuthCurrent = azimuthNew;
}
}
Here is my custom ImageDraw class where I implemted physical behavior of the pointing arrow based on equation of circular motion of dipole in magnetic field.
It don't uses any animators nor interpolators--on every iteration angular position is recalculated based on physical parameters. These parameters can be widely adjusted via setPhysical method. For example, to make rotations more smooth and slow, increase alpha (damping coefficient), to make arrow more responsitive, increase mB (coefficient of magnetic field), to make arrow oscillate on rotations, increase inertiaMoment.
Animation and redraw is performed implicitly by invoke of invalidate() on every iteration. There is no need to handle it explicitly.
To update the angle at which the arrow should rotate, just call rotationUpdate (by user's choice or using device orientation sensor callback).
/**
* Class CompassView extends Android ImageView to perform cool, real-life animation of objects
* such compass needle in magnetic field. Rotation is performed relative to the center of image.
*
* It uses angular motion equation of magnetic dipole in magnetic field to implement such animation.
* To vary behaviour (damping, oscillation, responsiveness and so on) set various physical properties.
*
* Use `setPhysical()` to vary physical properties.
* Use `rotationUpdate()` to change angle of "magnetic field" at which image should rotate.
*
*/
public class CompassView extends ImageView {
static final public float TIME_DELTA_THRESHOLD = 0.25f; // maximum time difference between iterations, s
static final public float ANGLE_DELTA_THRESHOLD = 0.1f; // minimum rotation change to be redrawn, deg
static final public float INERTIA_MOMENT_DEFAULT = 0.1f; // default physical properties
static final public float ALPHA_DEFAULT = 10;
static final public float MB_DEFAULT = 1000;
long time1, time2; // timestamps of previous iterations--used in numerical integration
float angle1, angle2, angle0; // angles of previous iterations
float angleLastDrawn; // last drawn anglular position
boolean animationOn = false; // if animation should be performed
float inertiaMoment = INERTIA_MOMENT_DEFAULT; // moment of inertia
float alpha = ALPHA_DEFAULT; // damping coefficient
float mB = MB_DEFAULT; // magnetic field coefficient
/**
* Constructor inherited from ImageView
*
* #param context
*/
public CompassView(Context context) {
super(context);
}
/**
* Constructor inherited from ImageView
*
* #param context
* #param attrs
*/
public CompassView(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* Constructor inherited from ImageView
*
* #param context
* #param attrs
* #param defStyle
*/
public CompassView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* onDraw override.
* If animation is "on", view is invalidated after each redraw,
* to perform recalculation on every loop of UI redraw
*/
#Override
public void onDraw(Canvas canvas){
if (animationOn){
if (angleRecalculate(new Date().getTime())){
this.setRotation(angle1);
}
} else {
this.setRotation(angle1);
}
super.onDraw(canvas);
if (animationOn){
this.invalidate();
}
}
/**
* Use this to set physical properties.
* Negative values will be replaced by default values
*
* #param inertiaMoment Moment of inertia (default 0.1)
* #param alpha Damping coefficient (default 10)
* #param mB Magnetic field coefficient (default 1000)
*/
public void setPhysical(float inertiaMoment, float alpha, float mB){
this.inertiaMoment = inertiaMoment >= 0 ? inertiaMoment : this.INERTIA_MOMENT_DEFAULT;
this.alpha = alpha >= 0 ? alpha : ALPHA_DEFAULT;
this.mB = mB >= 0 ? mB : MB_DEFAULT;
}
/**
* Use this to set new "magnetic field" angle at which image should rotate
*
* #param angleNew new magnetic field angle, deg., relative to vertical axis.
* #param animate true, if image shoud rotate using animation, false to set new rotation instantly
*/
public void rotationUpdate(final float angleNew, final boolean animate){
if (animate){
if (Math.abs(angle0 - angleNew) > ANGLE_DELTA_THRESHOLD){
angle0 = angleNew;
this.invalidate();
}
animationOn = true;
} else {
angle1 = angleNew;
angle2 = angleNew;
angle0 = angleNew;
angleLastDrawn = angleNew;
this.invalidate();
animationOn = false;
}
}
/**
* Recalculate angles using equation of dipole circular motion
*
* #param timeNew timestamp of method invoke
* #return if there is a need to redraw rotation
*/
protected boolean angleRecalculate(final long timeNew){
// recalculate angle using simple numerical integration of motion equation
float deltaT1 = (timeNew - time1)/1000f;
if (deltaT1 > TIME_DELTA_THRESHOLD){
deltaT1 = TIME_DELTA_THRESHOLD;
time1 = timeNew + Math.round(TIME_DELTA_THRESHOLD * 1000);
}
float deltaT2 = (time1 - time2)/1000f;
if (deltaT2 > TIME_DELTA_THRESHOLD){
deltaT2 = TIME_DELTA_THRESHOLD;
}
// circular acceleration coefficient
float koefI = inertiaMoment / deltaT1 / deltaT2;
// circular velocity coefficient
float koefAlpha = alpha / deltaT1;
// angular momentum coefficient
float koefk = mB * (float)(Math.sin(Math.toRadians(angle0))*Math.cos(Math.toRadians(angle1)) -
(Math.sin(Math.toRadians(angle1))*Math.cos(Math.toRadians(angle0))));
float angleNew = ( koefI*(angle1 * 2f - angle2) + koefAlpha*angle1 + koefk) / (koefI + koefAlpha);
// reassign previous iteration variables
angle2 = angle1;
angle1 = angleNew;
time2 = time1;
time1 = timeNew;
// if angles changed less then threshold, return false - no need to redraw the view
if (Math.abs(angleLastDrawn - angle1) < ANGLE_DELTA_THRESHOLD){
return false;
} else {
angleLastDrawn = angle1;
return true;
}
}
Are you filtering your sensor data? The Magnetometer is a pain low pass filtering isn't really enough. You could use weighted-smoothing or maybe rounding data would be helpful:
Math.round( xyz * 10) / 10; ?
You could also reduce the frequency at which you get sensor updates. That might help.
mSensorManager.registerListener(this, mMagnetometer, 10000);
Espessially for gilonm, nice implementation of fixed size queue and getting its mean value:
float queue[ARRAY_LENGTH] = {0};
int queueFront = queue.length - 1 // position of front element
float meanValue = 0; // calculated mean value
float pushNewAndGetMean(float newValue){
// recalculate mean value
meanValue = meanValue + (newValue - queue[queueFront]) / queue.length;
// overwrite value in front pointer position
queue[queueFront] = newValue;
// shift front pointer 1 step right or to '0' if end of array reached
queueStart = (queueFront + 1) % array.length;
return meanValue
};
Here, not dependent on array length, you make just 2 reassignments of variables (instead of N) and use only 3 elements in mean calculation (instead of N). This makes algorithm O(1) complexity instead of O(N).
What you could do is where you get your data from the sensors - you can just use and array to do an average of say last 5 readings - that should smooth things down.
something like this:
Declare an array private float azimArray[] = {0,0,0,0,0};
Now where you get sensor data, use:
azimArray[0] = azimArray[1];
azimArray[1] = azimArray[2];
azimArray[2] = azimArray[3];
azimArray[3] = azimArray[4];
azimArray[4] = event.values[0]; //get actual sensor data into last array cell
currentAzimuth = Math.round(azimArray[0]+azimArray[1]+azimArray[2]+azimArray[3]+azimArray[4]/5);
Now currentAzimuth holds the rounded average of last 5 readings, which should smooth things down for you.
Hope this helped!