Friday, December 12, 2008

Reading and decoding RDS (Radio Data System) in C#

RDS or Radio Data System is very common in US and many European countries. It is communication protocol used to send small amount of digital information using regular FM radio broadcast. This protocol is used to "tell" your receiver about alternative frequencies, time, program notifications, program types, traffic information and regular text (such as singer name or genre). Unfortunately in Israel RDS is not very common and there is very limited number of radio stations broadcasts RDS information.

image

How RDS works?

As mentioned earlier, it uses FM subcarrier to broadcast digital information. It was designed to support 10 and 18 characters numeric and 80 characters alphanumeric displays. RDS operates at 1187.5 bps and based on 26-bit word consisting of 16 data and 10 error detection bits. Due to the fact, that FM carrier is not very reliable, error code allows correct information to be received even if an error of 3-5 bits exists within 26 bit block. Each four data blocks interpreted as 104-bit signal and named "group". Depending of the type of information, contained within the group, as different group type code is defined and transmitted within the group as upper five bits code. Even if more, then 104 bits required to completely send the information, there is no requirement that the next segment of the transmission be sent in the next group. There are 32 known groups types, defined by RFC:

private enum groupType : byte {
   RDS_TYPE_0A = (0 * 2 + 0),
   RDS_TYPE_0B = (0 * 2 + 1),
   RDS_TYPE_1A = (1 * 2 + 0),
   RDS_TYPE_1B = (1 * 2 + 1),
   RDS_TYPE_2A = (2 * 2 + 0),
   RDS_TYPE_2B = (2 * 2 + 1),
   RDS_TYPE_3A = (3 * 2 + 0),
   RDS_TYPE_3B = (3 * 2 + 1),
   RDS_TYPE_4A = (4 * 2 + 0),
   RDS_TYPE_4B = (4 * 2 + 1),
   RDS_TYPE_5A = (5 * 2 + 0),
   RDS_TYPE_5B = (5 * 2 + 1),
   RDS_TYPE_6A = (6 * 2 + 0),
   RDS_TYPE_6B = (6 * 2 + 1),
   RDS_TYPE_7A = (7 * 2 + 0),
   RDS_TYPE_7B = (7 * 2 + 1),
   RDS_TYPE_8A = (8 * 2 + 0),
   RDS_TYPE_8B = (8 * 2 + 1),
   RDS_TYPE_9A = (9 * 2 + 0),
   RDS_TYPE_9B = (9 * 2 + 1),
   RDS_TYPE_10A = (10 * 2 + 0),
   RDS_TYPE_10B = (10 * 2 + 1),
   RDS_TYPE_11A = (11 * 2 + 0),
   RDS_TYPE_11B = (11 * 2 + 1),
   RDS_TYPE_12A = (12 * 2 + 0),
   RDS_TYPE_12B = (12 * 2 + 1),
   RDS_TYPE_13A = (13 * 2 + 0),
   RDS_TYPE_13B = (13 * 2 + 1),
   RDS_TYPE_14A = (14 * 2 + 0),
   RDS_TYPE_14B = (14 * 2 + 1),
   RDS_TYPE_15A = (15 * 2 + 0),
   RDS_TYPE_15B = (15 * 2 + 1)
}

Not all groups are in use all the time. However, there are some commitments, defined by the protocol. For example, 1A have to be transmitted at least once a second. This group contains special information, required for receivers to be synchronized and locked into the transmitting channel.

Within the error correction information we also receive the direction to treat them.

private enum correctedType : byte {
   NONE = 0,
   ONE_TO_TWO = 1,
   THREE_TO_FIVE = 2,
   UNCORRECTABLE = 3
}

Also, each message type has it own limits. For example RT (Radio Text - 64 character text to display on your receiver) and PS (Programme Service - eight character station identification) message are limited to 2 groups, when PI (Programme Identification - unique code of the station) and PTY (Programme Type - one of 31 predefined program types - e.g. News, Drama, Music) are limited to 4.

In addition to those constraints, block types are also different. But in this case, there are only 4 kinds

private enum blockType : byte {
   A = 6,
   B = 4,
   C = 2,
   D = 0
}

So, what we're waiting for? Let's start working.

Handling errors

First of all we should take care on errors and fix them if possible. For this purpose, we should first count them and detect the way of fixing

var errorCount = (byte)((registers[0xa] & 0x0E00) >> 9);
var errorFlags = (byte)(registers[0x6] & 0xFF);
if (errorCount < 4) {
   _blocksValid += (byte)(4 - errorCount);
} else { /*drop data on more errors*/ return; }

Once it done, we can try to fix them

//Also drop the data if more than two errors were corrected
if (_getErrorsCorrected(errorFlags, blockType.B) > correctedType.ONE_TO_TWO) return;

private correctedType _getErrorsCorrected(byte data, blockType block) { return (correctedType)((data >> (byte)block) & 0x30); }

Now, our registers should be fine and we can start the detection of group type

Group Type Detection

This is very simple task, all we have to do is to get five upper bites to get a type and version.

var group_type = (groupType)(registers[0xD] >> 11);

Then we can handle PI and PTY, which we always have in RDS.

PI and PTY treatment

Now, let's update pi code, due to the fact, that B format always have PI in words A and C

_updatePI(registers[0xC]);

if (((byte)group_type & 0x01) != 0) {
_updatePI(registers[0xE]);
}

To update PI, we should check whether the new value is different from the previous and update it only in case it changed.

private void _updatePI(byte pi) {
   uint rds_pi_validate_count = 0;
   uint rds_pi_nonvalidated = 0;

   // if the pi value is the same for a certain number of times, update a validated pi variable
   if (rds_pi_nonvalidated != pi) {
      rds_pi_nonvalidated = pi;
      rds_pi_validate_count = 1;
   } else {
      rds_pi_validate_count++;
   }

   if (rds_pi_validate_count > PI_VALIDATE_LIMIT) {
      _piDisplay = rds_pi_nonvalidated;
   }
}

Then we will update PTY

_updatePTY((byte)((registers[0xd] >> 5) & 0x1f));

PTY treatment is very similar to PI, however it can be multiplied.

private void _updatePTY(byte pty) {
   uint rds_pty_validate_count = 0;
   uint rds_pty_nonvalidated = 0;

   // if the pty value is the same for a certain number of times, update a validated pty variable
   if (rds_pty_nonvalidated != pty) {
      rds_pty_nonvalidated = pty;
      rds_pty_validate_count = 1;
   } else {
      rds_pty_validate_count++;
   }

   if (rds_pty_validate_count > PTY_VALIDATE_LIMIT) {
      _ptyDisplay = rds_pty_nonvalidated;
   }
}

When we done with those two groups, we can start handling another. Today, we'll handle only 0B, 2A and 2B types (I have a good reason for it, due to the fact, that only those are supported in Israel by now :) ) So,

Handling PS and different RTs

Simple switch on those groups

switch (group_type) {
   case groupType.RDS_TYPE_0B:
      addr = (byte)((registers[0xd] & 0x3) * 2);
      _updatePS((byte)(addr + 0), (byte)(registers[0xf] >> 8));
      _updatePS((byte)(addr + 1), (byte)(registers[0xf] & 0xff));
      break;
   case groupType.RDS_TYPE_2A:
      addr = (byte)((registers[0xd] & 0xf) * 4);
      abflag = (byte)((registers[0xb] & 0x0010) >> 4);
      _updateRT(abflag, 4, addr, (byte[])registers.Skip(0xe), errorFlags);
      break;
   case groupType.RDS_TYPE_2B:
      addr = (byte)((registers[0xd] & 0xf) * 2);
      abflag = (byte)((registers[0xb] & 0x0010) >> 4);
      // The last 32 bytes are unused in this format
      _rtTmp0[32] = 0x0d;
      _rtTmp1[32] = 0x0d;
      _rtCnt[32] = RT_VALIDATE_LIMIT;
      _updateRT(abflag, 2, addr, (byte[])registers.Skip(0xe), errorFlags);
      break;
}

and let's dig into PS.

In PS, we have high and low probability bits. So, if new bit in sequence matches the high probability bite and we have recieved enough bytes to max out the counter, we'll push it into the low probability array.

if (_psTmp0[idx] == default(byte)) {
           if (_psCnt[idx] < PS_VALIDATE_LIMIT) {
               _psCnt[idx]++;
            } else {
               _psCnt[idx] = PS_VALIDATE_LIMIT;
               _psTmp1[idx] = default(byte);
            }
         }

