Calculating rotation in > 360 deg. situations
I'm trying to work out a problem I'm having with degrees. I have data that is a list of of angles, in standard degree notation -- e.g. 26 deg.
Usually when dealing with angles, if an angle exceeds 360 deg then the angle continues around and effectively "resets" -- i.e. the angle "starts again", e.g. 357 deg, 358 deg, 359 deg, 0 deg, 1 deg, etc. What I want to happen is the degree to continue increasing -- i.e. 357 deg, 358 deg, 359 deg, 360 deg, 361 deg, etc. I want to modify my data so that I have this converted data in it.
开发者_开发知识库When numbers approach the 0 deg limit, I want them to become negative -- i.e. 3 deg, 2 deg, 1 deg, 0 deg, -1 deg, -2 deg, etc.
With multiples of 360 deg (both positive and negative), I want the degrees to continue, e.g. 720 deg, etc.
Any suggestions on what approach to take? There is, no doubt, a frustratingly simple way of doing this, but my current solution is kludgey to say the least .... ! My best attempt to date is to look at the percentage difference between angle n and angle n - 1. If this is a large difference -- e.g. > 60% -- then this needs to be modified, by adding or subtracting 360 deg to the current value, depending on the previous angle value. That is, if the previous angle is negative, substract 360, and add 360 if the previous angle is positive.
Any suggestions on improving this? Any improvements?
What you're talking about is an unwrap
algorithm, which generalizes (not specific to the number 360... you could do it in radians with m=2*pi). Here's one in javascript:
/* symmetric modulo:
* y = smod(x,m) = x+k*m where k is an integer,
* and y is always in the range [-0.5,0.5)*m
*/
function smod(x, m)
{
return x-((Math.floor(x/m + 0.5))*m);
}
/* unwrap:
* for all i, y[i] = x[i] + k*m where k is an integer,
* and for i > 0, the increment y[i]-y[i-1] is in the
* range [-0.5,0.5)*m as in smod().
*
* the "init" parameter is optional (default to 0)
* and specifies the starting value for the unwrap state.
*/
function unwrap(x, m, init)
{
var yi = init || 0;
var y = [];
for (i = 0; i < x.length; ++i)
{
yi += smod(x[i]-yi, m);
y[i] = yi;
}
return y;
}
And here's a sample output:
js>unwrap([100, 200, 348, 359, 23, 37, 46, 10, 350, 190], 360)
100,200,348,359,383,397,406,370,350,190
another one with m=100:
js>unwrap([99,1,7,60,80,22,30,20,90,88,61,23,2,87,50,12], 100, 1000)
999,1001,1007,960,980,1022,1030,1020,990,988,961,923,902,887,850,812
FYI: In C/Java/etc. a similar algorithm exists for bit extension, where the inputs are, say, 16 bits and the output is 32 bits, and the wraparound modulus m = 65536 = the span of input values. You don't need an "smod" function, just use signed math:
typedef short int16_t;
typedef long int32_t;
// do typedefs as appropriate on your CPU
int32_t unwrap_extend(int32_t prev, int16_t input)
{
int16_t delta = input - prev;
return prev + delta;
}
Can you just keep adding / subtracting all of your degrees and then use a MODULUS operator on the final result with 360. This will give you the remaining degrees.
Take the difference of each angle and the previous one using some method to ensure you get the correct sign when crossing 0/360 in both directions. Then add this difference to a running total that does not roll over.
If I understand the problem correctly, this might work:
int prev=[first data piece in data set]
int total=prev
foreach data in [the rest of the data set]
total+=data-prev
prev=data
data=total
Then at the end of the loop, the dataset will contain all of the data, added up as you've specified.
So basically loop through the dataset, and add the difference to the running total. The running total becomes each data piece as you iterate.
This C program takes a list of angles in the range 0 to 359 degrees from standard input and prints unbounded values to standard output by accumulating changes in angles. Wrap around is detected by assuming a maximum possible change in angle per input.
#include <stdio.h>
#include <stdlib.h>
int
main()
{
const float MAX_DELTA = 180.f; /* highest expected change */
float previous, next, delta, output;
/* set the initial value */
if (EOF == scanf("%f", &next))
exit(0);
previous = next;
output = previous;
do {
/* calculate the change in angle and adjust if too big */
delta = next - previous;
if (MAX_DELTA < delta)
delta -= 360.f;
else if (-MAX_DELTA > delta)
delta += 360.f;
/* accumlate the changes without wrap-around */
output += delta;
printf("%f\n", output);
/* store the value for calculating the next delta */
previous = next;
/* read angle values until end of file is reached */
} while (EOF != scanf("%f", &next));
exit(0);
}
Use modulous function
static inline float GetAbsoluteModulous(float input ,float devisor )
{
double output = (devisor==0)?input:fmod(input, devisor);
return (output>0)?output:devisor+output;
}
angle = GetAbsoluteModulous(angle,360);
精彩评论