has anybody already programmed a iphone compass heading tilt compensation?
i have got some approaches, but some help or a better solution would be cool!
FIRST
i define a vector Ev, calculated out of the cross product of Gv and Hv. Gv is a gravity vector i build out of the accelerometer values and Hv is an heading vector built out the magnetometer values.
Ev stands perpendicular on Gv and Hv, so it is heading to horizonatl East.
SECOND
i define a vector Rv, calculated out of the cross product Bv and Gv. Bv is my looking vector and it is defined as [0,0,-1]. Rv is perpendicular to Gv and Bv and shows always to the right.
THIRD
the angle between these two vectors, Ev and Rv, should be my corrected heading. to calculate the angle i build the dot product and thereof the arcos.
phi = arcos ( Ev * Rv / |Ev| * |Rv| )
Theoretically it should work, but maybe i have to normalize the vectors?!
Has anybody got a solution for this?
Thanks, m01d
Yep. You DEFINITELY have to normalize.
This is from my code that I use to extract the orientation of the device.
Gravity is obtained as the x,y,z of the accelerometer
and compass is obtained from the x,y,z of the heading function
gravity.normalize();
compass.normalize();
compassEast=gravity.cross(compass);
compassEast.normalize();
compassNorth=compassEast.cross(gravity);
compassNorth.normalize();
Let me know if you need the full code.
Also, for those who havnt yet seen the iphone 4s gyroscope in action: its amazing! I swapped the above input to gravity and compass for the equivalents from the gyro and the result is stable and smooth and awesome :) Go Apple.
I didn't receive the source code but I set up my own example. You can see the project and code here: http://www.sundh.com/blog/2011/09/stabalize-compass-of-iphone-with-gyroscope/
yes, i did it like described above. but the result is not very accurate. i think with smoother accelerometer values it should work that way. because of this i have choosen to do the tilt compensation by adding/subtracting the accelermoter values of the corresponding axis to/from the compass values.
Here iss my code for the solution above, but its not a final working solution:
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading{
if (newHeading != nil) {
float Ax = accelerationValueX;
float Ay = accelerationValueY;
float Az = accelerationValueZ;
float filterFactor = 0.2;
Mx = [newHeading x] * filterFactor + (Mx * (1.0 - filterFactor));
My = [newHeading y] * filterFactor + (My * (1.0 - filterFactor));
Mz = [newHeading z] * filterFactor + (Mz * (1.0 - filterFactor));
float counter = ( -pow(Ax, 2)*Mz + Ax*Az*Mx - pow(Ay, 2)*Mz + Ay*Az*My );
float denominator = ( sqrt( pow((My*Az-Mz*Ay), 2) + pow((Mz*Ax-Mx*Az), 2) + pow((Mx*Ay-My*Ax), 2) ) * sqrt(pow(Ay, 2)+pow(-Ax, 2)) );
headingCorrected = (acos(counter/denominator)* (180.0 / M_PI)) * filterFactor + (headingCorrected * (1.0 - filterFactor));
}
...
}
Related
I've looked around and seen mention of the haversine formula to determine distance between two coordinates (lat1, lng1) and (lat2, lng2).
I've implemented this code:
function haversineGreatCircleDistance(
$latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371000)
{
// convert from degrees to radians
$latFrom = deg2rad($latitudeFrom);
$lonFrom = deg2rad($longitudeFrom);
$latTo = deg2rad($latitudeTo);
$lonTo = deg2rad($longitudeTo);
$latDelta = $latTo - $latFrom;
$lonDelta = $lonTo - $lonFrom;
$angle = 2 * asin(sqrt(pow(sin($latDelta / 2), 2) +
cos($latFrom) * cos($latTo) * pow(sin($lonDelta / 2), 2)));
return $angle * $earthRadius;
}
And am trying to determine:
1) what units this is returning? (goal being in feet)
2) is this equation written the right way?
For example what should be the distance between these two points?
(32.8940695525,-96.7926336453) and (33.0642604502, -96.8064332754)?
I'm getting 18968.0903312 from the formula above.
Thanks!
1) what units this is returning? (goal being in feet)
Whatever units in which you supply the Earth's radius.
2) is this equation written the right way?
Test it. You can compare your results with an existing Haversine formula implementation, like this one.
I want to throw a ball that has a projectile motion. I have a monkey on centre of screen and onTouchBegin I am taking the starting point of the touch and onTouchEnded I am taking the ending points. From the starting and ending points I am taking the angle value between them. Like 30 degrees, 45 or 90 degree.
This is my code by which I have calculated angle of start to endpoint
float angleRadians = atan2(startTouchPoint.x - touchPoint.x, startTouchPoint.y - touchPoint.y);
float angleDegrees = CC_RADIANS_TO_DEGREES(angleRadians);
float cocosAngle = -1 * angleDegrees;
Now i am using Projectile motion formula to throw ball with angle i have calculated from above formula .
inside init method
gravity = 9.8; // metres per second square
X = 0;
Y = 0;
V0 = 50; // meters per second -- elevation
VX0 = V0 * cos(angle); // meters per second
VY0 = V0 * sin(angle); // meters per second
gameTime = 0;
and onTouchEnded i have called fire method which will throw ball .
-(void)fire:(ccTime) dt
{
CCLOG(#"Angle 1: %.2f",angle);
gameTime += dt*6;
// x = v0 * t * cos(angle)
X = (V0 * gameTime * cos(angle))/2+120;
// y = v0 * t * sin(angle) - 0.5 * g * t^2
Y = (V0 * gameTime * sin(angle) - 0.5 * gravity * pow(gameTime, 2))/2+255;
if (Y > 50)
{
sprite_webfire.position = ccp(X,Y);
flag = true;
}
else
{
//angleValue += 15;
angleValue = angle;
angle = [self DegreesToRadians:angleValue];
gravity = 9.8; // metres per second square
X = 0;
Y = 0;
V0 = 50; // meters per second -- elevation
VX0 = V0 * cos(angle); // meters per second
VY0 = V0 * sin(angle); // meters per second
gameTime = 0;
// [self pauseSchedulerAndActions];
}
if (Y < 50)
{
[self unschedule:#selector(fire:)];
}
NSLog(#"ball (%lf,%lf), dt = %lf angle value %d", X, Y, dt,angleValue);
}
this code is working . by this code i can throw ball in projectile motion but i cant throw it where i want to. i cant throw wrt to given angle from start to end point.
i can throw it like red mark but i want to throw it blue mark with swipe . but its not throwing like i am swiping screen.
I am not certain on what math you are using to do this, I find your documentation a bit confusing.
Generally, for project tile motion this is what you need to do:
Find out what the take off angle is relative to the horizontal. Then depending on whatever initial velocity you want the object to have, use that and you trig equations to put your initial velocities into rectangular components.
For example:
If initial velocity was 10, the initial velocity in the y direction would be 10sin(angle), and in the x direction it would be 10cos(angle).
Then in to update the position of the sprite you should use kinematics equations: http://www.physicsclassroom.com/class/1dkin/u1l6c.cfm
First update velocities:
Velocity in the Y direction: V = v(initial) + gravity*(Delta-time)
Velocity in the X direction is constant unless you want to factor in some sort of resistance to make things a lot more complicated.
then position y = oldPositionY + velocity(in Y direction)*(Delta-time) + 1/2(gravity)(delta-time)^2.
and position x = oldPositionX + Xvelocity*delta-time
I have done some projectile motion stuff, and I have found you need to make gravity a large constant, something around 500 to make it look life-like. Let me know if this is confusing or you don't know how to implement it.
I would suggest that you take a look at the following tutorial: http://www.raywenderlich.com/4756/how-to-make-a-catapult-shooting-game-with-cocos2d-and-box2d-part-1.
It shows you how to use a physics engine, so you don't need to do much of the math. All the 'bullets' in the tutorial are also moving with projectile motion.
I'll add a bit to what was already said (which was good). Firstly, you should not be wasting time computing any angles. Stick with vectors for your velocity. In other words, get the initial velocity vector from the touch start and end location, and that will be your (v0x, v0y). For example:
CGPoint initialVelocity = ccpSub(touchPoint, startTouchPoint);
float v0x = initialVelocity.x;
float v0y = initialVelocity.y;
If you wish to assign a different magnitude to the initial velocity vector, simply normalize it and then multiply it by a new magnitude.
CGPoint unitVelocity = ccpNormalize(initialVelocity);
float magnitude = 200; // or whatever you want it to be
CGPoint velocity = ccpMult(unitVelocity, magnitude);
Anyway, with this velocity set properly you can then use it in your position calculations as before, but without the added complexity of calculating the angles.
-(void) fire:(ccTime)dt
{
.
.
gameTime += dt;
// if x(t) = x0 + v0x*t, then dx = v0x*dt
x += v0x*dt;
// if y(t) = y0 + v0y*t - 0.5t^2, then dy = v0y*dt - g*t*dt
y += (v0y * dt - g*gameTime*dt);
.
.
}
Also you should not set v0 = 50. Calculate the velocity from the vector as I suggested.
Something important to consider is that you are calculating what the movement should be in a physical world based upon units of meters. The screen is operating in points, not meters, so you will probably have to apply a scaling factor to the new position (x,y) to get the look that you are going for.
Edit: my bad, I had to revisit my math in the position calculation. My differentials was a bit rusty.
I am currently using the following code to count the number of steps a user takes in my indoor navigation application. As I am holding the phone around my chest level with the screen facing upwards, it counts the number of steps I take pretty well. But common actions like a tap on the screen or panning through the map register step counts as well. This is very frustrating as the tracking of my movement within the floor plan will become highly inaccurate. Does anyone have any idea how I can improve the accuracy of tracking in this case? Any comments will be much appreciated! To have a better idea of what I'm trying to do, you guys can check out a similar Android application at http://www.youtube.com/watch?v=wMgIa44mJXY. Thanks!
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
float xx = acceleration.x;
float yy = acceleration.y;
float zz = acceleration.z;
float dot = (px * xx) + (py * yy) + (pz * zz);
float a = ABS(sqrt(px * px + py * py + pz * pz));
float b = ABS(sqrt(xx * xx + yy * yy + zz * zz));
dot /= (a * b);
if (dot <= 0.9989) {
if (!isSleeping) {
isSleeping = YES;
[self performSelector:#selector(wakeUp) withObject:nil afterDelay:0.3];
numSteps += 1;
}
}
px = xx; py = yy; pz = zz;
}
The data from the accelerometer is basically a unidimensional (time) non uniform sampling of a three dimensional vector signal. The best way to figure out how to count steps will be to write an app that records and store the samples over a certain period of time, then export the data to a mathematical application like Wolfram's Mathematica for analysis and visualization. Remember that the sampling is non uniform, you may or may not want to transform it into a uniformly sampled digital signal.
Then you can try different signal processing algorithms to see what works best.
It's possible that, once you know the basic shape of a step in accelerometer data, you can recognize them by simple convolution.
I have set up an event system in FMOD with 3D sound triggered when the listener is close to them. I want to be able to change the listener angle of the listener so that my entire sound landscape shifts the same amount of degrees as my listener rotates. Nice huh?
Is it possible? What would be the iPhone code for that?
Would I pass something to the method set3DListenerAttributes?
I'm not sure I fully understand, do you just want to rotate the listener in 3D space? If so you adjust the listener forward vector to point in the new direction.
** Based on your response **
FMOD works with a cartesian co-ordinate system, it has a unit length vector which points in the direction an object (or listener) is facing. I would recommend you do some reading on trigonometry to fully understand how to convert an angle (in degrees or radians) to a vector.
For your forward vector the equation is (if I remember correctly):
x = cos(angle)
z = sin(angle)
y = 0;
This assumes angle is in radians, to convert from degrees to radians:
radians = degrees * (180 / Pi)
where Pi is roughly 3.14159265
This was the code I used in the end:
float degree = 90;
float radians = (degree) * (M_PI/180);
float fx = cos(radians);
float fz = sin(radians);
forward.x = fx;
forward.z = fz;
listenerpos.x = lxPos * DISTANCEFACTOR;
listenerpos.z = lyPos * DISTANCEFACTOR;
result = eventSystem->set3DListenerAttributes(0, &listenerpos,&vel,&forward,NULL);
I'm trying to convert the geomagnetic and accelerometer to rotate the camera in opengl ES1, I found some code from android and changed this code for iPhone, actually it is working more or less, but there are some mistakes, I´m not able to find this mistake, I put the code, also the call to Opengl Es1: glLoadMatrixf((GLfloat*)matrix);
- (void) GetAccelerometerMatrix:(GLfloat *) matrix headingX: (float)hx headingY:(float)hy headingZ:(float)hz;
{
_geomagnetic[0] = hx * (FILTERINGFACTOR-0.05) + _geomagnetic[0] * (1.0 - FILTERINGFACTOR-0.5)+ _geomagnetic[3] * (0.55);
_geomagnetic[1] = hy * (FILTERINGFACTOR-0.05) + _geomagnetic[1] * (1.0 - FILTERINGFACTOR-0.5)+ _geomagnetic[4] * (0.55);
_geomagnetic[2] = hz * (FILTERINGFACTOR-0.05) + _geomagnetic[2] * (1.0 - FILTERINGFACTOR-0.5)+ _geomagnetic[5] * (0.55);
_geomagnetic[3]=_geomagnetic[0] ;
_geomagnetic[4]=_geomagnetic[1];
_geomagnetic[5]=_geomagnetic[2];
//Clear matrix to be used to rotate from the current referential to one based on the gravity vector
bzero(matrix, sizeof(matrix));
//MAGNETIC
float Ex = -_geomagnetic[1];
float Ey =_geomagnetic[0];
float Ez =_geomagnetic[2];
//ACCELEROMETER
float Ax= -_accelerometer[0];
float Ay= _accelerometer[1] ;
float Az= _accelerometer[2] ;
float Hx = Ey*Az - Ez*Ay;
float Hy= Ez*Ax - Ex*Az;
float Hz = Ex*Ay - Ey*Ax;
float normH = (float)sqrt(Hx*Hx + Hy*Hy + Hz*Hz);
float invH = 1.0f / normH;
Hx *= invH;
Hy *= invH;
Hz *= invH;
float invA = 1.0f / (float)sqrt(Ax*Ax + Ay*Ay + Az*Az);
Ax *= invA;
Ay *= invA;
Az *= invA;
float Mx = Ay*Hz - Az*Hy;
float My = Az*Hx - Ax*Hz;
float Mz = Ax*Hy - Ay*Hx;
// if (mOut.f != null) {
matrix[0] = Hx; matrix[1] = Hy; matrix[2] = Hz; matrix[3] = 0;
matrix[4] = Mx; matrix[5] = My; matrix[6] = Mz; matrix[7] = 0;
matrix[8] = Ax; matrix[9] = Ay; matrix[10] = Az; matrix[11] = 0;
matrix[12] = 0; matrix[13] = 0; matrix[14] = 0; matrix[15] = 1;
}
Thank you very much for the help.
Edit: The iPhone it is permantly in landscape orientation and I know that something is wrong because the object painted in Opengl Es appears two times.
Have you looked at Apple's GLGravity sample code? It does something very similar to what you want here, by manipulating the model view matrix in response to changes in the accelerometer input.
I'm unable to find any problems with the code posted, and would suggest the problem is elsewhere. If it helps, my analysis of the code posted is that:
The first six lines, dealing with _geomagnetic 0–5, effect a very simple low frequency filter, which assumes you call the method at regular intervals. So you end up with a version of the magnetometer vector, hopefully with high frequency jitter removed.
The bzero zeroes the result, ready for accumulation.
The lines down to the declaration and assignment to Hz take the magnetometer and accelerometer vectors and perform the cross product. So H(x, y, z) is now a vector at right angles to both the accelerometer (which is presumed to be 'down') and the magnetometer (which will be forward + some up). Call that the side vector.
The invH and invA stuff, down to the multiplication of Az by invA ensure that the side and accelerometer/down vectors are of unit length.
M(x, y, z) is then created, as the cross product of the side and down vectors (ie, a vector at right angles to both of those). So it gives the front vector.
Finally, the three vectors are used to populate the matrix, taking advantage of the fact that the inverse of an orthonormal 3x3 matrix is its transpose (though that's sort of hidden by the way things are laid out — pay attention to the array indices). You actually set everything in the matrix directly, so the bzero wasn't necessary in pure outcome terms.
glLoadMatrixf is then the correct thing to use because that's how you multiply by an arbitrary column-major matrix in OpenGL ES 1.x.