Monday, July 8, 2013

Modulus and Angle Wrapping

Introduction

Since this is my first blog post ever, I thought it would be fitting to start off with an introduction. My hope is for this blog to eventually become a useful resource for other software developers who find themselves solving problems similar to mine. Also, as an open source developer, I like sharing information so many of my posts will likely be rather lengthy. I will try to present it so that those who aren't interested can just skip whatever sections don't interest them.

I'll be starting my blog with a basic and fundamental concept--modulus--in the context of some of the work I've done in my applications. The background section describes the particular problem I was trying to solve. The next section describes the approach using the modulus operator and why I couldn't use the default implementation in C#. Finally, the solution describes how I solved the problem, and how it can be generalized for a wider array of applications.

Background

Synchrophasors are represented as vectors. Our systems store these vectors as two separate measurements: a magnitude and an angle. Angles are a bit unusual, however, in that their actual value is not as important as their values relative to each other. During normal operations in a three-phase power system, you expect each of the phases to be 120 degrees apart from the others. Measurements taken from different locations on the same phase should be 0 degrees different. These angles tend to be "wrapped," so to speak, between -180 degrees and 180 degrees so they can be easily compared to one another.

However, when exchanging data between two separate utilities, for instance, things get a bit trickier. Since the actual values don't matter so much as the difference between them, it's common for one utility's angles to be 120 degrees offset from another utility's. This causes problems because now the measurements taken by two different utilities on the same phase will look like they are 120 degrees out of phase. Since our software deals with collecting and exchanging synchrophasor data, we were often asked for a solution: to be able to not only add an arbitrary value to the measurements, but also to re-wrap the angles between -180 degrees and 180 degrees.

Modulus

There are several ways to get an angle into the proper range. The most obvious approach was to use two while loops, adding or subtracting 360 degrees until our angle finds its way into the zone. If you assume your angles don't fall very far outside the target range, it won't even be that costly to execute. In this case, however, since our system deals with these measurements in real time, and even small operations can become costly when dealing with large volumes of data, I was inclined to look for a more elegant approach.

Fortunately, there's a mathematical operation that seems to be designed for this very purpose. Indeed, if you just take the original value mod 360, you will end up with a value between 0 and 360, which can be easily adjusted to fit between -180 and 180. Sounds easy, but I always try to be wary of edge cases, and most examples assume you're dealing with positive integers. Fortunately, MSDN has good documentation for their modulo operator.

    class MainClass6
    {
        static void Main()
        {
            Console.WriteLine(5 % 2);       // int
            Console.WriteLine(-5 % 2);      // int
            Console.WriteLine(5.0 % 2.2);   // double
            Console.WriteLine(5.0m % 2.2m); // decimal
            Console.WriteLine(-5.2 % 2.0);  // double
        }
    }
    /*
    Output:
    1
    -1
    0.6
    0.6
    -1.2
    */

Unfortunately, that doesn't quite do what I want. The problem becomes just slightly more difficult if the value can fall between -360 and 360 depending on the sign of the numerator. I was positive that I could do better than that.

The solution

Using a mixture of Euclidean division and a simple transformation, it's actually pretty easy to arrive right between the desired range using straight mathematical operations--no conditionals or loops. Here's the solution.

    public static double WrapAngle(double angle)
    {
        double transform = angle + 180.0;
        double quotient = Math.Floor(transform / 360.0);
        double remainder = transform - 360.0 * quotient;
        return remainder - 180.0;
    }

Euclidean division is used to find the positive remainder regardless of the sign in the numerator. The transformation before and after are necessary because the result of the division is between 0 and 360. This can actually be generalized even further to wrap a value to any given range.

    public static double ToRange(double value,
                                 double minimum,
                                 double range)
    {
        double transform = value - minimum;
        double quotient = Math.Floor(transform / range);
        double remainder = transform - range * quotient;
        return remainder + minimum;
    }

Finally, one more note. My boss actually told me that I was supposed to wrap the angles such that -180 was not included in the range, but 180 was. So here's the final modification you'd need to make to solve that particular issue.

    public static double ToUpperRange(double value,
                                      double maximum,
                                      double range)
    {
        double transform = value - maximum;
        double quotient = Math.Ceiling(transform / range);
        double remainder = transform - range * quotient;
        return remainder + maximum;
    }

No comments:

Post a Comment