Else, if new byte matches with the low probability byte, we should swap them and then reset the counter, by flagging the text as in transition.

else if (_psTmp1[idx] == default(byte)) {
            if (_psCnt[idx] >= PS_VALIDATE_LIMIT) {
               isTextChange = true;
            }
            _psCnt[idx] = PS_VALIDATE_LIMIT + 1;
            _psTmp1[idx] = _psTmp0[idx];
            _psTmp0[idx] = default(byte);
         }

When we have an empty byte in high probability array or new bytes does not match anything we know, we should put it into low probability array.

else if (_psCnt[idx] == null) {
            _psTmp0[idx] = default(byte);
            _psCnt[idx] = 1;
         } else {
            _psTmp1[idx] = default(byte);
         }

Now, if we marked our text as changed, we should decrement the count for all characters to prevent displaying of partical message, which in still in transition.

         if (isTextChange) {
            for (byte i = 0; i < _psCnt.Length; i++) {
               if (_psCnt[i] > 1) {
                  _psCnt[i]--;
               }
            }
         }

Then by checking PS text for incompetence, when there are characters in high probability array has been seen fewer times, that was limited by validation.

         for (byte i = 0; i < _psCnt.Length; i++) {
            if (_psCnt[i] < PS_VALIDATE_LIMIT) {
               isComplete = false;
               break;
            }
         }

Only if PS text in the high probability array is complete, we'll copy it into display.


         if (isComplete) {
            for (byte i = 0; i < _psDisplay.Length; i++) {
               _psDisplay[i] = _psTmp0[i];
            }
         }

It is not very hard to treat PS. Isn't it? Let's see what's going on with RT.

If A and B message flag changes, we'll try to force a display by increasing the validation count for each byte. Then, we'll wipe any cached text.

   if (abFlag != _rtFlag && _rtFlagValid) {
      // If the A/B message flag changes, try to force a display
      // by increasing the validation count of each byte
      for (i = 0; i < _rtCnt.Length; i++) _rtCnt[addr + i]++;
      _updateRTValue();

      // Wipe out the cached text
      for (i = 0; i < _rtCnt.Length; i++) {
         _rtCnt[i] = 0;
         _rtTmp0[i] = 0;
         _rtTmp1[i] = 0;
      }
   }

Now A and B flags are safe, sp we can start with message processing. First of all, NULL in RDS means space :)


   _rtFlag = abFlag;    
   _rtFlagValid = true;   

   for (i = 0; i < count; i++) {
      if (p[i] == null) p[i] = (byte)' ';

The new byte matches the high probability byte also in this case. We habe to recieve this bite enough to max out counters. Then we can push it into the low probability as well.

      if (_rtTmp0[addr + i] == p[i]) {
         if (_rtCnt[addr + i] < RT_VALIDATE_LIMIT) _rtCnt[addr + i]++;
         else {
            _rtCnt[addr + i] = RT_VALIDATE_LIMIT;
            _rtTmp1[addr + i] = p[i];
         }
      }

When the new byte matches with low probability byte, we'll swap them as well and reset counters to update text in transition flag. However in this case, our counter will go higher, then the validation limit. So we'll have to remove it down later.

else if (_rtTmp1[addr + i] == p[i]) {

         if (_rtCnt[addr + i] >= PS_VALIDATE_LIMIT) isChange = true;

         _rtCnt[addr + i] = RT_VALIDATE_LIMIT + 1;
         _rtTmp1[addr + i] = _rtTmp0[addr + i];
         _rtTmp0[addr + i] = p[i];
      }

Now, the new byte is replaced an empty byte in the high probability array. Also, if this byte does not match anything, we should move it into low probability.

else if (_rtCnt[addr + i] == null) { 
         _rtTmp0[addr + i] = p[i];
         _rtCnt[addr + i] = 1;
      } else _rtTmp1[addr + i] = p[i];

   }

Now when the text is changing, we'll decrement the counter for all characters exactly as we did for PS.


      for (i = 0; i < _rtCnt.Length; i++) {
         if (_rtCnt[i] > 1) _rtCnt[i]--;
      }
   }

However, right after, we'll update display. 

   _updateRTValue();
}

Displaying RT

But how to convert all those byte arrays into readable message? Simple :)

First of all if text is incomplete, we should keep loading it. Also it makes sense to check whether the target array is shorter then maximum allowed to prevent junk from being displayed.

for (i = 0; i < _rtTmp0.Length; i++) {
   if (_rtCnt[i] < RT_VALIDATE_LIMIT) {
      isComplete = false;
      break;
   }
   if (_rtTmp0[i] == 0x0d) {
      break;
   }
}

Now, when our Radio Text is in the high probability and it complete, we should copy buffers.

if (isComplete) {
   _Text = string.Empty;

   for (i = 0; i < _rtDisplay.Length; i += 2) {
      if ((_rtDisplay[i] != 0x0d) && (_rtDisplay[i + 1] != 0x0d)) {
         _rtDisplay[i] = _rtTmp0[i + 1];
         _rtDisplay[i + 1] = _rtTmp0[i];
      } else {
         _rtDisplay[i] = _rtTmp0[i];
         _rtDisplay[i + 1] = _rtTmp0[i + 1];
      }

      if (_rtDisplay[i] != 0x0d)
         _Text += _rtDisplay[i];

      if (_rtDisplay[i + 1] != 0x0d)
         _Text += _rtDisplay[i + 1];

      if ((_rtDisplay[i] == 0x0d) || (_rtDisplay[i + 1] == 0x0d))
         i = (byte)_rtDisplay.Length;
   }

And not forget to wipe out everything after the end of the message :)


   for (i++; i < _rtDisplay.Length; i++) {
      _rtDisplay[i] = 0;
      _rtCnt[i] = 0;
      _rtTmp0[i] = 0;
      _rtTmp1[i] = 0;
   }
}

And finally update the text

Text = _Text;

We done. Now we can handle RDS digital messages, but what to do with analog data we get? Don't you already know? I blogged about it here.

Have a nice day and be good people, because you know how to write client, knows to get and parse radio data in managed code.

image

Friday, November 21, 2008

Creating transparent buttons, panels and other control with Compact Framework and putting one into other

In WPF/Silverlight world it's very simple to make transparent controls and put anything inside anything. However, that's not the situation in WinForms, and even worth in the world of compact devices with CF. Within this worlds, there is only one way to make controls transparent - to use color masks. Today, we'll create transparent controls with Compact Framework and put it into panel, which has image background.

image

So let's start. First of all, we need create our own control. For this purpose, we have to inherit from Control and override couple of things. More precise: OnPaint and OnPaintBackground. We do not want to paint background for transparent control, so let's prevent it.

public class TransparentImageButton : Control

