Imagine you have a computer, Computer B, that only has a tangent function and can only return accurate results on the tangent of an angle between 0 and 45 degrees. Given an angle, ø, greater than 45 degrees and less than 90, what mathematical operations could be performed to return an accurate tangent value? Also provide a detailed example.
I need some help on this. This tan problem is in terms of the coprocessor and FPTAN instruction. Any ideas?
This looks like homework, so as a hint: given a right angle triangle, if one angle is greater than 45 deg, then the other is less than 45 deg. Furthermore
cos(PHI) = sin(THETA)
sin(PHI) = cos(THETA)
If PHI and THETA are the angles in your triangle. I believe that should get you going.
Use the fact that 1/TAN(θ)=COT(θ)=TAN(π/2-θ).
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 programming an asteroids type game in C, and I have a sprite sheet of 36 sprites that is the ship rotating. I would like to know a math formula for figuring out how to move the ship in the direction of the sprite I have chosen from the sprite sheet. Note that I am incrementing by 10 degrees (hence 36 sprites for 360 degrees).
For example, my screen is 320 pixels wide by 256 pixels high.
If I select sprite image 10 (which is 90 degrees (the ship is facing right)), how can I calculate (using some sort of formula) the X and Y coordinates to move the ship in? I know 90 degrees is an easy one, by imagine if it were 30 degrees. There is a certain value for X and a certain value for Y. Since the screen in wider in width that height, the X speed would be higher than the Y speed.
Hope that makes sense.
Many thanks.
There are two easy approaches: you can build a table of [x,y] distances for each of the 36 angles, or you can do the math "on the fly".
The advantage of calculating the distances immediately is that you can easily increase the accuracy later on, if you decide you want more than 36 angles (and don't mind the sprite is off by a couple of degrees). Also, since you will be working with floats anyway, you can do all of your calculations with a far greater accuracy. Your speed could be as low as 0.01 pixel per second, and if you store your position as floats as well, you'd see your sprite move a tiny bit every few minutes.
Pre-calculating a table is easy and fast, though. Run this program to create the arrays xmove and ymove. Then, for an angle a, you can set xpos += ((speed*xmove[a])>>8) and ypos += ((speed*ymove[a])>>8).
The table stores sin and cos times 256, as integers. The values need to be multiplied by some large factor because they always fall inside the floating point range -1..1; storing them as their original floating point value is possible but unnecessary (it would only re-introduce floating point calculations in what can be reasonably approximated with pure integers, in your case). Now since the values are "premultiplied" by 256, you need to divide the speed*move calculation again by that number -- shifting right by 8 bits is all it takes. (There is a small rounding issue here; if it bothers you, add 128 before the right-shift.)
You can use a larger accuracy by using a multiplier of 1024 or higher, but again, more accuracy is probably entirely invisible for your purposes. ('1024' instead of '1000' because you can still efficiently use bit-shifting with that number.)
I believe that nowadays any modern screen has nigh-on square pixels, so unless you want it as some sort of special effect, speed in the y direction should be the same as x-speed. However, it's simple to add. Instead of dividing by 256, you'd use something like ypos += ((speed*ymove[angle])/341); -- this is (4*256/3), so the vertical speed is 75% of the horizontal speed.
A final possible refinement: you can also store your xpos,ypos as pre-multiplied by 256! Then you would not shift right the new coordinates, but immediately add the correct value. Only when displaying the actual sprite, you'd divide the coordinates by 256. That way your ship will not move by "entire pixels" only, but way more smooth. If your speed is variable, you can store it with higher accuracy the same way (remember to scale down correctly, because it'd make your 'virtual' speed is 256*256 higher than your 'screen' speed).
The table created below assumes #0 is "straight up", #9 (not 10!) is "right", #18 is down and #27 is "left", where positive y points downwards.
By the way: the size of your ship doesn't really matter ... You probably don't want it to "jump" distances equal to its own size.
#include <stdio.h>
#include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
int main (void)
{
int i, angle;
printf ("int xmove[36] = {\n");
for (i=0; i<36; i++)
{
angle = 10*i;
// x distance: sin
printf ("\t%d,", (int)(round(256*sin(angle * M_PI/180))));
printf ("\t\tangle: %d\n", angle);
}
printf ("};\n");
printf ("\n");
printf ("int ymove[36] = {\n");
for (i=0; i<36; i++)
{
angle = 10*i;
// y distance: cos
printf ("\t%d,", (int)(round(-256*cos(angle * M_PI/180))));
printf ("\t\tangle: %d\n", angle);
}
printf ("};\n");
return 0;
}
So I am trying to code Hough Transform on C. I have a binary image and have extracted the binary values from the image. Now to do hough transform I have to convert the [X,Y] values from the image into [rho,theta] to do a parametric transform of the form
rho=xcos(theta)+ysin(theta)
I don't quite understand how it's actually transformed, looking at other online codes. Any help explaining the algorithm and how the accumulator for [rho,theta] values should be done based on [X,Y] would be appreciated.Thanks in advance. :)
Your question hints at the fact that you think that you need to map each (X,Y) point of interest in the image to ONE (rho, theta) vector in the Hough space.
The fact of the matter is that each point in the image is mapped to a curve, i.e. SEVERAL vectors in the Hough space. The number of vectors for each input point depends on some "arbitrary" resolution that you decide upon. For example, for 1 degree resolution, you'd get 360 vectors in Hough space.
There are two possible conventions, for the (rho, theta) vectors: either you use [0, 359] degrees range for theta, and in that case rho is always positive, or you use [0,179] degrees for theta and allow rho to be either positive or negative. The latter is typically used in many implementation.
Once you understand this, the Accumulator is little more than a two dimension array, which covers the range of the (rho, theta) space, and where each cell is initialized with 0. It is used to count the number of vectors that are common to various curves for different points in the input.
The algorithm therefore compute all 360 vectors (assuming 1 degree resolution for theta) for each point of interest in the input image. For each of the these vectors, after rounding rho to the nearest integral value (depends on precision in the rho dimension, e.g. 0.5 if we have 2 points per unit) it finds the corresponding cell in the accumulator, and increment the value in this cell.
when this has been done for all points of interest, the algorithm searches for all cells in the accumulator which have a value above a chosen threshold. The (rho, theta) "address" of these cells are the polar coordinates values for the lines (in the input image) that the Hough algorithm has identified.
Now, note that this gives you line equations, one is typically left with figure out the segment of these lines that effectively belong in the input image.
A very rough pseudo-code "implementation" of the above
Accumulator_rho_size = Sqrt(2) * max(width_of_image, height_of_image)
* precision_factor // e.g. 2 if we want 0.5 precision
Accumulator_theta_size = 180 // going with rho positive or negative convention
Accumulator = newly allocated array of integers
with dimension [Accumulator_rho_size, Accumulator_theta_size]
Fill all cells of Accumulator with 0 value.
For each (x,y) point of interest in the input image
For theta = 0 to 179
rho = round(x * cos(theta) + y * sin(theta),
value_based_on_precision_factor)
Accumulator[rho, theta]++
Search in Accumulator the cells with the biggest counter value
(or with a value above a given threshold) // picking threshold can be tricky
The corresponding (rho, theta) "address" of these cells with a high values are
the polar coordinates of the lines discovered in the the original image, defined
by their angle relative to the x axis, and their distance to the origin.
Simple math can be used to compute various points on this line, in particular
the axis intercepts to produce a y = ax + b equation if so desired.
Overall this is a rather simple algorithm. The complexity lies mostly in being consistent with the units, for e.g. for the conversion between degrees and radians (most math libraries' trig functions are radian-based), and also regarding the coordinates system used for the input image.
I have doubles that represent latitudes and longitudes.
I can easily limit longitudes to (-180.0, 180.0] with the following function.
double limitLon(double lon)
{
return fmod(lon - 180.0, 360.0) + 180.0;
}
This works because one end is exclusive and the other is inclusive. fmod includes 0 but not -360.0.
Can anyone think of an elegant method for latitude?
The required interval is [-90.0, 90.0]. A closed form solution would be best, i.e. no loop. I think fmod() is probably a non-starter because both ends are inclusive now.
Edit: As was pointed out, one can't go to 91 degrees latitude anyway. Technically 91 should map to 89.0. Oh boy, that changes things.
There is a much, much more efficient way to do this than using sin and arcsin. The most expensive operation is a single division. The observation that the required interval is closed is key.
Divide by 360 and take the remainder. This yields a number in the interval [0, 360), which is half-open, as observed.
Fold the interval in half. If the remainder is >=180, subtract it from 360. This maps the interval [180, 360) to the interval (0, 180]. The union of this interval with the bottom half is the closed interval [0, 180].
Subtract 90 from the result. This interval is [-90, 90], as desired.
This is, indeed, the exact same function as arcsin(sin(x)), but without the expense or any issue with numeric stability.
Using trig functions sin()/cos() is expensive in time and introduces loss of precision. Much better to use the remainder() function. Note the result has the same sign as x and magnitude less than the magnitude of y, if able.
OP was on the right track! The below solution is easy to adjust per the edge values of -180 and + 180.0.
#include <math.h>
// Reduce to (-180.0, 180.0]
double Limit_Longitude(double longitude_degrees) {
// A good implementation of `fmod()` will introduce _no_ loss of precision.
// -360.0 <= longitude_reduced <=- 360.0
double longitude_reduced = fmod(longitude_degrees, 360.0);
if (longitude_reduced > 180.0) {
longitude_reduced -= 360.0;
} else if (longitude_reduced <= -180.0) {
longitude_reduced += 360.0;
}
return longitude_reduced;
}
Limiting Latitude to [-90 to +90] is trickier as a latitude of +91 degrees is going over the North Pole but switching the longitude +/- 180 degrees. To preserve longitude precision, adjust by 180 toward 0 degrees.
void Limit_Latitude_Longitude(double *latitude_degrees, double *longitude_degrees) {
*latitude_degrees = Limit_Longitude(*latitude_degrees);
int flip = 0;
if (*latitude_degrees > 90.0) {
*latitude_degrees = 180.0 - *latitude_degrees;
flip = 1;
} else if (*latitude_degrees < -90.0) {
*latitude_degrees = -180.0 - *latitude_degrees;
flip = 1;
}
if (flip) {
*longitude_degrees += *longitude_degrees > 0 ? -180.0 : 180.0;
}
*longitude_degrees = Limit_Longitude(*longitude_degrees);
}
Minor: Although the goal is "limit longitudes to (-180.0, 180.0]", I'd expect ranges of [-180.0, 180.0), [-180.0, 180.0] to be more commonly needed.
How about using the sin and inverse functions?
asin(sin((lat/180.0)*3.14159265)) * (180.0/3.14159265);
Neither answer provided (D Stanley, eh9) works ... though for eh9's I might be misinterpreting something. Try them with multiple values.
The proper answers are unfortunately expensive. See the following from Microsoft Research: https://web.archive.org/web/20150109080324/http://research.microsoft.com/en-us/projects/wraplatitudelongitude/.
From there, the answers are:
latitude_new = atan(sin(latitude)/fabs(cos(latitude))) -- note the absolute value around cos(latitude)
longitude_new = atan2(sin(latitude),cos(latitude))
Note that in C you may want to use atan2f (float vs double). Also, all trig functions take radians.
I'm using Sqlalchemy to define my tables and such and here is some code I came up with:
locations = Table('locations', Base.metadata,
Column("lat", Float(Precision=64), primary_key=True),
Column("lng", Float(Precision=64), primary_key=True),
)
I read somewhere that latitude and longitude require better precision than floats, usually double precision. So I set the precision manually to 64, is this sufficient? Overkill? Would this even help for my situation?
Nobody else here provided concrete numbers with proof for the worst-case accuracy of a floating point lat/long. I needed to know this for something I was working on, so here is my analysis in case it helps someone else.
A single-precision floating point offers 24-bits of precision in the significand (the binary exponential notation of a number). As the whole part of the number gets larger, the number of bits after the decimal goes down. Therefore, the worst-case accuracy for a latitude or longitude is when the magnitude is as far away from 0 as is possible. Assuming you bound your latitudes to [-90, 90] and longitudes from (-180, 180], the worst-case will be at the equator for longitude 180.
In binary, 180 requires 8-bits of the 24-bits available, leaving 16 bits after the decimal point. Therefore, the distance between consecutively representable values at this longitude would be 2^-16 deg (approximately 1.526E-5). Multiplying that number (in radians) by the WGS-84 radius of the Earth at the equator (6,378,137 m) yields a worst-case precision of:
2^-16 deg * 6,378,137 m * PI rad / 180 deg = 1.6986 m (5.5728 ft).
The same analysis against lat/longs stored in radians yields the following:
2^-22 rad * 6,378,137 m = 1.5207 m (4.9891 ft)
And finally, if you normalize the latitudes to the range [-1, 1] and the longitudes to the range (-1, 1], then you can achieve the following worst-case precision:
2^-24 * PI rad * 6,378,137 m = 1.1943 m (3.9184 ft)
So storing lat/long in radians buys you around 7 inches of additional accuracy, and storing them in normalized form buys you around 1'8" of additional accuracy, both in the worst-case scenario.
If, when converting between double-precision and single-precision you rounded (instead of truncating), the single-precision value will be within half of the distance between two consecutive values computed above.
It depends on what you are using your data for. If you use a float it will be ok if the you only need it down to about the meter level of detail. Using the data in graphically applications will cause a jitter effect if the user zooms in to far. For more about jitter and see Precisions, Precisions. Hope this helps.
Update: Jeff's answer has a better analysis. However...
To improve upon Jeff's answer:
If you divide the actual angle in radians by π, thus encoding the angle in a scale going from 0 to ±1, then it should be possible to use all the digits of the significand (23 bits (24 - 1 sign bit)). The precision would then be:
2^-23 * 6,378,137 m = 0.7603 m (76 cm)
My Old answer:
A 32 bit floating point number can represent a number with about 7.2 decimal digits of precision. This is an approximation because the floating point number is actually in binary, and when converted to decimal, the number of significant digits might vary.
If we take it as 6 decimal digits of precision (to play on the safe side), and if we are storing latitude and longitude in degrees, then we get a precision of about 1/1000th of a degree which is a precision of about 111 meters in the worst case. In the best case, if we get 7 decimal digits of precision, the accuracy would be about 11.1 meters.
It is possible to get a better precision using radians as the unit. In the worst case we get a precision of 10 millionth of a radian which is about 63 meters. In the best case, it would be 1 millionth of a radian which is about 6 meters.
Needless to say, a 64bit floating point number would be extremely precise (about 6 micro meters in the worst case).
TL;DR: if one-meter resolution is acceptable then a single-precision float storing degrees is acceptable.
This answer is a bit late to the party but I needed a solid answer myself and so hacked out some code to quickly get it. There are of course more elegant ways to do this, but it looks to work. As noted by Jeff, the worst case scenario will be at +/- 180 degrees longitude (ie, the date line).
Per the code below, a single-precision float is accurate to 0.85 meters at the date line using single-precision floats storing degrees. Accuracy increases significantly (to w/in mm) when close to the Prime meridian.
#include <stdio.h>
// per wikipedia, earth's circumference is 40,075.017 KM
#define METERS_PER_DEG (40075017 / 360.0)
// worst case scenario is near +/-180.0 (ie, the date line)
#define LONGITUDE 180.0
int main()
{
// subtract very small but increasingly larger values from
// 180.0 and recast as float until it no longer equals 180.0
double step = 1.0e-10;
int ctr = 1;
while ((float) LONGITUDE == (float) (LONGITUDE - (double) ctr * step)) {
ctr++;
}
double delta = (double) ctr * step;
printf("Longitude %f\n", LONGITUDE);
printf("delta %f (%d steps)\n", delta, ctr);
printf("meters: %f\n", delta * METERS_PER_DEG);
return 0;
}
Output from this code is
Longitude 180.000000
delta 0.000008 (76294 steps)
meters: 0.849301