I am dying here. So I have a complex number(-4.9991 + 15.2631i). In matlab if I do
angle(-4.9991 + 15.2631i) = 1.8873
I thought that angle basically calculated like
atan(15.2631/-4.9991) = -1.2543
Why are these different? I need to write a c function that calculates the angle of a complex number. I have done so like this:
#define angle(x) (atan((GSL_IMAG(x)/GSL_REAL(x))))
But that way gives me the -1.2543 answer, not the 1.8873 answer. What am I doing wrong?
-1.2543 + Pi(radians) = 1.8873 (with rounding)
As pointed out by others, use atan2()
Although using atan2 solves the problem, the actual question hasn't been answered:
Why are these different?
You are missing that the tangent function is periodic, with period pi = 3.141592... So, when you write z = atan(y/x) you expect a number z such that tan(z) = y/x, but there are infinite such numbers, since tan(z + pi) = tan(z). Of course, you get just one of these infinite values: The closest to zero, which isn't the one you always need.
In particular, note that since you are calculating the quotient Im/Re, you can't tell the difference from -Im/-Re, i.e. a minus sign on both componentes doesn't change the quotient, but it's the opposite complex number (same applies for 2-d vectors). That's what atan2 and angle do: They check for the sign of each component separately, and then determine if +/- pi should be added to the result of atan.
Related
I want to develop a simple geo-fencing algorithm in C, that works without using sin, cos and tan. I am working with a small microcontroller, hence the restriction. I have no space left for <math.h>. The radius will be around 20..100m. I am not expecting super accurate results this way.
My current solution takes two coordinate sets (decimal, .00001 accuracy, but passed as a value x10^5, in order to eliminate the decimal places) and a radius (in m). When multiplying the coordinates with 0.9, they can approximately be used for a Pythagorean equation which checks, if one coordinate lies within the radius of another:
static int32_t
geo_convert_coordinates(int32_t coordinate)
{
return (cordinate * 10) / 9;
}
bool
geo_check(int32_t lat_fixed,
int32_t lon_fixed,
int32_t lat_var,
int32_t lon_var,
uint16_t radius)
{
lat_fixed = geo_convert_distance(lat_fixed);
lon_fixed = geo_convert_distance(lon_fixed);
lat_var = geo_convert_distance(lat_var);
lon_var = geo_convert_distance(lon_var);
if (((lat_var - lat_fixed) * (lat_var - lat_fixed) + (lon_var - lon_fixed) * (lon_var - lon_fixed))
<= (radius * radius))
{
return true;
}
return false;
}
This solution works quite well for the equator, but when changing the latitude, this becomes increasingly inaccurate, at 70°N the deviation is around 50%. I could change the factor depending on the latitude, but I am not happy with this solution.
Is there a better way to do this calculation? Any help is very much appreciated. Best regards!
UPDATE
I used the input I got and managed to implement a decent solution. I used only signed ints, no floats.
The haversine formula could be simplified: due to the relevant radii (50-500m), the deltas of the latitude and longitude are very small (<0.02°). This means, that the sine can be simplified to sin(x) = x and also the arcsine to asin(x) = x. This approach is very accurate for angles <10° and even better for the small angles used here. This leaves the cosine, which I implemented according to #meaning-matters 's suggestion. The cosine will take an angle and return the actual result multiplied by 100, in order to be able to use ints. The square root was implemented with an iterative loop (I cannot find the so post anymore). The haversine calculation was done with the inputs multiplied by powers of 10 in order to achieve accuracy and afterwards divided by the necessary power of 10.
For my 8bit system, this caused a memory usage of around 2000-2500 Bytes.
Implement the Havesine function using your own trigonometric functions that use lookup tables and do interpolation.
Because you don't want very accurate results, small lookup tables, of perhaps twenty points, would be sufficient. And, simple linear interpolation would also be fine.
In case you don't have much memory space: Bear in mind that to implement sine and cosine, you only need one lookup table for 90 degrees of either function. All values can then be determined by mirroring and offsetting.
I am trying to self teach myself C (C99 I think? gcc 8.1.0) coming from python/java. One of the practice problems I am working on is how to calculate pi to a given decimal.
I am currently using the following equation 2 * (Arcsin(sqrt(1 - 0.5^2)) + abs(Arcsin(0.5))).
float pi_find(float nth)
{
float x, y, z;
/* Equation = 2 * (Arcsin(sqrt(1 - x^2)) + abs(Arcsin(x))) [x|-1<=x=>1, xeR]*/
x = sqrt(1-pow(nth, 2)); /* Carrot (^) notation does not work, use pow() */
y = fabs(asin(nth)); /* abs is apparently int only, use fabs for floats */
z = x+y;
printf("x: %f\ny: %f\nsum: %f\n", x, y, (x+y));
printf("%f\n", asin(z));
return 2 * asin(z); /* <- Error Happens */
}
int main()
{
float nth = 0.5f;
double pi = pi_find(nth);
printf("Pi: %f\n", pi);
return 0;
}
Results:
x: 0.866025
y:0.523599
sum: 1.389624
z:-1.#IND00
Pi:-1.#IND00
I know the issue lies in the addition of x + y which sums out to 1.389... and asin() can only handle values between -1 and +1 inclusive.
HOWEVER!
I am using Wolfram Alpha along side python to check the calc is correct at every step and it can calculate asin(1.389...). [1]
I don't understand Imaginary mathematics, it is far beyond my capabilities as a mathematician but below is what Wolfram is doing. [2]
1.570796 -0.8563436 i
Interpreting as: 0.8563436 i
Assuming multiplication | Use a list instead
Assuming i is the imaginary unit | Use i as a variable instead
While writing this I found out about the _Imaginary Datatype added in C99, but I don't really understand if it's doing the same thing as what Wolfram does.
Also looked up how imaginary numbers worked, but I don't really understand how 'The square roots of a negative number cannot be distinguished until one of the two is defined as the imaginary unit' works. [3]
Can someone nudge me in the direction to fix this please?
It is obviously a knowledge issue and not a mathematical or language limitation
p.s yes I know it's trash code, I am using a weird way of debugging before I rewrite it properly.
[1]:Wolfram_Alpha Calculation
[2]:Wolfram_Alpha Assumption
[3]:Imaginary Numbers
The problem is you're grouping the expression incorrectly. The desired expression is:
2 * (Arcsin(sqrt(1 - 0.5^2)) + abs(Arcsin(0.5)))
With nth substituted for 0.5, this becomes:
2 * (Arcsin(sqrt(1 - nth^2)) + abs(Arcsin(nth))).
In particular, the argument to the first Arcsin is sqrt(1 - nth^2)), and the argument to the second Arcsin is nth.
You're also better off using nth * nth rather than pow(nth, 2). It's both faster and more accurate.
So what you want is:
x = asin(sqrt(1 - nth*nth));
y = fabs(asin(nth));
r = 2*(x + y);
Notice that the argument to asin can never have magnitude greater than 1 (as long as nth is less than 1).
Also, as I mentioned earlier in a comment, you should change all your float variables to double. You're using the double-precision math library functions anyway, so there's no reason to discard half of the precision by storing the results in float variables.
In C, the float and double types model "real" numbers, which I'll assume you have a handle on.
In mathematics, "complex" numbers are an extension of the real numbers. Every real number counts as a complex number, but so do "imaginary numbers", which you can get by multiplying the real numbers by the "imaginary unit" (labeled i in mathematical notation, and conventionally described as "the square root of -1").
Mathematically speaking, the basic arithmetic operations (+, -, *, /) are defined on complex numbers. It turns out that you can extend functions like arcsine to operate on complex numbers as well.
Without getting any further into the details, the Wolfram Alpha is almost certainly giving you values from a complex version of arcsine.
However, the standard C function asin() is the un-extended version: it takes a double as an argument, and returns a double as a result. Since double only models real numbers, asin() makes no sense for input values outside [-1,1].
I'm trying to do the Steinhart-Hart temperature calculation on an Arduino. The equation is
I solved a system of 3 equations to obtain the values of A, B and C, which are:
A = 0.0164872
B = -0.00158538
C = 3.3813e-6
When I plug these into WolframAlpha to solve for T I get a value in Kelvins that makes sense:
T=1/(0.0164872-0.00158538*log2(10000)+3.3813E-6*(log2(10000))^3) solve for T
T = 298.145 Kelvins = 77 Fahrenheit
However when I try to use this equation on my Arduino, I get a very wrong answer, I suspect because doubles do not have enough precision. Here's what I'm using:
double temp = (1 / (A + B*log(R_therm) + C*pow(log(R_therm),3)));
This returns 222 Kelvin instead, which is way off.
So, how can I do a calculation like this in Arduino?? Any advice is greatly appreciated, thanks.
Precision is not the main issue. Could even use float and powf(). A thermistor temperature calculation is not that accurate. After all the temperature is certainly not better than ±0.1°C accurate. Self heating of the thermistor is a larger factor.
OP's C code assumes log base 2, use log base e log() as the constants were derived using log base 2. #Martin R
// double temp = (1 / (A + B*log(R_therm) + C*pow(log(R_therm),3)));
double temp = (1 / (A + B*log(R_therm)/log(2) + C*pow(log(R_therm)/log(2),3)));`
Sample implementation, that avoids an unnecessary slow pow() call.
static const inv_ln2 = 1.4426950408889634073599246810019;
double ln2_R = log(R_therm)*inv_ln2;
double temp = 1.0 / (A + ln2_R*(B + C*ln2_R*ln2_R));
Yes, floating point arithmetic has limited precision on most arduinos.
Have you considered using fixed precision? If used correctly, this might give you better results. The requirement for this is to have rather narrow parameters, however, and be careful about unit conversions.
An unsigned long on arduino is 4 bytes too, so it can contain numbers up to 2^32-1. If using fixed point, you might want to replace this 1/T by something like 100000/T, where the numerator constant and T have been scaled according to the desired precision.
You will also need to keep a (mental or paper) model of the number of decimals each variable contains, in order to optimize the operation order not to lose precision.
For the log2 function, I doubt it is available out of the box for integers. You could either cast the result or reimplement it. There is plenty of ressources for this problem, even here on SO.
A naive implementation of vector rotation in 3d gives huge rounding errors, especially when multiple rotations around different axis are performed. A simple 1-axis example shows the basic problem. I have a code where I rotate points around x- and y- axis a few times. In some cases, I get errors in the second decimal place (e.g. length of the vector is 1 before rotations and 0.9 after). I'd be happy with relative errors < 1e-5.
void Rotate_x(double data[3], double agl) {
agl *= M_PI/180.0;
double c = cos(agl); double s = sin(agl);
double tmp_y = c*data[1] - s*data[2];
double tmp_z = s*data[1] + c*data[2];
data[1] = tmp_y; data[2] = tmp_z;
}
Can someone point me to a library or some code that rotates points around the coordinate axis with minimal error?
Everything I found were bloated linear algebra libraries that are overkill for my purposes.
Edit:
I went to long double precision and combined rotations to improve errors. With doubles I was not fully satisfied (1e-3 relative error in worst case). That was the easiest solution an it works okay. Still wouldn't mind a nice library that does rotations in regular double precision accurately.
better precision variables are not enough
you need more precise sin,cos functions to improve accuracy
so make your own functions via Taylor series expansion
and use that ... then compare the results
and increase the polynomial order until accuracy stop raising or start dropping again
if you are applying many transformations on the same data
then create cumulative transform matrix
then check if it is orthogonal/orthonormal
and repair if not (with use of cross product)
I use this for 3D render object matrices (many cumulative transforms over time)
but in your case this can also increase error (if chosen wrong order of axises during correction)
this is better suited to ensure that object will stay the same size/shape over time ...
[edit1] test
I took your code to Borland BDS2006 compile as win32 app
and the result is:
original: (0.0000000000000000000,1.0000000000000000000,0.0000000000000000000)
rotated: (0.0000000000000000000,0.9999999999999998890,-0.0000000000000000273)
also do not forget if your sin,cos taking radians (as usuall for C/C++) then add this to Rotate
agl*=M_PI/180.0;
What compiler/platform are you using?
This is how mine Rotate looks like
void Rotate(double *data,double agl)
{
agl*=M_PI/180.0;
double c = cos(agl); double s = sin(agl);
double tmp_y = c*data[1] - s*data[2];
double tmp_z = s*data[1] + c*data[2];
data[1] = tmp_y; data[2] = tmp_z;
}
[edit2] 32/64 bit comparison
[double] //64bit floating point
(0.0000000000000000000,1.0000000000000000000,0.0000000000000000000)
(0.0000000000000000000,0.9999999999999998890,-0.0000000000000000273)
[float] //32bit floating point
(0.0000000000000000000,1.0000000000000000000,0.0000000000000000000)
(0.0000000000000000000,0.9999999403953552246,-0.0000000146747787255)
I have a homework in C. We have to write our own asin() function with Taylor method, and we can't use math.h
It works fine, but once I put higher count of iterations(int i), it returns NaN(Not a Number), and when I use low count of i, the number is not exact. Can anyone help me with this?
double my_asin(double x)
{
int i = 0;
double vypocet = x;
double y = vypocet;
for(i=1;i<=10000;i++)
{
vypocet*=((x*x)*(2*i-1)*(2*i-1))/((2*i)*(2*i+1));
y+=vypocet;
}
printf("my_asin = %.10e\n", y);
return y;
}
EDIT: Thank you all! finished it :)
Two things are required for your answer :
Regarding maths : The series expansion you are coding is a sin inverse (arcsin) and expecting an output in radian.
sin^(-1)x=x+1/6x^3+3/(40)x^5+5/(112)x^7+(35)/(1152)x^9+... . As you can see this is an expansion which is monotonically increasing and expecting value (input) between [-1,1] only. When you plug in large values e.g. 10 you are bound to get results you don't expect.So , plug in correct values. I guess, put correct values [-1,1] when calling the function my_asin() and your code would work fine FOR THE number of ITERATIONS YOU HAVE NOW.
e.g 1.5146343691e+000 looks fine for 90 degrees or pi/2 or my_asin(1).
2 .Regarding Floating Point (double i.e. single prrecision floating point ):They cant represent all the numbers on the real line, their range is a subset of R.And when there is a number that can't be represented correctly by their 32 bits encoding (IEEE 754) you will get error in result.
Number as simple as 0.1 cant be represented exactly using floating point.
Check these pages for FP Errors and FP Exceptions :
http://www.gnu.org/software/libc/manual/html_node/Infinity-and-NaN.html
http://www.gnu.org/software/libc/manual/html_node/FP-Exceptions.html#FP-Exceptions