protected override void OnPaintBackground(PaintEventArgs e) {
           //prevent
       }

       protected override void OnPaint(PaintEventArgs e) {

Next, we have to get graphics, delivered by OnPain event argument and draw our image over it. However, BitBlt (which is used by core graphics system) is not very fast method, so it's better for us to draw everything first and then copy final image to the device.

Graphics gxOff;
Rectangle imgRect;
var image = (_isPressed && PressedImage != null) ? PressedImage : Image;

if (_imgOffscreen == null) {
_imgOffscreen = new Bitmap(ClientSize.Width, ClientSize.Height);
}

gxOff = Graphics.FromImage(_imgOffscreen);
gxOff.Clear(this.BackColor);  
...
         

if (image != null) {
var imageLeft = (this.Width - image.Width) / 2;
var imageTop = (this.Height - image.Height) / 2;

if (!_isPressed) imgRect = new Rectangle(imageLeft, imageTop, image.Width, image.Height);
else imgRect = new Rectangle(imageLeft + 1, imageTop + 1, image.Width, image.Height);
var imageAttr = new ImageAttributes();

To make images transparent, we have to use (as mentioned earlier) transparency color key (to tell windows what color it should not draw. We can code or provide this value to detect it by hitting any pixel on the image. Just like this:

public static Color BackgroundImageColor(this Bitmap bmp) {
           return bmp.GetPixel(0, 0);
       }

Now we can keep working.

imageAttr.SetColorKey(image.BackgroundImageColor(), image.BackgroundImageColor());
gxOff.DrawImage(image, imgRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, imageAttr);
} if (_isPressed) {
var rc = this.ClientRectangle;
  rc.Width--;
  rc.Height--;
  gxOff.DrawRectangle(new Pen(Color.Black), rc);
}
e.Graphics.DrawImage(_imgOffscreen, 0, 0);

Also, we have to provide others with possibility to handle this even too, thus we will not forget to add base.OnPaint(e); at  the end.

Next step is to detect whether our button is clicked or not. We'll override keyboard and mouse events to detect this.

protected override void OnKeyDown(KeyEventArgs e) {
            _isPressed = this.Focused; this.Invalidate();
            base.OnKeyDown(e);
        }

        protected override void OnKeyUp(KeyEventArgs e) {
            _isPressed = false; this.Invalidate();
            base.OnKeyUp(e);
        }

        protected override void OnMouseDown(MouseEventArgs e) {
            _isPressed = this.Focused; this.Invalidate();
            base.OnMouseDown(e);
        }

        protected override void OnMouseUp(MouseEventArgs e) {
            _isPressed = false; this.Invalidate();
            base.OnMouseUp(e);
        }

Compile and run to see no problem, when our transparent button lies on solid color control, however, we want to put it into panel with background - just like this one. In this case, you can use real transparent PNG and GIF images, also you can replace transparent color with well known Magenta (or any other color).

public class ImagePanel : Panel {

        public Bitmap Image { get; set; }

        protected override void OnPaintBackground(PaintEventArgs e) {
            e.Graphics.DrawImage(Image, 0, 0);
        }
    }

When we'll put it onto anything, that has no background color, we'll see that our "fake transparency" disappears. Why this happen? To provide transparency Windows uses color masks, also while confederating facts, clipping algorithm within GDI is not very trustful, thus the only thing can be taken into account is color. But what to do if we have an image? We should clip it manually. We cannot just get the handle to parent device surface (see above about trustful GDI), so the only way to do it is by providing something, that we know for sure. For example interface, telling us, that parent has image, which drawn on the screen.

internal interface IHaveImage {
        Bitmap Image { get; set; }
    }

When we know it, all we have to do is to clip the region of this image (not device context) and draw it as part of our really transparent control.

if (this.Parent is IHaveImage) {
                var par = this.Parent as IHaveImage;
                gxOff.DrawImage(par.Image.Clip(this.Bounds), 0, 0);
            }

The implementation of Image.Clip is very straight forward.

public static Bitmap GetSS(this Graphics grx, Rectangle bounds) {
    var res = new Bitmap(bounds.Width, bounds.Height);
    var gxc = Graphics.FromImage(res);
    IntPtr hdc = grx.GetHdc();
    PlatformAPI.BitBlt(gxc.GetHdc(), 0, 0, bounds.Width, bounds.Height, hdc, bounds.Left, bounds.Top, PlatformAPI.SRCCOPY);
    grx.ReleaseHdc(hdc);
    return res;
}

public static Bitmap Clip(this Bitmap source, Rectangle bounds) {
    var grx = Graphics.FromImage(source);
    return grx.GetSS(bounds);
}

We done. Compiling all together will fake transparency for controls, even when it's parents background is not pained with  solid color brush.

Source code for this article

P.S. Do not even try to inherit your custom Button control from framework Button class, dev team "forgot" to expose it's event for override. So, OnPaint, OnPaintBackground, OnKeyUp, OnKeydown, OnMouseUp and OnMouseDown aside with most of other base events will not work for you, also BaseButton class has no default constructor, so the only class you can inherit from is Control.

Have a nice day and be good people.

Wednesday, November 19, 2008

How to P/Invoke VarArgs (variable arguments) in C#? ... or hidden junk in CLR

Recently I wrote a cheat sheet for pinvoking in .NET. Shortly after I got a question in comments about how to deal with variable arguments, when it's more, then one parameter. Also what to do if those arguments are heterogeneous?

Let's say, that we have following method in C:

int VarSum(int nargs, ...){
    va_list argp;
    va_start( argp, nargs );
    int sum = 0;
    for( int i = 0 ; i < nargs; i++ ) {
        int arg = va_arg( argp, int );
        sum += arg;
    }
    va_end( argp );

    return sum;
}

We can expose this method to C# as following:

[System.Runtime.InteropServices.DllImportAttribute("unmanaged.dll", EntryPoint = "VarSum")]
        public static extern int VarSum(int nargs,int arg1);

[System.Runtime.InteropServices.DllImportAttribute("unmanaged.dll", EntryPoint = "VarSum")]
        public static extern int VarSum(int nargs,int arg1,int arg2);

[System.Runtime.InteropServices.DllImportAttribute("unmanaged.dll", EntryPoint = "VarSum")]
        public static extern int VarSum(int nargs,int arg1,int arg2,int arg3);

etc...

And it will work. However, if you'll try to expose it as int array, marshaller will fail to understand how to align things

[System.Runtime.InteropServices.DllImportAttribute("unmanaged.dll", EntryPoint = "VarSum")]
        public static extern int VarSum(int nargs,int[] arg);

This in spite of the fact, that this method will work properly with another signature

int ArrSum(int* nargs) {
    int sum = 0;
    for( int i = 0 ; i < 2; i++ ) {
        sum += nargs[i];
    }
    return sum;
}

So what to do? The official answer is - you have nothing to do, rather then override all possibilities. This is very bad and absolutely not flexible. So, there is small class in C#, named ArgIterator. This one is similar to params object[], but knows to marshal into varargs. The problem is, that you have no way to add things inside. It's "kind-of-read-only".

Let's look into reflected version of ArgIterator. We'll see there something, named __arglist and __refvalue. OMG, isn't it good old stuff similar to "__declspec(dllexport) int _stdcall" etc.? It is! But can we use it in C#? We can! Just sign your method as Cdecl and you have working signature for "..."

[System.Runtime.InteropServices.DllImportAttribute("unmanaged.dll", EntryPoint = "VarSum",
            CallingConvention=System.Runtime.InteropServices.CallingConvention.Cdecl)]
        public static extern int VarSum(int nargs, __arglist);

Yes, looks strange, and absolutely not CLR compliant. However, this is the only way to expose varargs to CLR via P/Invoke. How to use it? Simple:

c = VarSum(2, __arglist(5, 10));

Have a nice day and be good people. Also, my question to Microsoft is why this stuff is not in MSDN and we, as developers, have no way to get rid of it.

Is not it very good practices to use non-compliant methods? Give us another way to do it!
Is not it very good practices to use variable arguments in unmanaged method signatures? So why you want dynamic types in C# 4?

Source code for this article

Wednesday, November 12, 2008

Consultants for charity

As you, probably, know, I left consulting field. However, it does not mean, that I quit helping developers community with client application development. Also, every day I'm getting between 50 and 300 emails with questions (I'm trying to answer all of those) and sometimes proposals for consulting. Currently I'm refusing all those, because I do not want to engage to it. However, there are too much people, who really need professional developers help and there are very few good development consultants in our area. Thus I decided to keep consulting, but this time only for charity.

image
© ColorBlind photographers

How does it work?

  1. You want me to help you with your development.
  2. I have free time for it.
  3. We decide together about the fee.
  4. You get consulting and you are happy with it.
  5. I tell you what charity organization to transfer all amount, you should pay (except TBL, if there are).
  6. You transfer it.
  7. We made the world a bit better!

To clarify things:

  1. It's not charity foundation - you will transfer the money directly to organization, that need it
  2. I'm not doing it for free - I feel, that finally I'm able to do something really big for those, who need it

So, if you are one of those, who want me to consult, contact me via this form or Twitter.

If you're good consultant and want to join me, contact me via this form or Twitter and we'll make the world better together.

I still had no chance to speak with my ex-engagement manager, however I believe, that he will not have a problem with this kind of payment to me. If so (and you have open PO in Microsoft Israel with him), you'll be able to use it.

Spear the world with this news! Post in your blogs, twitters, facebook, any other community stuff or just join me :)

Monday, November 10, 2008

Auto scroll ListBox in WPF

In WinForms era it was very simple to autoscroll listbox content in order to select last or newly added item. It become a bit complicated in WPF. However, complicated does not mean impossible.

image

As for me, Microsoft should add this feature to base ListBox implementation as another attempt to be attractive environment for LOB application. See, for example this thread from MSDN forums. I'm really understand this guy. He do not want to implement it with a lot of code, he just want it to be included in core WPF control (but he should mark answers)

Generally, the simplest way to it is by using attached properties. So, your code will look like this

<ListBox Height="200" l:SelectorExtenders.IsAutoscroll="true" IsSynchronizedWithCurrentItem="True" Name="list"/>

But what's going on under the hoods? There it bit complicated :) First of all, we should create attached property, named IsAutoscroll

