acos and cos with degrees not giving proper result - ios

While I write code like below
NSLog(#"%f",acos(cos(1)));
it returns 1 as result but if I tried to obtain result from degree instead of radian then facing problem as below
#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)
NSLog(#"%f",acos(cos(DEGREES_TO_RADIANS(1))));
then it returns 0.017453 instead of 1.If I write like below
NSLog(#"%f",acos(cos(DEGREES_TO_RADIANS(1)))*180/M_PI);
then I'm getting proper 1 as result. But if I'll write like below
NSString *a1 = [NSString stringWithFormat:#"%f",(cos(DEGREES_TO_RADIANS(1)))];
NSString *a2 = [NSString stringWithFormat:#"%f",(acos([a1 doubleValue])*180/M_PI)];
NSLog(#"%f",[a2 doubleValue]);
then I'm getting 0.998999 as result.
Then where the problem is?
Please help me

0.017453 is about how many radians are in one degree.
If you want your logging line to output 1, you'll need to convert back to degrees.
#define RADIANS_TO_DEGREES(angle) ((angle) * 180.0 / M_PI)
NSLog(#"%f", RADIANS_TO_DEGREES(acos(cos(DEGREES_TO_RADIANS(1)))));
Note that you may get a result like 0.9999, though, due to the limited accuracy of trigonometric functions, and of floats in general.

Arc Cosine is the inverse of cosine.
Cosine takes an angle as input, and returns a value ranging from -1 to 1.
Arc Cosine (acos) takes a value from -1 to 1 as input, and returns an angle.
The input to acos() is not an angle, so you should not try to convert it between degrees and radians. The result is an angle. That's what you convert.
So you would write
x = RADIANS_TO_DEGREES(acos(1)).
That would give you the angle at which the cosine is 1, expressed in degrees. (The value of acos(1) is 0, which is the same in degrees or radians.)
Better to use acos(0) as a test value. The cosine function = 0 at pi/2 (or 90 degrees.)
So
acos(0) == pi/2. (~1.5707)
RADIANS_TO_DEGREES(acos(0)) == 90.
EDIT:
I see now that in your question you were taking acos(cos(DEGREES_TO_RADIANS(1)), which is correct.
However, you still need to convert the result of acos() to degrees if you want your answer in degrees, as described above. (And Kevin's answer is also correct. My mistake)
Note that cos(1) is a little strange, in both degrees and radians. That isn't going to give an even answer in either.
cos(0) = 1, cos(pi) = -1, cos(2pi) = 1.
(or in degrees)
cos(180) = -1, and cos(360) = 1)
cos(1) in degrees is going to be an strange decimal value, and so will cos(1) in radians.

Related

Pi Output In Swift

I am trying to build a degrees/radians calculator. Here are the functions I have.
func degreesToRadians(degrees: Double) -> Double {
return degrees * (M_PI / 180)
}
func radiansToDegrees(radians: Double) -> Double {
return radians * (180 / M_PI)
}
degreesToRadians(90)
radiansToDegrees(M_PI/4)
degreesToRadians(90) returns 1.5707963267949
radiansToDegrees(M_PI/4) returns 45.0
What I want to happen is in the Degrees to Radians function instead of 1.5707963267949 I want the output to be π/2. Is this even possible?
Thanks.
To represent 90 degrees as π/2, what you want to do is
consider the fraction of degrees over 180 (i.e. numerator of degrees and denominator of 180);
reduce this fraction (i.e. divide both the numerator and denominator by the greatest common factor); in the case of 90/180, the greatest common factor is, in fact, 90, yielding a reduced numerator of 1 and a reduced denominator of 2;
the result as a string representation of the fraction, i.e. something like
"\(reducedNumerator)π/\(reducedDenominator)"
Obviously, if either the numerator or denominator are 1, then you can suppress that portion of the string, but hopefully you get the basic idea.
Take a crack at that.
If you want exactly: "π/2", I don't think this is possible with double. You might get this in String, but not in a number. I think your best option would be to take an optional bool in which case result is returned as multiple of pi.
degreesToRadians(90, resultAsMultipleOfPi:true)
If this bool is true then 0.5 should be returned. You may need to do some rounding off to get well rounded numbers.
If you look closely on what is really M_PI you will see that it is predefined double value in math.h that equals
#define M_PI 3.14159265358979323846264338327950288 /* pi */
If you want more precision you can declare and use long double value instead.
You can directly use M_PI constant.
If you need in float format just use this,
let pi = Float(M_PI)

Objective-C; asin issue

#import "ViewControllerSettings.h"
#define DEGREES(radians) (radians * 180 / M_PI)
NSLog(#"%f", sinFita);
sinFita = asin(DEGREES(sinFita));
NSLog(#"%f", sinFita);
returns
2014-04-20 22:10:09.916 ---[8561:60b] 0.239580
2014-04-20 22:10:09.920 ---[8561:60b] nan
I require my answer to be in Degreesº, the and doubles are used.
The answer should be 13.86º
asin argument should be in radians and not degrees and the result returned by asin is also in radians so you will need to execute it as follows :
sinFita = DEGREES(asin(sinFita)); // Be aware that sinFita will now be in degrees and not in radians once this line is executed
Result is correct. Go through you calculation step by step. What do you think asin should return when the argument is greater than 1?

iOS core motion detect forard / backward tilt

I am using the iOS core motion framework to detect if the device is tilted forward or backwards. See image for details: http://i.stack.imgur.com/2Ojw5.jpg
Using the pitch value a can detect this movement but I can not distinguish between forward AND backward.
More details:
I try to detect if there is a movement (tilting forward and backward) in either the forward area OR backward area (see updated sketch).
The problem with the pitch is that it starts with a value of about 1.6 if the device is in an upright position. And the value decreases the same when I am tilting it towards a horizontal potion either forward or backward. The same behavior applies to the accelerometer y value.
It looks like I miss something in the whole core motion thing. ANy Ideas
thanks christian
Using attitude pitch, leaning forward and backward are indistinguishable. However with quaternions you can calculate pitch, and if you convert radians to degrees,
0 means the device is on its back
90 means it's standing up
180 means it's on its face
The opposite hemisphere of rotation is 0 to -180. Here's the code:
func radiansToDegrees(_ radians: Double) -> Double {
return radians * (180.0 / Double.pi)
}
let quat = motionData.attitude.quaternion
let qPitch = CGFloat(radiansToDegrees(atan2(2 * (quat.x * quat.w + quat.y * quat.z), 1 - 2 * quat.x * quat.x - 2 * quat.z * quat.z)))
Try this:
// Create a CMMotionManager
CMMotionManager *mManager = [(AppDelegate *)[[UIApplication sharedApplication] delegate] sharedManager];
// Check whether the accelerometer is available
if ([mManager isAccelerometerAvailable] == YES) {
[mManager setAccelerometerUpdateInterval: .02];
[mManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
[self updateGravityWithAccelerometerX:accelerometerData.acceleration.x y:accelerometerData.acceleration.y z:accelerometerData.acceleration.z];
}];
}
This will call updateGravityWithAccelerometerData every .02 seconds. You should be able to create that method and use NSLog to watch the values change to decipher what you are looking for. I believe you are looking for the acceleration.y value.
You are right, pitch works that way, considering the 4 typical quadrants (http://en.wikipedia.org/wiki/Quadrant_(plane_geometry)) and the counterclockwise direction:
Quadrant I, values range from 0 to PI/2 (or 0 to 90 in degrees).
Quadrant II, values range from PI/2 to 0 (or 90 to 0 in degrees).
Quadrant III, values range from 0 to -PI/2 (or 0 to -90 in degrees).
Quadrant IV, values range from -PI/2 to 0 (or -90 to 0 in degrees).
Considering this looks pretty obvious that you cannot difference between the phone leaning forwards or backwards.
I have recently faced the same problem for an iOS app that counts the number of flips that the phone does. Apple has rejected it so I have published it on GitHub, may be useful for you:
Flip Your Phone! -
https://github.com/apascual/flip-your-phone
You don't want to read the accelerometer for tilt. Accelerometer is for detecting differences in movements. You want the gyroscope so you can determine the absolute attitude (i.e. yaw, pitch and roll). In your case it sounds like you just want roll.
Use startDeviceMotionUpdatesToQueue and then attitude.roll for front and back and attitude.pitch for side to side. Here is what I did in Swift:
func motion(data: CMDeviceMotion){
let pitch = data.attitude.pitch
let roll = data.attitude.roll
let dampener:Float = -0.25 // the ball was rolling too fast
var forward_force = Float(1.6 - roll) * dampener //1.6 is vertical
var side_force = Float(pitch) * dampener // 0 is untilted when rotating cw/ccw
ballNode.physicsBody?.applyForce(SCNVector3Make(side_force, 0, forward_force), atPosition: SCNVector3Make(0, 0, 0), impulse: true)
}
With this you can see if it tilted frontward or backward based on whether the roll is greater than or equal to 1.6 which is approximately straight up.

generate random number between pi/2 and -pi/2

Can someone provide a way to produce a random float between -Pi/2 and Pi/2 please?
I've tried...
float angleR = M_PI / arc4random_uniform(1000) - M_PI * 0.5;
But that doesn't work, lol.
float angle = (rand()/(float)RAND_MAX)*PI - PI/2;
You can easily adapt it to use arc4rand function (mind that its maximum value should be 0x100000000).
Something like this should work. If you want your result to be (roughly) uniformly random, you certainly don't want to divide by a uniformly random number (as that will bias you severely toward angles near, in your case, -π/2).
float angleR = ((float)arc4random_uniform(1000) - 500) * M_PI;

How do I calculate the Azimuth (angle to north) between two WGS84 coordinates

I have got two WGS84 coordinates, latitude and longitude in degrees. These points are rather close together, e.g. only one metre apart.
Is there an easy way to calculate the azimuth of the line between these points, that is, the angle to north?
The naive approach would be to assume a Cartesian coordinate system (because these points are so close together) and just use
sin(a) = abs(L2-L1) / sqrt(sqr(L2-L1) + sqr(B2-B1))
a = azimuth
L1, L2 = longitude
B1, B2 = latitude
The error will be larger as the coordinates move away from the equator because there the distance between two longitudinal degrees becomes increasingly smaller than the one between two latitudinal degrees (which remains constant).
I found some quite complex formulas which I don't really want to implement because they seem to be overkill for points that are that close together and I don't need very high precision (two decimals are enough, one is probably fine either since there are other factors that reduce precision anyway, like the one the GPS returns).
Maybe I could just determine an approximate longitudinal correction factor depending on latitude and use somthing like this:
sin(a) = abs(L2*f-L1*f) / sqrt(sqr(L2*f-L1*f) + sqr(B2-B1))
where f is the correction factor
Any hints?
(I don't want to use any libraries for this, especially not ones that require runtime licenses. Any MPLed Delphi Source would be great.)
The formulas that you refer to in the text are to calculate the great circle distance between 2 points. Here's how I calculate the angle between points:
uses Math, ...;
...
const
cNO_ANGLE=-999;
...
function getAngleBetweenPoints(X1,Y1,X2,Y2:double):double;
var
dx,dy:double;
begin
dx := X2 - X1;
dy := Y2 - Y1;
if (dx > 0) then result := (Pi*0.5) - ArcTan(dy/dx) else
if (dx < 0) then result := (Pi*1.5) - ArcTan(dy/dx) else
if (dy > 0) then result := 0 else
if (dy < 0) then result := Pi else
result := cNO_ANGLE; // the 2 points are equal
result := RadToDeg(result);
end;
Remember to handle the situation where 2 points are equal (check if the result equals cNO_ANGLE, or modify the function to throw an exception);
This function assumes that you're on a flat surface. With the small distances that you've mentioned this is all fine, but if you're going to be calculating the heading between cities all over the world you might want to look into something that takes the shape of the earth in count;
It's best to provide this function with coordinates that are already mapped to a flat surface. You could feed WGS84 Latitude directly into Y (and lon into X) to get a rough approximation though.
Here is the C# solution. Tested for 0, 45, 90, 135, 180, 225, 270 and 315 angles.
Edit I replaced my previous ugly solution, by the C# translation of Wouter's solution:
public double GetAzimuth(LatLng destination)
{
var longitudinalDifference = destination.Lng - this.Lng;
var latitudinalDifference = destination.Lat - this.Lat;
var azimuth = (Math.PI * .5d) - Math.Atan(latitudinalDifference / longitudinalDifference);
if (longitudinalDifference > 0) return azimuth;
else if (longitudinalDifference < 0) return azimuth + Math.PI;
else if (latitudinalDifference < 0) return Math.PI;
return 0d;
}
public double GetDegreesAzimuth(LatLng destination)
{
return RadiansToDegreesConversionFactor * GetAzimuth(destination);
}
I found this link
http://williams.best.vwh.net/avform.htm
given in the answer to
Lat/Lon + Distance + Heading --> Lat/Lon
This looks promising, especially the flat earth approximation given near the end.
This would work only for small differences. Otherwise you can't just "latitudinalDifference / longitudinalDifference".
I would recommend implementing a correction factor based on the longitude. I implemented a simular routine once to return all geocoded records within x miles of a specific spot and ran into simular issues. Unfortunately I don't have the code anymore, and can't seem to recall how I got to the correction number but you are on the right track.
Has anyone tested this? It does not return the correct answers
This function assumes that you're on a flat surface. With the small distances that you've mentioned this is all fine, but if you're going to be calculating the heading between cities all over the world you might want to look into something that takes the shape of the earth in count;
Your flat earth has little to do with this. The error, as you call it, is because you are calculating an initial azimuth from a point. Unless you are heading straight to a pole, you relationship to the pole will change with distance. Regardless, the above program does not return correct results.

Resources