public class SelectorExtenders : DependencyObject {

        public static bool GetIsAutoscroll(DependencyObject obj) {
            return (bool)obj.GetValue(IsAutoscrollProperty);
        }

        public static void SetIsAutoscroll(DependencyObject obj, bool value) {
            obj.SetValue(IsAutoscrollProperty, value);
        }

        public static readonly DependencyProperty IsAutoscrollProperty =
            DependencyProperty.RegisterAttached("IsAutoscroll", typeof(bool), typeof(SelectorExtenders), new UIPropertyMetadata(default(bool),OnIsAutoscrollChanged));

now handle it when you set it's value by handling new items arrivals, set current and then scroll into it

public static void OnIsAutoscrollChanged(DependencyObject s, DependencyPropertyChangedEventArgs e) {
            var val = (bool)e.NewValue;
            var lb = s as ListBox;
            var ic = lb.Items;
            var data = ic.SourceCollection as INotifyCollectionChanged;

            var autoscroller = new System.Collections.Specialized.NotifyCollectionChangedEventHandler(
                (s1, e1) => {
                    object selectedItem = default(object);
                    switch (e1.Action) {
                        case NotifyCollectionChangedAction.Add:
                        case NotifyCollectionChangedAction.Move: selectedItem = e1.NewItems[e1.NewItems.Count - 1]; break;
                        case NotifyCollectionChangedAction.Remove: if (ic.Count < e1.OldStartingIndex) { selectedItem = ic[e1.OldStartingIndex - 1]; } else if (ic.Count > 0) selectedItem = ic[0]; break;
                        case NotifyCollectionChangedAction.Reset: if (ic.Count > 0) selectedItem = ic[0]; break;
                    }

                    if (selectedItem != default(object)) {
                        ic.MoveCurrentTo(selectedItem);
                        lb.ScrollIntoView(selectedItem);
                    }
                });

            if (val) data.CollectionChanged += autoscroller; 
            else  data.CollectionChanged -= autoscroller;

        }

That's all. Also it can be done by visual tree querying (as thread started proposed). Find scrollviewer inside the ListBox visual tree and then invoke "ScrollToEnd" method of it.

Have a nice day and be good people. And, yes, WPF development team should consider this feature implemented internally for any Selector and ScrollViewer control

Source code for this article

Sunday, November 09, 2008

Programming for Windows 7

Well, Windows 7 is going to be released by the end of next year. This is great news, because it seemed, that Microsoft finally understand how to get the best of Windows Vista and make it to work not only on monster machines.

image

It even works on new brandy my wife's pinky machine. And if it works there and my wife is happy with it, this OS going to be very impressive.

image

But from the other hand, we, as developers should be ready today to developer Windows 7 ready application (by the way, Vista Battery Saver works for Windows 7 as well as for Windows Vista, in spite of the fact, that power management in Windows 7 was improved dramatically). So let's start!

First thing we need is to read big Windows 7 Developer Guide. This document will explain most of new features for developers to build applications right. What is includes?

Windows Driver Kit (WDK) 3.0

Basically, Windows 7 works with Vista drivers, however, hibernation, power management, networking, PREfast will work much better. You also will have new WMI access for reliability monitors and ACPI.

Management and deployment

By default Windows 7 uses PowerShell 2.0 and Windows Installer. For PowerShell it includes enhanced cmdlets to manage Active Directory, IIS, etc. For Windows Installer, you finally can build "chainers" by yourself (the same approach, used for latest deployment of Microsoft products such as Silverlight, Visual Studio 2008 SP1 etc.) Also, you can get advantage by using Windows Filtering Platform (Firewall) and User Account Control (UAC) from inside your application by using new APIs.

Performance

The most significant change in Windows 7 for end-user point of view is improved performance. Windows 7 kernel is much smaller, that kernel of Windows Vista. Also it uses specific patterns to decrease background activities on low power, based on system triggers. New user-mode and kernel-mode APIs are used by Windows Drivers Foundation much more efficiently. Also system services are much smarter. For example, DCIA starts only when you connect new hardware. After drivers were installed the service shuts down. The same approach used by domain join, GP changes, new IP fetching etc. Windows 7 knows to run and stop services, based on system events, which decreases average work load and enhances whole system performance.

Multi-touch gestures and Interia API and used interface in general

Yes, you can use this API for your applications. Finally we can have more, then just mouse. And it is not only about multiple mouse devices. We can use single finder panning, raw touch input data, internal multitouch ink recognition, which is also supports math. Also it uses build-in MathML export feature.

There are a lot of other enhancements, such as smart bars, windows' stacking, gadget desktop (it does not eat battery as external process anymore), system ribbon menu integration. etc

Graphics

Direct 11, new Direct2D, DirectWrite (we can turn text anti-aliasing for small fonts, hurrah!), improved WIC, DX/GDI interoperability on system level with automatic fallback for weak hardware (yes, you should not be worry about it anymore). Also new video and audio format support with human readable interfaces. Yes, no more DirectDraw hacks. We can use new high level interfaces such as MFPlay to manage playbacks, Source Reader for decoding, Sink Writer for transcoders and re-coding compressions.

Web and communication

WCF is inside, as well as distributed routing table for peer-to-peer operations. BranchCache - new technology to reduce WAN traffic and latency.

Also Windows 7 is compatible with OpenSearch (I told, that Microsoft does not know to build search engines). Sharepoint integration and environment sensors platform, that can be used either for desktop and web applications.

There are much more features, that makes Windows 7 to pretend to be very good operation system. If you want to learn more about all those Windows 7 new features, I highly advice you to download and read this document. It includes most of new features of new OS with explanations and screenshots to make your learn and understand what can your future application do with all those new features.

Have a nice day and be good people.

BTW, if you have PDC version of Windows 7 and want to unlock it for using of some cool features, introduced during keynotes, it worth to visit here and learn how to :)

Download Windows 7 Developer Guide and start programming.

Monday, October 20, 2008

P/Invoke cheat sheet

I’m working a lot with p/invoke, and know how it’s hard to produce correct signature for unmanaged method. So, today I decided to publish basic cheat sheet for methods, parameters and attributes you should use in order to invoke unmanaged methods from managed code without a lot of problems. We start with data type translations. Here the table to understand it.

Data type from unmanaged signature Data type in managed signature
int int

the same with all other simple types such as double, uint, etc or private objects
void* IntPtr
int* ref int

the same with all other simple types such as double, uint, etc or private objects
char** ref IntPtr

later, you should get ascii string by using System.Runtime.InteropServices.Marshal.PtrToStringAnsi() method
wcar_t** ref IntPtr

later, you should get ascii string by using System.Runtime.InteropServices.Marshal.PtrToStringUni() method
const int* ref int
const char* [System.Runtime.InteropServices.In()] [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)] string
(variable argument) [System.Runtime.InteropServices.In()] [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.AsAny)] object

You can use either System.Runtime.InteropServices.In or System.Runtime.InteropServices.Out attribute to specify how arguments should be used.

Now we done with simple arguments, let’s see what can be done when argument is actually callback or delegate?

Unmanaged definition Managed definition
typedef void (*MyCallback)(int Arg)

[System.Runtime.InteropServices.UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.Cdecl)]delegate void MyCallback(int Arg)
Caller cleans stack argument is used to assure, that we can call varargs type function, usually used by API provider. It is very similar to C# overrides for methods. Also you can use StdCall (this is default), ThisCall – stores this first and pushes other parameters on the stack, FastCall – not very supported :(

 

To call all those methods, we should know managed equivalents of unmanaged types. Here the table. The rule is simple – know how many bytes unmanaged type has and find managed type with the same number of bytes. Other words, you can marshal int into IntPtr too…

Unmanaged type Managed equivalent
bool bool
char sbyte (signed), byte (unsigned)
wchar_t char
double double
float single
int, long (signed) Int32
int, long (unsigned) UInt32
__int64 (signed) Int64
__int64 UInt64
short (signed) Int16
short (unsigned) UInt16
void void

But not only types are problem in managed/unmanaged transitions. Also structures are aligned differently. For this purpose we can use StructLayout attribute. Even if unmanaged classes are sequential and you used correct managed data types, you can find you with problems in Pack. What “pack” is? Pack is actually slot size in bytes for members of your structure. It can be 0, 1, 2, 4, 8, 16, 32, 64, or 128 and depends on the platform and application setting.

Now you can see, that it is not very complicated to create managed signatures when you have header of unmanaged assemblies. So go ahead and ask, if I missed something.

That’s all by now. Have a nice day and be good people.

Monday, October 13, 2008

Set binding, based on trigger

Let’s say, that you want to set binding. However, you want to set it by using some trigger. Wait! Why I need it? Let’s say, that I have some very special object, that actually has no hard-coded properties. All it’s properties are created during the runtime, based on some trigger. In this case, I still want to use binding, however, if the property does not exists, I cannot do it. In this special case (that we’ll probably speak more later), we can use this class – TriggerBinding.

How to do it? How to extend build-in binding and add such functionality? Simple. All we need is to write custom markup extension. So, we’ll create new one

public class TriggerBindingExtension : MarkupExtension

And expose all regular binding properties + three custom – TriggerSource, TriggerSourceName and Trigger itself

public IValueConverter Converter { get; set; }
public object ConverterParameter { get; set; }
public string ElementName { get; set; }
public RelativeSource RelativeSource { get; set; }
public object Source { get; set; }
public string TriggerSourceName { get; set; }
public UIElement TriggerSource { get; set; }
[TypeConverter(typeof(RoutedEventConverter))]
public RoutedEvent Trigger { get; set; }
public bool ValidatesOnDataErrors { get; set; }
public bool ValidatesOnExceptions { get; set; }
public string StringFormat { get; set; }
[TypeConverter(typeof(EnumConverter))]
public BindingMode Mode { get; set; }
[TypeConverter(typeof(PropertyPathConverter))]
public PropertyPath Path { get; set; }
[TypeConverter(typeof(BooleanConverter))]
public bool IsAsync { get; set; }
public string XPath { get; set; }
[TypeConverter(typeof(CultureInfoIetfLanguageTagConverter))]
public CultureInfo ConverterCulture { get; set; }

Next step is to override ProvideValue method of MarkupExtension and create the actual binding. We still do not want to bind it to any property

public override sealed object ProvideValue(IServiceProvider serviceProvider) {
        var _valueProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
        if (_valueProvider != null) {
           var _bindingTarget = _valueProvider.TargetObject as FrameworkElement;
           var _bindingProperty = _valueProvider.TargetProperty as DependencyProperty;
           var _binding = new Binding();
           _binding.Path = Path;
           _binding.XPath = XPath;
           _binding.Mode = Mode;
           _binding.Converter = Converter;
           _binding.ConverterCulture = ConverterCulture;
           _binding.ConverterParameter = ConverterParameter;
           _binding.IsAsync = IsAsync;
           if (ElementName != null) _binding.ElementName = ElementName;
           if (RelativeSource != null) _binding.RelativeSource = RelativeSource;
           if (Source != null) _binding.Source = Source;
           _binding.ValidatesOnDataErrors = ValidatesOnDataErrors;
           _binding.ValidatesOnExceptions = ValidatesOnExceptions;
           _binding.StringFormat = StringFormat;

Now, if we have TriggerSource and Trigger, everything fine, we can set subscribe to the trigger event and wire binding in the handler

private RoutedEventHandler _bindigHandler;

private void _registerHandler(FrameworkElement bindingTarget, DependencyProperty bindingProperty, Binding binding) {
   if (_bindigHandler == default(RoutedEventHandler)) {
      _bindigHandler = (RoutedEventHandler)(delegate {
         var _oldValue = bindingTarget.GetValue(bindingProperty);
         bindingTarget.SetBinding(bindingProperty, binding);
         if (_oldValue != bindingProperty.DefaultMetadata.DefaultValue) {
            bindingTarget.SetValue(bindingProperty, _oldValue);
            BindingOperations.GetBindingExpression(bindingTarget, bindingProperty).UpdateSource();
         }
      });
      TriggerSource = TriggerSource ?? bindingTarget;
      if (Trigger != null && TriggerSource != null) {
         TriggerSource.AddHandler(Trigger, _bindigHandler);
      } else {
         _bindigHandler.Invoke(this, null);
      }
   }
}

However, we also want to be able to seek Logical tree and find trigger source by it’s name. Let’s say this way

<TextBox Name="one" Text="{l:TriggerBinding ElementName=two, Path=Text, Mode=TwoWay, TriggerSourceName=three, Trigger=Button.Click}"/>
<TextBox Name="two"/>
<Button Name="three" Content="Click to trigger binding"/>

What’s the problem? We also have FrameworkElement.FindName method and LogicalTreeHelper.FindLogicalNode. But wait, in our case, when TextBox “one” is initialized, we still have no Button “three”, so we have to wait until the root element of TextBox will be fully loaded and then try to find logical node. For this purpose, we have very useful extended method

public static DependencyObject GetRoot(this DependencyObject current) {
         var _root = current;
         do { _root = LogicalTreeHelper.GetParent(_root); if (_root != null) current = _root; } while (_root != null);
         return current;
      }

So, all we have to do is to subscribe to Loaded event of the root and then wire the trigger for set binding

if (!string.IsNullOrEmpty(TriggerSourceName)) {
   var _root = _bindingTarget.GetRoot() as FrameworkElement;
   _root.Loaded += delegate {
      TriggerSource = LogicalTreeHelper.FindLogicalNode(_root, TriggerSourceName) as UIElement;
      _registerHandler(_bindingTarget, _bindingProperty, _binding);
   };
} else { _registerHandler(_bindingTarget, _bindingProperty, _binding); }

We done. Have a good day and be good people.

BTW, it’s also possible to postpone binding by using timer. Paul Stovell has very good sample of how to perform it.

Source code for this article

Saturday, October 11, 2008

Issues, you reported were fixed

Hi, folks. And thank you for reporting issues with WpfPerf Performance Profiling tool. Now it was fixed, so, download and use new and fixed version.

Keep reporting, as you can see, you have the power to change!

image

Imaginary by Malcolm Dare

Sunday, October 05, 2008

Silverlight Bidi Controls Library RC0 and movement from Beta 2 to RC0

Finally, I got free minute to convert Silverlight BiDi controls from Silverlight beta 2 to RC0 (you can download SL rc0 tools for VS2008 here) and as usual some breaking changes (the full list is here)

  • Calendar and DatePicker moved from System.Windows.Controls.Extended into System.Windows.Controls – Extended namespace is now deprecated.
  • CalendarButton is not inside System.Windows.Controls.Primitives
  • TypeConverter.CanConvertFrom(Type sourceType) was changed and now it has new first parameter ITypeDescriptorContext context
  • TypeConverter.CanConvertFrom(object value) was changed and now it has new first parameter ITypeDescriptorContext context and second parameter System.Globalization.CultureInfo culture
  • TypeConverter.ConvertFromString is not virtual anymore
  • TextDecorationCollectionConverter was removed
  • generic.xaml should be placed into themes directory (as in WPF)
  • VisualTransition.Duration is not VisualTransition.GeneratedDuration
  • ContentPresenter has no HorizontalContentAlignment and VerticalContentAlignment. It has HorizontalAlignment and VerticalAlignment now. Also it has no Background, Padding,TextAlignment,TextDecorations and TextWrapping properties

Those, basically, all changes done in Silverlight RTL support library. So, you can download and use the latest version within Silverlight RC0 version

Have a nice day and be good people.

Saturday, October 04, 2008

Free Microsoft Expression series video training catalogs

MSDN download center just published video catalogs, where you can find sample chapters of Total Training’s video series for Microsoft Expression studio. Worth to download and see. Here the breakdown by products

Also, if you are “in” Office development, it makes sense to take a look in to Data Connectivity Components for Office 2007 System. It can be used by non-Microsoft Office applications to read data from Office system files.

Happy downloading!

Monday, September 29, 2008

For all those, who have problems with running WPF Performance Profiling tool – Microsoft cares

Three days ago, I announced the new release of WPF Performance Profiling Tool. A couple hours after this was announced, I got a number of comments from you, readers. It was about issues with running this tool. I checked the issue and forwarded it to development team from Microsoft. The problem was in bad parsing of comma and point characters in this tool, when using it on non-US locale. Dev team took care about it and hopefully they will provide a fix soon.

Thank you for reading my blog, reporting and your awareness of such issues. This is very important to me and I’m really appreciate your efforts to help us to develop WPF community.

I’ll update you as soon as the patch will be available.

Thank you and Shana Tova!

image

Saturday, September 27, 2008

Nokia Mail for Exchange 2.7 is out

New version of outstanding Exchange synchronization tool for Nokia phones is out. There are no big changes, except heart beat implementation of ActiveSync and fixes to contacts mapping bug is there. The main feature of this version is, that it can now run on any S60 phone (including 3.0, 3.1 and 3.2 devices), not only on E and N series. Full list of changes can be found here.

The bad news is, that Nokia MFE 2.7 still does not support inbox folders synchronization. This is the main feature missing in this great software. However, according the update rate, it should arrive soon.

Download Nokia Mail for Exchange 2.7 >>

Friday, September 26, 2008

The new version of WPF Performance Profiling Tool is available for download

Finally, after a long time of silence, the new version of WPF Performance Profiling Tool is available for download for x32 and x64 OSs.  So, what’s new there?

Ton of UI improvements for Visual Profiler

image

New search function to quick find elements in visual tree

image

Hot path (critical path) of CPU usage aside with CPU usage for single element

image

Configuration of tint for overlay windows

image

Live preview, ability to split columns, slider of graph duration, expanders to have cleaner screen and much much more

Perforator also got new UI and has history now.

image

There is new tool, named String allocation profiler

image

This tool is very useful for viewing and managing strings inside your application (another step toward normal localization support for WPF? Probably)

There are also some improvements in Event tracing tool. Select process for example :)

image

And much much more. Great thank to Josef and his team for this great work

Download the new version of WPF Performance Profiling Tool >>

Monday, September 22, 2008

Visual Studio snippet designer

Chicks love CodePlex as well as Microsoft loves it too and today they release extremely useful tool, that was internal for more, then three years. It named: “Visual Studio Snippet Designer”.

image

As you can, probably, understand. This tools is used to create and manage VS time savers - snippet files (introduced in VS2005)

image

This is great tool, that will help you a lot to save your time during regular everyday development. Any other word is unnecessary. Download, install and use it!

Monday, September 15, 2008

WGS to UTM, UTM to WGS conversions, geo distance, azimuth and other geographical calculations in C#

"The reports of my death are greatly exaggerated"

Since my post about leaving consulting field, I got huge amount of email with questions about all community projects, I'm leading, blogging in general and specific to the future of this blog.

To make things clear, I leaved consulting, and now, I have less time and reasons to blog, however, I'm keep blogging and maintaining almost all of my community projects (see the left side of the main page to list of most of those projects). Also, I try to answer all questions, I got via emails, however it's too much to handle, thus be prepared for delays.

To be sure, I'm alive, you can follow me at twitter (it demands less time to write) :) Anyway, that you for reading my blog and supporting me

Now it's good time to write something useful for you :)

If you ever wrote GIS programs, you, probably, know, that every time we forget how to convert latlon (Latitude-Longitude or, simpler World Geographical System) coordinates into Universal Transverse Mercator coordinates and vise verse, how to calculate geographical distance from point to point, line or segment to point and line to line, how to calculate azimuth between two geo points, or how to calculate destination point, based on start coordinate, distance and bearing.

image

In order to make our life easier, I decided to post number of methods in C#, I always use to perform such GIS calculations. I believe, that those classes will help you to perform your own geographical calculations. Also, do not forget two handy functions to convert degrees to radians and vv

public static double DegToRad(double deg) { return (deg / 180.0 * Math.PI); }
public static double RadToDeg(double rad) { return (rad / Math.PI * 180.0); }

Also, there are some constants should be used, if you're calculating geo information in the Earth

const double sm_a = 6378137.0;
const double sm_b = 6356752.314;
const double sm_EccSquared = 6.69437999013e-03;
const double UTMScaleFactor = 0.9996;

Conversion WGS2UTM (LatLon2UTM)

Fist of all, we should calculate UTM zone. This one is simple

int zone = (int)(Math.Floor((latlon.Longitude + 180.0) / 6) + 1);

Now, when we have zone, we should calculate UTM central meridian, footprint of latitude and arc length of the meridian

public static double UTMCentralMeridian(int zone) { return DegToRad(-183.0 + (zone * 6.0)); }

public static double FootpointLatitude(double y) {
         /* Precalculate n (Eq. 10.18) */
         var n = (sm_a - sm_b) / (sm_a + sm_b);

         /* Precalculate alpha_ (Eq. 10.22) */
         /* (Same as alpha in Eq. 10.17) */
         var alpha_ = ((sm_a + sm_b) / 2.0) * (1 + (Math.Pow(n, 2.0) / 4) + (Math.Pow(n, 4.0) / 64));

         /* Precalculate y_ (Eq. 10.23) */
         var y_ = y / alpha_;

         /* Precalculate beta_ (Eq. 10.22) */
         var beta_ = (3.0 * n / 2.0) + (-27.0 * Math.Pow(n, 3.0) / 32.0) + (269.0 * Math.Pow(n, 5.0) / 512.0);

         /* Precalculate gamma_ (Eq. 10.22) */
         var gamma_ = (21.0 * Math.Pow(n, 2.0) / 16.0) + (-55.0 * Math.Pow(n, 4.0) / 32.0);

         /* Precalculate delta_ (Eq. 10.22) */
         var delta_ = (151.0 * Math.Pow(n, 3.0) / 96.0) + (-417.0 * Math.Pow(n, 5.0) / 128.0);

         /* Precalculate epsilon_ (Eq. 10.22) */
         var epsilon_ = (1097.0 * Math.Pow(n, 4.0) / 512.0);

         /* Now calculate the sum of the series (Eq. 10.21) */
         return y_ + (beta_ * Math.Sin(2.0 * y_)) + (gamma_ * Math.Sin(4.0 * y_)) + (delta_ * Math.Sin(6.0 * y_)) + (epsilon_ * Math.Sin(8.0 * y_));
      }

public static double ArcLengthOfMeridian(double phi) {
         /* Precalculate n */
         var n = (sm_a - sm_b) / (sm_a + sm_b);

         /* Precalculate alpha */
         var alpha = ((sm_a + sm_b) / 2.0) * (1.0 + (Math.Pow(n, 2.0) / 4.0) + (Math.Pow(n, 4.0) / 64.0));

         /* Precalculate beta */
         var beta = (-3.0 * n / 2.0) + (9.0 * Math.Pow(n, 3.0) / 16.0) + (-3.0 * Math.Pow(n, 5.0) / 32.0);

         /* Precalculate gamma */
         var gamma = (15.0 * Math.Pow(n, 2.0) / 16.0) + (-15.0 * Math.Pow(n, 4.0) / 32.0);

         /* Precalculate delta */
         var delta = (-35.0 * Math.Pow(n, 3.0) / 48.0) + (105.0 * Math.Pow(n, 5.0) / 256.0);

         /* Precalculate epsilon */
         var epsilon = (315.0 * Math.Pow(n, 4.0) / 512.0);

         /* Now calculate the sum of the series and return */
         return alpha * (phi + (beta * Math.Sin(2.0 * phi)) + (gamma * Math.Sin(4.0 * phi)) + (delta * Math.Sin(6.0 * phi)) + (epsilon * Math.Sin(8.0 * phi)));
      }

Now, we have everything to calculate UTM

public static GeoPoint MapLatLonToXY(double phi, double lambda, double lambda0) {
         /* Precalculate ep2 */
         var ep2 = (Math.Pow(sm_a, 2.0) - Math.Pow(sm_b, 2.0)) / Math.Pow(sm_b, 2.0);

         /* Precalculate nu2 */
         var nu2 = ep2 * Math.Pow(Math.Cos(phi), 2.0);

         /* Precalculate N */
         var N = Math.Pow(sm_a, 2.0) / (sm_b * Math.Sqrt(1 + nu2));

         /* Precalculate t */
         var t = Math.Tan(phi);
         var t2 = t * t;
         var tmp = (t2 * t2 * t2) - Math.Pow(t, 6.0);

         /* Precalculate l */
         var l = lambda - lambda0;

         /* Precalculate coefficients for l**n in the equations below
            so a normal human being can read the expressions for easting
            and northing
            -- l**1 and l**2 have coefficients of 1.0 */
         var l3coef = 1.0 - t2 + nu2;

         var l4coef = 5.0 - t2 + 9 * nu2 + 4.0 * (nu2 * nu2);

         var l5coef = 5.0 - 18.0 * t2 + (t2 * t2) + 14.0 * nu2 - 58.0 * t2 * nu2;

         var l6coef = 61.0 - 58.0 * t2 + (t2 * t2) + 270.0 * nu2 - 330.0 * t2 * nu2;

         var l7coef = 61.0 - 479.0 * t2 + 179.0 * (t2 * t2) - (t2 * t2 * t2);

         var l8coef = 1385.0 - 3111.0 * t2 + 543.0 * (t2 * t2) - (t2 * t2 * t2);

         var xy = new GeoPoint();
         /* Calculate easting (x) */
         xy.X = N * Math.Cos(phi) * l + (N / 6.0 * Math.Pow(Math.Cos(phi), 3.0) * l3coef * Math.Pow(l, 3.0)) + (N / 120.0 * Math.Pow(Math.Cos(phi), 5.0) * l5coef * Math.Pow(l, 5.0)) + (N / 5040.0 * Math.Pow(Math.Cos(phi), 7.0) * l7coef * Math.Pow(l, 7.0));

         /* Calculate northing (y) */
         xy.Y = ArcLengthOfMeridian(phi) + (t / 2.0 * N * Math.Pow(Math.Cos(phi), 2.0) * Math.Pow(l, 2.0)) + (t / 24.0 * N * Math.Pow(Math.Cos(phi), 4.0) * l4coef * Math.Pow(l, 4.0)) + (t / 720.0 * N * Math.Pow(Math.Cos(phi), 6.0) * l6coef * Math.Pow(l, 6.0)) + (t / 40320.0 * N * Math.Pow(Math.Cos(phi), 8.0) * l8coef * Math.Pow(l, 8.0));

         return xy;
      }

public static GeoCoord MapXYToLatLon(double x, double y, double lambda0) {
   /* Get the value of phif, the footpoint latitude. */
   double phif = FootpointLatitude(y);

   /* Precalculate ep2 */
   double ep2 = (Math.Pow(sm_a, 2.0) - Math.Pow(sm_b, 2.0)) / Math.Pow(sm_b, 2.0);

   /* Precalculate cos (phif) */
   var cf = Math.Cos(phif);

   /* Precalculate nuf2 */
   var nuf2 = ep2 * Math.Pow(cf, 2.0);

   /* Precalculate Nf and initialize Nfpow */
   var Nf = Math.Pow(sm_a, 2.0) / (sm_b * Math.Sqrt(1 + nuf2));
   var Nfpow = Nf;

   /* Precalculate tf */
   var tf = Math.Tan(phif);
   var tf2 = tf * tf;
   var tf4 = tf2 * tf2;

   /* Precalculate fractional coefficients for x**n in the equations
      below to simplify the expressions for latitude and longitude. */
   var x1frac = 1.0 / (Nfpow * cf);

   Nfpow *= Nf;   /* now equals Nf**2) */
   var x2frac = tf / (2.0 * Nfpow);

   Nfpow *= Nf;   /* now equals Nf**3) */
   var x3frac = 1.0 / (6.0 * Nfpow * cf);

   Nfpow *= Nf;   /* now equals Nf**4) */
   var x4frac = tf / (24.0 * Nfpow);

   Nfpow *= Nf;   /* now equals Nf**5) */
   var x5frac = 1.0 / (120.0 * Nfpow * cf);

   Nfpow *= Nf;   /* now equals Nf**6) */
   var x6frac = tf / (720.0 * Nfpow);

   Nfpow *= Nf;   /* now equals Nf**7) */
   var x7frac = 1.0 / (5040.0 * Nfpow * cf);

   Nfpow *= Nf;   /* now equals Nf**8) */
   var x8frac = tf / (40320.0 * Nfpow);

   /* Precalculate polynomial coefficients for x**n.
      -- x**1 does not have a polynomial coefficient. */
   var x2poly = -1.0 - nuf2;

   var x3poly = -1.0 - 2 * tf2 - nuf2;

   var x4poly = 5.0 + 3.0 * tf2 + 6.0 * nuf2 - 6.0 * tf2 * nuf2 - 3.0 * (nuf2 * nuf2) - 9.0 * tf2 * (nuf2 * nuf2);

   var x5poly = 5.0 + 28.0 * tf2 + 24.0 * tf4 + 6.0 * nuf2 + 8.0 * tf2 * nuf2;

   var x6poly = -61.0 - 90.0 * tf2 - 45.0 * tf4 - 107.0 * nuf2 + 162.0 * tf2 * nuf2;

   var x7poly = -61.0 - 662.0 * tf2 - 1320.0 * tf4 - 720.0 * (tf4 * tf2);

   var x8poly = 1385.0 + 3633.0 * tf2 + 4095.0 * tf4 + 1575 * (tf4 * tf2);

   var philambda = new GeoCoord();
   /* Calculate latitude */
   philambda.Latitude = phif + x2frac * x2poly * (x * x) + x4frac * x4poly * Math.Pow(x, 4.0) + x6frac * x6poly * Math.Pow(x, 6.0) + x8frac * x8poly * Math.Pow(x, 8.0);

   /* Calculate longitude */
   philambda.Longitude = lambda0 + x1frac * x + x3frac * x3poly * Math.Pow(x, 3.0) + x5frac * x5poly * Math.Pow(x, 5.0) + x7frac * x7poly * Math.Pow(x, 7.0);

   return philambda;
}

We done, the only thing, should be adjusted is easting and northing for UTM system

xy.X = xy.X * UTMScaleFactor + 500000.0;
xy.Y = xy.Y * UTMScaleFactor;
if (xy.Y < 0.0) xy.Y += 10000000.0;

Conversion UTM2WGS (UTM2LatLon)

After al had all thin math in previous chapter, now we should calculate opposite conversion

First adjust

x -= 500000.0;
x /= UTMScaleFactor;

/* If in southern hemisphere, adjust y accordingly. */
if (southhemi) y -= 10000000.0;

y /= UTMScaleFactor;

var cmeridian = UTMCentralMeridian(zone);

Now calculate

public static GeoCoord MapXYToLatLon(double x, double y, double lambda0) {
         /* Get the value of phif, the footpoint latitude. */
         double phif = FootpointLatitude(y);

         /* Precalculate ep2 */
         double ep2 = (Math.Pow(sm_a, 2.0) - Math.Pow(sm_b, 2.0)) / Math.Pow(sm_b, 2.0);

         /* Precalculate cos (phif) */
         var cf = Math.Cos(phif);

         /* Precalculate nuf2 */
         var nuf2 = ep2 * Math.Pow(cf, 2.0);

         /* Precalculate Nf and initialize Nfpow */
         var Nf = Math.Pow(sm_a, 2.0) / (sm_b * Math.Sqrt(1 + nuf2));
         var Nfpow = Nf;

         /* Precalculate tf */
         var tf = Math.Tan(phif);
         var tf2 = tf * tf;
         var tf4 = tf2 * tf2;

         /* Precalculate fractional coefficients for x**n in the equations
            below to simplify the expressions for latitude and longitude. */
         var x1frac = 1.0 / (Nfpow * cf);

         Nfpow *= Nf;   /* now equals Nf**2) */
         var x2frac = tf / (2.0 * Nfpow);

         Nfpow *= Nf;   /* now equals Nf**3) */
         var x3frac = 1.0 / (6.0 * Nfpow * cf);

         Nfpow *= Nf;   /* now equals Nf**4) */
         var x4frac = tf / (24.0 * Nfpow);

         Nfpow *= Nf;   /* now equals Nf**5) */
         var x5frac = 1.0 / (120.0 * Nfpow * cf);

         Nfpow *= Nf;   /* now equals Nf**6) */
         var x6frac = tf / (720.0 * Nfpow);

         Nfpow *= Nf;   /* now equals Nf**7) */
         var x7frac = 1.0 / (5040.0 * Nfpow * cf);

         Nfpow *= Nf;   /* now equals Nf**8) */
         var x8frac = tf / (40320.0 * Nfpow);

         /* Precalculate polynomial coefficients for x**n.
            -- x**1 does not have a polynomial coefficient. */
         var x2poly = -1.0 - nuf2;

         var x3poly = -1.0 - 2 * tf2 - nuf2;

         var x4poly = 5.0 + 3.0 * tf2 + 6.0 * nuf2 - 6.0 * tf2 * nuf2 - 3.0 * (nuf2 * nuf2) - 9.0 * tf2 * (nuf2 * nuf2);

         var x5poly = 5.0 + 28.0 * tf2 + 24.0 * tf4 + 6.0 * nuf2 + 8.0 * tf2 * nuf2;

         var x6poly = -61.0 - 90.0 * tf2 - 45.0 * tf4 - 107.0 * nuf2 + 162.0 * tf2 * nuf2;

         var x7poly = -61.0 - 662.0 * tf2 - 1320.0 * tf4 - 720.0 * (tf4 * tf2);

         var x8poly = 1385.0 + 3633.0 * tf2 + 4095.0 * tf4 + 1575 * (tf4 * tf2);

         var philambda = new GeoCoord();
         /* Calculate latitude */
         philambda.Latitude = phif + x2frac * x2poly * (x * x) + x4frac * x4poly * Math.Pow(x, 4.0) + x6frac * x6poly * Math.Pow(x, 6.0) + x8frac * x8poly * Math.Pow(x, 8.0);

         /* Calculate longitude */
         philambda.Longitude = lambda0 + x1frac * x + x3frac * x3poly * Math.Pow(x, 3.0) + x5frac * x5poly * Math.Pow(x, 5.0) + x7frac * x7poly * Math.Pow(x, 7.0);

         return philambda;
      }

At the end do not forget to return Latitude and Longitude in degrees, rather, then in radians

Azimuth calculation

We done with coordinates, now azimuth

public static double GetAzimuth(WGSCoord c1, WGSCoord c2) {
         var lat1 = DegToRad(c1.Latitude);
         var lon1 = DegToRad(c1.Longitude);
         var lat2 = DegToRad(c2.Latitude);
         var lon2 = DegToRad(c2.Longitude);

         return RadToDeg(Math.Asin(Math.Sin(lon1 - lon2) * Math.Cos(lat2) / Math.Sin(Math.Acos(Math.Sin(lat2) * Math.Sin(lat1) + Math.Cos(lat1) * Math.Cos(lat2) * Math.Cos(lon2 - lon1)))));
      }

Distance calculations

public static double GetDistance(WGSCoord c1, WGSCoord c2) {
         var dLat = DegToRad(c2.Latitude - c1.Latitude);
         var dLon = DegToRad(c2.Longitude - c2.Longitude);
         var a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(DegToRad(c1.Latitude)) * Math.Cos(DegToRad(c2.Latitude)) * Math.Sin(dLon / 2) * Math.Sin(dLon / 2);
         var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
         return sm_a * c;
      }

Final destination coordinates, based on start coordinate, bearing and distance

public static WGSCoord GetDestination(WGSCoord start, double distance, double azimuth) {
         var lat = RadToDeg(Math.Asin(Math.Sin(DegToRad(start.Latitude)) * Math.Cos(DegToRad(distance / sm_a)) + Math.Cos(DegToRad(start.Latitude)) * Math.Sin(DegToRad(distance / sm_a)) * Math.Cos(DegToRad(azimuth))));
         var lon = start.Longitude + DegToRad(Math.Atan2(Math.Sin(DegToRad(azimuth)) * Math.Sin(DegToRad(distance / sm_a)) * Math.Cos(DegToRad(start.Latitude)), Math.Cos(DegToRad(distance / sm_a)) - Math.Sin(DegToRad(start.Latitude)) * Math.Sin(DegToRad(lat))));
         return new WGSCoord(lon,lat);

      }

Midpoint calculation

public static WGSCoord GetMidpoint(WGSCoord start, WGSCoord end) {
         var dLat = DegToRad(end.Latitude - start.Latitude);
         var dLon = DegToRad(end.Longitude - start.Longitude);
         var lat2 = DegToRad(end.Latitude);
         var lat1 = DegToRad(start.Latitude);
         var lon1 = DegToRad(start.Longitude);

         var Bx = Math.Cos(lat2) * Math.Cos(dLon);
         var By = Math.Cos(lat2) * Math.Sin(dLon);
         var lat3 = Math.Atan2(Math.Sin(lat1) + Math.Sin(lat2), Math.Sqrt((Math.Cos(lat1) + Bx) * (Math.Cos(lat1) + Bx) + By * By));
         var lon3 = lon1 + Math.Atan2(By, Math.Cos(lat1) + Bx);
         return new WGSCoord(RadToDeg(lon3), RadToDeg(lat3));
      }

Rhumb lines calculation

First of all, what is rhumb lines? Rhumb lines or loxodrome is a path of constant bearing, which crosses all meridians at the same angle. This calculation is very useful, if you want to follow constant compass bearing, instead of continually adjustment of it.

public static double GetRhumbDistance(WGSCoord start, WGSCoord end) {
   var dLat = DegToRad(end.Latitude - start.Latitude);
   var dLon = DegToRad(end.Longitude - start.Longitude);
   var lat1 = DegToRad(start.Latitude);
   var lon1 = DegToRad(start.Longitude);
   var lat2 = DegToRad(end.Latitude);
   var lon2 = DegToRad(end.Longitude);

   var dPhi = Math.Log(Math.Tan(lat2 / 2 + Math.PI / 4) / Math.Tan(lat1 / 2 + Math.PI / 4));
   var q = (Math.Abs(dLat) > 1e-10) ? dLat / dPhi : Math.Cos(lat1);
   if (Math.Abs(dLon) > Math.PI) {
      dLon = dLon > 0 ? -(2 * Math.PI - dLon) : (2 * Math.PI + dLon);
   }
   return Math.Sqrt(dLat * dLat + q * q * dLon * dLon) * sm_a;
}

public static double GetRhumbBearing(WGSCoord start, WGSCoord end) {
   var dLat = DegToRad(end.Latitude - start.Latitude);
   var dLon = DegToRad(end.Longitude - start.Longitude);

   var lat1 = DegToRad(start.Latitude);
   var lon1 = DegToRad(start.Longitude);
   var lat2 = DegToRad(end.Latitude);
   var lon2 = DegToRad(end.Longitude);

   var dPhi = Math.Log(Math.Tan(lat2 / 2 + Math.PI / 4) / Math.Tan(lat1 / 2 + Math.PI / 4));
   if (Math.Abs(dLon) > Math.PI) {
      dLon = dLon > 0 ? -(2 * Math.PI - dLon) : (2 * Math.PI + dLon);
   }
   return Math.Atan2(dLon, dPhi);
}

public static WGSCoord GetRhumbDestination(WGSCoord start, WGSCoord end) {
         var lat1 = DegToRad(start.Latitude);
         var lon1 = DegToRad(start.Longitude);
         var lat2 = DegToRad(end.Latitude);
         var lon2 = DegToRad(end.Longitude);

         var d = GetRhumbDistance(start, end);
         var b = GetRhumbBearing(start, end);
         lat2 = lat1 +  d * Math.Cos(b);
         var dPhi = Math.Log(Math.Tan(lat2 / 2 + Math.PI / 4) / Math.Tan(lat1 / 2 + Math.PI / 4));
         var q = (Math.Abs(lat2 - lat1) > 1e-10) ? (lat2 - lat1) / dPhi : Math.Cos(lat1);
         var dLon = d * Math.Sin(b) / q;
         if (Math.Abs(lat2) > Math.PI / 2) lat2 = lat2 > 0 ? Math.PI - lat2 : -Math.PI - lat2;
         lon2 = (lon1 + dLon + Math.PI) % (2 * Math.PI) - Math.PI;

         return new WGSCoord(RadToDeg(lon2), RadToDeg(lat2));
      }

Conversion between decimal degrees to degrees-minutes-seconds

public static Pair<double,double> ToD_Mm(this double decCoord) {
         var degSeg = (int)decCoord;
         return new Pair<double, double>(degSeg, decCoord % degSeg * 60);
      }

Now it's good time for small math (if there were not enough until now) :) All following methods are not geo spatial oriented. All those are only geometry. The only helper method, you need here is Dot vector operation

private static double dot(Vector v1, Vector v2) { return (v1.X * v2.X + v1.Y * v2.Y); }

Get the distance between two points

private static double distance(Point p1, Point p2) {
         var a = Math.Pow(p1.X, 2) - Math.Pow(p2.X, 2);
         var b = Math.Pow(p1.Y, 2) - Math.Pow(p2.Y, 2);
         return Math.Sqrt(Math.Abs(a + b));
      }

Get the shortest distance between point and line segment

private static double distance(Point p1, Point p2, Point p) {
         var v = p1 - p2;
         var w = p - p2;

         var c1 = dot(w, v);
         if (c1 <= 0) return distance(p, p1);

         var c2 = dot(v, v);
         if (c2 <= c1) return distance(p, p2);

         var b = c1 / c2;
         Point Pb = p1 + b * v;
         return distance(p, Pb);
      }

Isn't it enough math for now? It is. Have a nice day and be good people. Next time, we'll speak about different path finding algorithms, edges, nodes and other fun math optimizations.