<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.microsoft.co.il/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Just code - Tamir Khason : Math, .NET 3.5, Work process</title><link>http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Math/.NET+3.5/Work+process/default.aspx</link><description>Tags: Math, .NET 3.5, Work process</description><dc:language>en</dc:language><generator>CommunityServer 2007.1 (Build: 20917.1142)</generator><item><title>New year – new blog or how to migrate Community Server to any other engine, supports XML-RPC</title><link>http://blogs.microsoft.co.il/blogs/tamir/archive/2009/01/01/new-year-new-blog-or-how-to-migrate-community-server-to-any-other-engine-supports-xml-rpc.aspx</link><pubDate>Fri, 02 Jan 2009 02:33:04 GMT</pubDate><guid isPermaLink="false">b5c4f5bc-c09b-4439-a595-91a98c1847df:205248</guid><dc:creator>Tamir Khason</dc:creator><slash:comments>0</slash:comments><description>&lt;p&gt;Please update your bookmarks, because the new url of this blog is &lt;a href="http://khason.net/"&gt;http://khason.net/&lt;/a&gt; (you have not update RSS feeds, it will be done automatically). Why I did it? Why I decided to go to “stand-alone”… Well. there are some reasons. Generally, I do not want to explain all those here, but trust me, there are some. The main reason is, that there is no responsible person in charge for this blog platform in Microsoft Israel. This why, if your blog is popular and you have a respect to your blog visitors, you cannot host it here… Take a look into new comments notifications in my inbox. Would you answer your readers with such “small amount” of SPAM and capcha, that cannot be fixed already for three years in this platform? This how my inbox looks like for last three years. So now, you should not ask me, why I not answered your email or comments. Aren’t you? :)&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="Capture" border="0" alt="Capture" src="http://blogs.microsoft.co.il/blogs/tamir/Capture_1D7350A7.jpg" width="354" height="397" /&gt; &lt;/p&gt;  &lt;p&gt;Currently, all comments in this blog are disabled, so if you want to comment, please use new url of posts (this will appear shortly in the beginning of each post). Also, this post will not be syndicated in RSS.&lt;/p&gt;  &lt;p&gt;So, this post is the last. I loved this platform, and loved people started it. But, unfortunately, it seemed, that bloggers community is not important enough for new platform managers. &lt;/p&gt;  &lt;p&gt;Also, If you want to learn &lt;a target="_blank" href="http://khason.net/dev/how-to-migrate-from-cs2007-to-wordpress-movable-type-or-any-other-blog-engine-supports-xml-rpc-with-c/"&gt;how to use C# and XML-RCP to migrate from CS2007, used in this platform, visit my new home&lt;/a&gt; :)&lt;/p&gt;  &lt;p&gt;Have a great year and, as always, be good people. This post marked with all possible tags automatically. Sorry.&lt;/p&gt;&lt;img src="http://blogs.microsoft.co.il/aggbug.aspx?PostID=205248" width="1" height="1"&gt;</description><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/soft/default.aspx">soft</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/blogging+tools/default.aspx">blogging tools</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/WPF/default.aspx">WPF</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/tutorial/default.aspx">tutorial</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Tips+and+Tricks/default.aspx">Tips and Tricks</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Performance/default.aspx">Performance</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/WPF+crossbow/default.aspx">WPF crossbow</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/source/default.aspx">source</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Mobile/default.aspx">Mobile</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/help/default.aspx">help</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Windows+Gadgets/default.aspx">Windows Gadgets</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/promo/default.aspx">promo</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Vista/default.aspx">Vista</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/thoughts/default.aspx">thoughts</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/tools/default.aspx">tools</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/events/default.aspx">events</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/demos/default.aspx">demos</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/WPF_2F00_E/default.aspx">WPF/E</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/download/default.aspx">download</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/jobs/default.aspx">jobs</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/VSTS/default.aspx">VSTS</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Visual+Studio/default.aspx">Visual Studio</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Web/default.aspx">Web</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/C_2300_/default.aspx">C#</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Silverlight/default.aspx">Silverlight</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/WCF/default.aspx">WCF</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Accessibility/default.aspx">Accessibility</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Blogging+rules/default.aspx">Blogging rules</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/blogging+general/default.aspx">blogging general</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/fun/default.aspx">fun</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Vista+Battery+Saver/default.aspx">Vista Battery Saver</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/SkyDrive/default.aspx">SkyDrive</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Windows+Live+Writer/default.aspx">Windows Live Writer</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Windows+Live/default.aspx">Windows Live</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Dell/default.aspx">Dell</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/DevAcademy2/default.aspx">DevAcademy2</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Microsoft/default.aspx">Microsoft</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/.NET+3.5/default.aspx">.NET 3.5</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/WPF+quiz/default.aspx">WPF quiz</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/x64/default.aspx">x64</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/WF/default.aspx">WF</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/SVG/default.aspx">SVG</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/XPS/default.aspx">XPS</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/TechedIsrael2008/default.aspx">TechedIsrael2008</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/teched/default.aspx">teched</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/XNA/default.aspx">XNA</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/PLINQ/default.aspx">PLINQ</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/LINQ/default.aspx">LINQ</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Micro+Framework/default.aspx">Micro Framework</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/SAP/default.aspx">SAP</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/XLINQ/default.aspx">XLINQ</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/DirectX/default.aspx">DirectX</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Work+process/default.aspx">Work process</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Linux/default.aspx">Linux</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Mono/default.aspx">Mono</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/DevAcademy3/default.aspx">DevAcademy3</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Math/default.aspx">Math</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/GIS/default.aspx">GIS</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Algorithms/default.aspx">Algorithms</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/DEV/default.aspx">DEV</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/TECH/default.aspx">TECH</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Windows+7/default.aspx">Windows 7</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Charity/default.aspx">Charity</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/OFFTOPIC/default.aspx">OFFTOPIC</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Hardware/default.aspx">Hardware</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Interop/default.aspx">Interop</category></item><item><title>Reading and decoding RDS (Radio Data System) in C#</title><link>http://blogs.microsoft.co.il/blogs/tamir/archive/2008/12/12/reading-and-decoding-rds-radio-data-system-in-c.aspx</link><pubDate>Fri, 12 Dec 2008 18:44:24 GMT</pubDate><guid isPermaLink="false">b5c4f5bc-c09b-4439-a595-91a98c1847df:191019</guid><dc:creator>Tamir Khason</dc:creator><slash:comments>4</slash:comments><description>&lt;h3&gt;[This blog was migrated. You will not be able to comment here.&lt;br /&gt;The new URL of this post is &lt;a href="http://khason.net/blog/reading-and-decoding-rds-radio-data-system-in-c/"&gt;http://khason.net/blog/reading-and-decoding-rds-radio-data-system-in-c/&lt;/a&gt;]&lt;/h3&gt;&lt;hr /&gt;&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Radio_Data_System"&gt;RDS or Radio Data System&lt;/a&gt; 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 &amp;quot;tell&amp;quot; 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. &lt;/p&gt; &lt;p&gt;&lt;img border="0" alt="image" src="http://blogs.microsoft.co.il/blogs/tamir/WindowsLiveWriter/ReadinganddecodingRDSRadioDataSysteminC_1238A/image_01bbfbdd-e89d-4a4e-89cd-e209655b5664.png" width="207" height="54" /&gt; &lt;/p&gt; &lt;h3&gt;How RDS works?&lt;/h3&gt; &lt;p&gt;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 &amp;quot;group&amp;quot;. 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 &lt;a&gt;RFC&lt;/a&gt;:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;private enum groupType : byte {&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_0A = (0 * 2 + 0),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_0B = (0 * 2 + 1),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_1A = (1 * 2 + 0),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_1B = (1 * 2 + 1),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_2A = (2 * 2 + 0),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_2B = (2 * 2 + 1),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_3A = (3 * 2 + 0),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_3B = (3 * 2 + 1),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_4A = (4 * 2 + 0),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_4B = (4 * 2 + 1),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_5A = (5 * 2 + 0),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_5B = (5 * 2 + 1),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_6A = (6 * 2 + 0),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_6B = (6 * 2 + 1),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_7A = (7 * 2 + 0),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_7B = (7 * 2 + 1),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_8A = (8 * 2 + 0),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_8B = (8 * 2 + 1),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_9A = (9 * 2 + 0),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_9B = (9 * 2 + 1),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_10A = (10 * 2 + 0),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_10B = (10 * 2 + 1),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_11A = (11 * 2 + 0),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_11B = (11 * 2 + 1),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_12A = (12 * 2 + 0),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_12B = (12 * 2 + 1),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_13A = (13 * 2 + 0),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_13B = (13 * 2 + 1),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_14A = (14 * 2 + 0),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_14B = (14 * 2 + 1),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_15A = (15 * 2 + 0),&lt;br /&gt;&amp;nbsp;&amp;nbsp; RDS_TYPE_15B = (15 * 2 + 1)&lt;br /&gt;}&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;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. &lt;/p&gt; &lt;p&gt;Within the error correction information we also receive the direction to treat them.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;private enum correctedType : byte {&lt;br /&gt;&amp;nbsp;&amp;nbsp; NONE = 0,&lt;br /&gt;&amp;nbsp;&amp;nbsp; ONE_TO_TWO = 1,&lt;br /&gt;&amp;nbsp;&amp;nbsp; THREE_TO_FIVE = 2,&lt;br /&gt;&amp;nbsp;&amp;nbsp; UNCORRECTABLE = 3&lt;br /&gt;}&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;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.&lt;/p&gt; &lt;p&gt;In addition to those constraints, block types are also different. But in this case, there are only 4 kinds&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;private enum blockType : byte {&lt;br /&gt;&amp;nbsp;&amp;nbsp; A = 6,&lt;br /&gt;&amp;nbsp;&amp;nbsp; B = 4,&lt;br /&gt;&amp;nbsp;&amp;nbsp; C = 2,&lt;br /&gt;&amp;nbsp;&amp;nbsp; D = 0&lt;br /&gt;}&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;So, what we&amp;#39;re waiting for? Let&amp;#39;s start working.&lt;/p&gt; &lt;h3&gt;Handling errors&lt;/h3&gt; &lt;p&gt;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&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;var errorCount = (byte)((registers[0xa] &amp;amp; 0x0E00) &amp;gt;&amp;gt; 9);&lt;br /&gt;var errorFlags = (byte)(registers[0x6] &amp;amp; 0xFF);&lt;br /&gt;if (errorCount &amp;lt; 4) {&lt;br /&gt;&amp;nbsp;&amp;nbsp; _blocksValid += (byte)(4 - errorCount);&lt;br /&gt;} else { /*drop data on more errors*/ return; } &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Once it done, we can try to fix them&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;//Also drop the data if more than two errors were corrected&lt;br /&gt;if (_getErrorsCorrected(errorFlags, blockType.B) &amp;gt; correctedType.ONE_TO_TWO) return;&lt;/p&gt; &lt;p&gt;private correctedType _getErrorsCorrected(byte data, blockType block) { return (correctedType)((data &amp;gt;&amp;gt; (byte)block) &amp;amp; 0x30); }&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now, our registers should be fine and we can start the detection of group type&lt;/p&gt; &lt;h3&gt;Group Type Detection&lt;/h3&gt; &lt;p&gt;This is very simple task, all we have to do is to get five upper bites to get a type and version.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;var group_type = (groupType)(registers[0xD] &amp;gt;&amp;gt; 11);&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Then we can handle PI and PTY, which we always have in RDS.&lt;/p&gt; &lt;h3&gt;PI and PTY treatment&lt;/h3&gt; &lt;p&gt;Now, let&amp;#39;s update pi code, due to the fact, that B format always have PI in words A and C&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;_updatePI(registers[0xC]);  &lt;p&gt;if (((byte)group_type &amp;amp; 0x01) != 0) {&lt;br /&gt; _updatePI(registers[0xE]);&lt;br /&gt;}&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;To update PI, we should check whether the new value is different from the previous and update it only in case it changed. &lt;blockquote&gt; &lt;p&gt;private void _updatePI(byte pi) {&lt;br /&gt;&amp;nbsp;&amp;nbsp; uint rds_pi_validate_count = 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp; uint rds_pi_nonvalidated = 0;  &lt;p&gt;&amp;nbsp;&amp;nbsp; // if the pi value is the same for a certain number of times, update a validated pi variable&lt;br /&gt;&amp;nbsp;&amp;nbsp; if (rds_pi_nonvalidated != pi) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rds_pi_nonvalidated = pi;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rds_pi_validate_count = 1;&lt;br /&gt;&amp;nbsp;&amp;nbsp; } else {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rds_pi_validate_count++;&lt;br /&gt;&amp;nbsp;&amp;nbsp; }  &lt;p&gt;&amp;nbsp;&amp;nbsp; if (rds_pi_validate_count &amp;gt; PI_VALIDATE_LIMIT) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _piDisplay = rds_pi_nonvalidated;&lt;br /&gt;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Then we will update PTY &lt;blockquote&gt; &lt;p&gt;_updatePTY((byte)((registers[0xd] &amp;gt;&amp;gt; 5) &amp;amp; 0x1f));&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;PTY treatment is very similar to PI, however it can be multiplied.&amp;nbsp; &lt;blockquote&gt; &lt;p&gt;private void _updatePTY(byte pty) {&lt;br /&gt;&amp;nbsp;&amp;nbsp; uint rds_pty_validate_count = 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp; uint rds_pty_nonvalidated = 0;  &lt;p&gt;&amp;nbsp;&amp;nbsp; // if the pty value is the same for a certain number of times, update a validated pty variable&lt;br /&gt;&amp;nbsp;&amp;nbsp; if (rds_pty_nonvalidated != pty) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rds_pty_nonvalidated = pty;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rds_pty_validate_count = 1;&lt;br /&gt;&amp;nbsp;&amp;nbsp; } else {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rds_pty_validate_count++;&lt;br /&gt;&amp;nbsp;&amp;nbsp; }  &lt;p&gt;&amp;nbsp;&amp;nbsp; if (rds_pty_validate_count &amp;gt; PTY_VALIDATE_LIMIT) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _ptyDisplay = rds_pty_nonvalidated;&lt;br /&gt;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;When we done with those two groups, we can start handling another. Today, we&amp;#39;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,  &lt;h3&gt;Handling PS and different RTs&lt;/h3&gt; &lt;p&gt;Simple switch on those groups&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;switch (group_type) {&lt;br /&gt;&amp;nbsp;&amp;nbsp; case groupType.RDS_TYPE_0B:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; addr = (byte)((registers[0xd] &amp;amp; 0x3) * 2);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _updatePS((byte)(addr + 0), (byte)(registers[0xf] &amp;gt;&amp;gt; 8));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _updatePS((byte)(addr + 1), (byte)(registers[0xf] &amp;amp; 0xff));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break;&lt;br /&gt;&amp;nbsp;&amp;nbsp; case groupType.RDS_TYPE_2A:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; addr = (byte)((registers[0xd] &amp;amp; 0xf) * 4);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; abflag = (byte)((registers[0xb] &amp;amp; 0x0010) &amp;gt;&amp;gt; 4);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _updateRT(abflag, 4, addr, (byte[])registers.Skip(0xe), errorFlags);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break;&lt;br /&gt;&amp;nbsp;&amp;nbsp; case groupType.RDS_TYPE_2B:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; addr = (byte)((registers[0xd] &amp;amp; 0xf) * 2);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; abflag = (byte)((registers[0xb] &amp;amp; 0x0010) &amp;gt;&amp;gt; 4);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // The last 32 bytes are unused in this format&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtTmp0[32] = 0x0d;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtTmp1[32] = 0x0d;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtCnt[32] = RT_VALIDATE_LIMIT;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _updateRT(abflag, 2, addr, (byte[])registers.Skip(0xe), errorFlags);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break;&lt;br /&gt;}&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;and let&amp;#39;s dig into PS.&lt;/p&gt; &lt;p&gt;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&amp;#39;ll push it into the low probability array.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;if (_psTmp0[idx] == default(byte)) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (_psCnt[idx] &amp;lt; PS_VALIDATE_LIMIT) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _psCnt[idx]++;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } else {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _psCnt[idx] = PS_VALIDATE_LIMIT;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _psTmp1[idx] = default(byte);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;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. &lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;else if (_psTmp1[idx] == default(byte)) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (_psCnt[idx] &amp;gt;= PS_VALIDATE_LIMIT) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; isTextChange = true;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _psCnt[idx] = PS_VALIDATE_LIMIT + 1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _psTmp1[idx] = _psTmp0[idx];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _psTmp0[idx] = default(byte);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;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.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;else if (_psCnt[idx] == null) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _psTmp0[idx] = default(byte);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _psCnt[idx] = 1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } else {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _psTmp1[idx] = default(byte);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;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.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (isTextChange) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for (byte i = 0; i &amp;lt; _psCnt.Length; i++) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (_psCnt[i] &amp;gt; 1) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _psCnt[i]--;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;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. &lt;blockquote&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for (byte i = 0; i &amp;lt; _psCnt.Length; i++) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (_psCnt[i] &amp;lt; PS_VALIDATE_LIMIT) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; isComplete = false;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Only if PS text in the high probability array is complete, we&amp;#39;ll copy it into display.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (isComplete) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for (byte i = 0; i &amp;lt; _psDisplay.Length; i++) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _psDisplay[i] = _psTmp0[i];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;It is not very hard to treat PS. Isn&amp;#39;t it? Let&amp;#39;s see what&amp;#39;s going on with RT.&lt;/p&gt; &lt;p&gt;If A and B message flag changes, we&amp;#39;ll try to force a display by increasing the validation count for each byte. Then, we&amp;#39;ll wipe any cached text.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; if (abFlag != _rtFlag &amp;amp;&amp;amp; _rtFlagValid) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // If the A/B message flag changes, try to force a display&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // by increasing the validation count of each byte&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for (i = 0; i &amp;lt; _rtCnt.Length; i++) _rtCnt[addr + i]++;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _updateRTValue();  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Wipe out the cached text &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for (i = 0; i &amp;lt; _rtCnt.Length; i++) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtCnt[i] = 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtTmp0[i] = 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtTmp1[i] = 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now A and B flags are safe, sp we can start with message processing. First of all, NULL in RDS means space :)&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; _rtFlag = abFlag;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; _rtFlagValid = true;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; for (i = 0; i &amp;lt; count; i++) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (p[i] == null) p[i] = (byte)&amp;#39; &amp;#39;; &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;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. &lt;blockquote&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (_rtTmp0[addr + i] == p[i]) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (_rtCnt[addr + i] &amp;lt; RT_VALIDATE_LIMIT) _rtCnt[addr + i]++;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtCnt[addr + i] = RT_VALIDATE_LIMIT;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtTmp1[addr + i] = p[i];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;When the new byte matches with low probability byte, we&amp;#39;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&amp;#39;ll have to remove it down later.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;else if (_rtTmp1[addr + i] == p[i]) { &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (_rtCnt[addr + i] &amp;gt;= PS_VALIDATE_LIMIT) isChange = true;  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtCnt[addr + i] = RT_VALIDATE_LIMIT + 1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtTmp1[addr + i] = _rtTmp0[addr + i];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtTmp0[addr + i] = p[i];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;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.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;else if (_rtCnt[addr + i] == null) { &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtTmp0[addr + i] = p[i];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtCnt[addr + i] = 1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } else _rtTmp1[addr + i] = p[i]; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; } &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now when the text is changing, we&amp;#39;ll decrement the counter for all characters exactly as we did for PS. &lt;blockquote&gt; &lt;p&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for (i = 0; i &amp;lt; _rtCnt.Length; i++) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (_rtCnt[i] &amp;gt; 1) _rtCnt[i]--;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp; } &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;However, right after, we&amp;#39;ll update display.&amp;nbsp; &lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; _updateRTValue();&lt;br /&gt;}&lt;/p&gt;&lt;/blockquote&gt; &lt;h3&gt;Displaying RT&lt;/h3&gt; &lt;p&gt;But how to convert all those byte arrays into readable message? Simple :)&lt;/p&gt; &lt;p&gt;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.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;for (i = 0; i &amp;lt; _rtTmp0.Length; i++) {&lt;br /&gt;&amp;nbsp;&amp;nbsp; if (_rtCnt[i] &amp;lt; RT_VALIDATE_LIMIT) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; isComplete = false;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break;&lt;br /&gt;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp; if (_rtTmp0[i] == 0x0d) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break;&lt;br /&gt;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;} &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now, when our Radio Text is in the high probability and it complete, we should copy buffers.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;if (isComplete) {&lt;br /&gt;&amp;nbsp;&amp;nbsp; _Text = string.Empty; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; for (i = 0; i &amp;lt; _rtDisplay.Length; i += 2) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ((_rtDisplay[i] != 0x0d) &amp;amp;&amp;amp; (_rtDisplay[i + 1] != 0x0d)) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtDisplay[i] = _rtTmp0[i + 1];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtDisplay[i + 1] = _rtTmp0[i];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } else {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtDisplay[i] = _rtTmp0[i];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtDisplay[i + 1] = _rtTmp0[i + 1];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (_rtDisplay[i] != 0x0d)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _Text += _rtDisplay[i];  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (_rtDisplay[i + 1] != 0x0d)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _Text += _rtDisplay[i + 1];  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ((_rtDisplay[i] == 0x0d) || (_rtDisplay[i + 1] == 0x0d))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; i = (byte)_rtDisplay.Length;&lt;br /&gt;&amp;nbsp;&amp;nbsp; } &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;And not forget to wipe out everything after the end of the message :)&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; for (i++; i &amp;lt; _rtDisplay.Length; i++) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtDisplay[i] = 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtCnt[i] = 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtTmp0[i] = 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _rtTmp1[i] = 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;} &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;And finally update the text&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Text = _Text;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;We done. Now we can handle RDS digital messages, but what to do with analog data we get? Don&amp;#39;t you already know? I &lt;a href="http://blogs.microsoft.co.il/blogs/tamir/archive/2008/02/17/sound-tone-and-dtmf-generation-by-using-managed-directsound-and-c-and-sine-tone-detection-with-pure-managed-goertzel-algorithm-implementation.aspx"&gt;blogged about it here&lt;/a&gt;. &lt;p&gt;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. &lt;p&gt;&lt;img border="0" alt="image" src="http://blogs.microsoft.co.il/blogs/tamir/WindowsLiveWriter/ReadinganddecodingRDSRadioDataSysteminC_1238A/image_988a1f1f-0dec-46b1-b494-3c75a0b9ce7d.png" width="263" height="44" /&gt;&lt;/p&gt;&lt;img src="http://blogs.microsoft.co.il/aggbug.aspx?PostID=191019" width="1" height="1"&gt;</description><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/WPF/default.aspx">WPF</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/tutorial/default.aspx">tutorial</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Tips+and+Tricks/default.aspx">Tips and Tricks</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/source/default.aspx">source</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Mobile/default.aspx">Mobile</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/C_2300_/default.aspx">C#</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/.NET+3.5/default.aspx">.NET 3.5</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Work+process/default.aspx">Work process</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Math/default.aspx">Math</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/DEV/default.aspx">DEV</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Hardware/default.aspx">Hardware</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Interop/default.aspx">Interop</category></item><item><title>WGS to UTM, UTM to WGS conversions, geo distance, azimuth and other geographical calculations in C#</title><link>http://blogs.microsoft.co.il/blogs/tamir/archive/2008/09/15/wgs-to-utm-utm-to-wgs-conversions-geo-distance-azimuth-and-other-geographical-calculations-in-c.aspx</link><pubDate>Mon, 15 Sep 2008 07:16:37 GMT</pubDate><guid isPermaLink="false">b5c4f5bc-c09b-4439-a595-91a98c1847df:139757</guid><dc:creator>Tamir Khason</dc:creator><slash:comments>2</slash:comments><description>&lt;h3&gt;[This blog was migrated. You will not be able to comment here.&lt;br /&gt;The new URL of this post is &lt;a href="http://khason.net/blog/wgs-to-utm-utm-to-wgs-conversions-geo-distance-azimuth-and-other-geographical-calculations-in-c/"&gt;http://khason.net/blog/wgs-to-utm-utm-to-wgs-conversions-geo-distance-azimuth-and-other-geographical-calculations-in-c/&lt;/a&gt;]&lt;/h3&gt;&lt;hr /&gt;&lt;blockquote&gt; &lt;h4&gt;&lt;em&gt;&amp;quot;The reports of my death are greatly exaggerated&amp;quot;&lt;/em&gt;&lt;/h4&gt; &lt;p&gt;Since &lt;a href="http://blogs.microsoft.co.il/blogs/tamir/archive/2008/08/19/i-m-leaving-consulting-field-joined-new-project-for-full-time.aspx"&gt;my post about leaving consulting field&lt;/a&gt;, I got huge amount of email with questions about all community projects, I&amp;#39;m leading, blogging in general and specific to the future of this blog. &lt;/p&gt; &lt;p&gt;To make things clear, I leaved consulting, and now, I have less time and reasons to blog, however, I&amp;#39;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&amp;#39;s too much to handle, thus be prepared for delays.&lt;/p&gt; &lt;p&gt;To be sure, I&amp;#39;m alive, you can &lt;a href="http://twitter.com/tamir"&gt;follow me at twitter&lt;/a&gt; (it demands less time to write) :) Anyway, &lt;strong&gt;that you for reading my blog and supporting me&lt;/strong&gt;.&amp;nbsp; &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now it&amp;#39;s good time to write something useful for you :)&lt;/p&gt; &lt;p&gt;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. &lt;/p&gt; &lt;p&gt;&lt;img style="border-top-width:0px;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;" height="318" alt="image" src="http://blogs.microsoft.co.il/blogs/tamir/WindowsLiveWriter/WGStoUTMUTMtoWGSconversionsgeodistanceaz_8D93/image_3.png" width="377" border="0" /&gt; &lt;/p&gt; &lt;p&gt;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&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;public static double DegToRad(double deg) { return (deg / 180.0 * Math.PI); } &lt;br /&gt;public static double RadToDeg(double rad) { return (rad / Math.PI * 180.0); }&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Also, there are some constants should be used, if you&amp;#39;re calculating geo information in the Earth&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;const double sm_a = 6378137.0; &lt;br /&gt;const double sm_b = 6356752.314; &lt;br /&gt;const double sm_EccSquared = 6.69437999013e-03; &lt;br /&gt;const double UTMScaleFactor = 0.9996;&lt;/p&gt;&lt;/blockquote&gt; &lt;h3&gt;Conversion WGS2UTM (LatLon2UTM)&lt;/h3&gt; &lt;p&gt;Fist of all, we should calculate UTM zone. This one is simple&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;int zone = (int)(Math.Floor((latlon.Longitude + 180.0) / 6) + 1);&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now, when we have zone, we should calculate UTM central meridian, footprint of latitude and arc length of the meridian&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;public static double UTMCentralMeridian(int zone) { return DegToRad(-183.0 + (zone * 6.0)); } &lt;br /&gt;&lt;br /&gt;&lt;/p&gt; &lt;p&gt;public static double FootpointLatitude(double y) { &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate n (Eq. 10.18) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var n = (sm_a - sm_b) / (sm_a + sm_b); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate alpha_ (Eq. 10.22) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* (Same as alpha in Eq. 10.17) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var alpha_ = ((sm_a + sm_b) / 2.0) * (1 + (Math.Pow(n, 2.0) / 4) + (Math.Pow(n, 4.0) / 64)); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate y_ (Eq. 10.23) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var y_ = y / alpha_; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate beta_ (Eq. 10.22) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 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); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate gamma_ (Eq. 10.22) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var gamma_ = (21.0 * Math.Pow(n, 2.0) / 16.0) + (-55.0 * Math.Pow(n, 4.0) / 32.0); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate delta_ (Eq. 10.22) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var delta_ = (151.0 * Math.Pow(n, 3.0) / 96.0) + (-417.0 * Math.Pow(n, 5.0) / 128.0); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate epsilon_ (Eq. 10.22) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var epsilon_ = (1097.0 * Math.Pow(n, 4.0) / 512.0); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Now calculate the sum of the series (Eq. 10.21) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 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_)); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;br /&gt;&lt;br /&gt;&lt;/p&gt; &lt;p&gt;public static double ArcLengthOfMeridian(double phi) { &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate n */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var n = (sm_a - sm_b) / (sm_a + sm_b); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate alpha */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var alpha = ((sm_a + sm_b) / 2.0) * (1.0 + (Math.Pow(n, 2.0) / 4.0) + (Math.Pow(n, 4.0) / 64.0)); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate beta */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 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); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate gamma */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var gamma = (15.0 * Math.Pow(n, 2.0) / 16.0) + (-15.0 * Math.Pow(n, 4.0) / 32.0); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate delta */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var delta = (-35.0 * Math.Pow(n, 3.0) / 48.0) + (105.0 * Math.Pow(n, 5.0) / 256.0); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate epsilon */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var epsilon = (315.0 * Math.Pow(n, 4.0) / 512.0); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Now calculate the sum of the series and return */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 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))); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now, we have everything to calculate UTM&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;public static GeoPoint MapLatLonToXY(double phi, double lambda, double lambda0) { &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate ep2 */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var ep2 = (Math.Pow(sm_a, 2.0) - Math.Pow(sm_b, 2.0)) / Math.Pow(sm_b, 2.0); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate nu2 */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var nu2 = ep2 * Math.Pow(Math.Cos(phi), 2.0); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate N */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var N = Math.Pow(sm_a, 2.0) / (sm_b * Math.Sqrt(1 + nu2)); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate t */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var t = Math.Tan(phi); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var t2 = t * t; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var tmp = (t2 * t2 * t2) - Math.Pow(t, 6.0); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate l */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var l = lambda - lambda0; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate coefficients for l**n in the equations below &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; so a normal human being can read the expressions for easting &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; and northing &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; -- l**1 and l**2 have coefficients of 1.0 */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var l3coef = 1.0 - t2 + nu2; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var l4coef = 5.0 - t2 + 9 * nu2 + 4.0 * (nu2 * nu2); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var l5coef = 5.0 - 18.0 * t2 + (t2 * t2) + 14.0 * nu2 - 58.0 * t2 * nu2; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var l6coef = 61.0 - 58.0 * t2 + (t2 * t2) + 270.0 * nu2 - 330.0 * t2 * nu2; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var l7coef = 61.0 - 479.0 * t2 + 179.0 * (t2 * t2) - (t2 * t2 * t2); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var l8coef = 1385.0 - 3111.0 * t2 + 543.0 * (t2 * t2) - (t2 * t2 * t2); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var xy = new GeoPoint(); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Calculate easting (x) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 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)); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Calculate northing (y) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 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)); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return xy; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;br /&gt;&lt;br /&gt;&lt;/p&gt; &lt;p&gt;public static GeoCoord MapXYToLatLon(double x, double y, double lambda0) { &lt;br /&gt;&amp;nbsp;&amp;nbsp; /* Get the value of phif, the footpoint latitude. */ &lt;br /&gt;&amp;nbsp;&amp;nbsp; double phif = FootpointLatitude(y); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; /* Precalculate ep2 */ &lt;br /&gt;&amp;nbsp;&amp;nbsp; double ep2 = (Math.Pow(sm_a, 2.0) - Math.Pow(sm_b, 2.0)) / Math.Pow(sm_b, 2.0); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; /* Precalculate cos (phif) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp; var cf = Math.Cos(phif); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; /* Precalculate nuf2 */ &lt;br /&gt;&amp;nbsp;&amp;nbsp; var nuf2 = ep2 * Math.Pow(cf, 2.0); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; /* Precalculate Nf and initialize Nfpow */ &lt;br /&gt;&amp;nbsp;&amp;nbsp; var Nf = Math.Pow(sm_a, 2.0) / (sm_b * Math.Sqrt(1 + nuf2)); &lt;br /&gt;&amp;nbsp;&amp;nbsp; var Nfpow = Nf; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; /* Precalculate tf */ &lt;br /&gt;&amp;nbsp;&amp;nbsp; var tf = Math.Tan(phif); &lt;br /&gt;&amp;nbsp;&amp;nbsp; var tf2 = tf * tf; &lt;br /&gt;&amp;nbsp;&amp;nbsp; var tf4 = tf2 * tf2; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; /* Precalculate fractional coefficients for x**n in the equations &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; below to simplify the expressions for latitude and longitude. */ &lt;br /&gt;&amp;nbsp;&amp;nbsp; var x1frac = 1.0 / (Nfpow * cf); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; Nfpow *= Nf;&amp;nbsp;&amp;nbsp; /* now equals Nf**2) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp; var x2frac = tf / (2.0 * Nfpow); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; Nfpow *= Nf;&amp;nbsp;&amp;nbsp; /* now equals Nf**3) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp; var x3frac = 1.0 / (6.0 * Nfpow * cf); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; Nfpow *= Nf;&amp;nbsp;&amp;nbsp; /* now equals Nf**4) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp; var x4frac = tf / (24.0 * Nfpow); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; Nfpow *= Nf;&amp;nbsp;&amp;nbsp; /* now equals Nf**5) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp; var x5frac = 1.0 / (120.0 * Nfpow * cf); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; Nfpow *= Nf;&amp;nbsp;&amp;nbsp; /* now equals Nf**6) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp; var x6frac = tf / (720.0 * Nfpow); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; Nfpow *= Nf;&amp;nbsp;&amp;nbsp; /* now equals Nf**7) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp; var x7frac = 1.0 / (5040.0 * Nfpow * cf); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; Nfpow *= Nf;&amp;nbsp;&amp;nbsp; /* now equals Nf**8) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp; var x8frac = tf / (40320.0 * Nfpow); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; /* Precalculate polynomial coefficients for x**n. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; -- x**1 does not have a polynomial coefficient. */ &lt;br /&gt;&amp;nbsp;&amp;nbsp; var x2poly = -1.0 - nuf2; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; var x3poly = -1.0 - 2 * tf2 - nuf2; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; var x4poly = 5.0 + 3.0 * tf2 + 6.0 * nuf2 - 6.0 * tf2 * nuf2 - 3.0 * (nuf2 * nuf2) - 9.0 * tf2 * (nuf2 * nuf2); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; var x5poly = 5.0 + 28.0 * tf2 + 24.0 * tf4 + 6.0 * nuf2 + 8.0 * tf2 * nuf2; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; var x6poly = -61.0 - 90.0 * tf2 - 45.0 * tf4 - 107.0 * nuf2 + 162.0 * tf2 * nuf2; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; var x7poly = -61.0 - 662.0 * tf2 - 1320.0 * tf4 - 720.0 * (tf4 * tf2); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; var x8poly = 1385.0 + 3633.0 * tf2 + 4095.0 * tf4 + 1575 * (tf4 * tf2); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; var philambda = new GeoCoord(); &lt;br /&gt;&amp;nbsp;&amp;nbsp; /* Calculate latitude */ &lt;br /&gt;&amp;nbsp;&amp;nbsp; 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); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; /* Calculate longitude */ &lt;br /&gt;&amp;nbsp;&amp;nbsp; 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); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; return philambda; &lt;br /&gt;}&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;We done, the only thing, should be adjusted is easting and northing for UTM system&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;xy.X = xy.X * UTMScaleFactor + 500000.0; &lt;br /&gt;xy.Y = xy.Y * UTMScaleFactor; &lt;br /&gt;if (xy.Y &amp;lt; 0.0) xy.Y += 10000000.0;&lt;/p&gt;&lt;/blockquote&gt; &lt;h3&gt;Conversion UTM2WGS (UTM2LatLon)&lt;/h3&gt; &lt;p&gt;After al had all thin math in previous chapter, now we should calculate opposite conversion&lt;/p&gt; &lt;p&gt;First adjust&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;x -= 500000.0; &lt;br /&gt;x /= UTMScaleFactor; &lt;/p&gt; &lt;p&gt;/* If in southern hemisphere, adjust y accordingly. */ &lt;br /&gt;if (southhemi) y -= 10000000.0; &lt;/p&gt; &lt;p&gt;y /= UTMScaleFactor; &lt;/p&gt; &lt;p&gt;var cmeridian = UTMCentralMeridian(zone);&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now calculate&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;public static GeoCoord MapXYToLatLon(double x, double y, double lambda0) { &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Get the value of phif, the footpoint latitude. */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; double phif = FootpointLatitude(y); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate ep2 */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; double ep2 = (Math.Pow(sm_a, 2.0) - Math.Pow(sm_b, 2.0)) / Math.Pow(sm_b, 2.0); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate cos (phif) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var cf = Math.Cos(phif); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate nuf2 */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var nuf2 = ep2 * Math.Pow(cf, 2.0); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate Nf and initialize Nfpow */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var Nf = Math.Pow(sm_a, 2.0) / (sm_b * Math.Sqrt(1 + nuf2)); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var Nfpow = Nf; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate tf */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var tf = Math.Tan(phif); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var tf2 = tf * tf; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var tf4 = tf2 * tf2; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate fractional coefficients for x**n in the equations &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; below to simplify the expressions for latitude and longitude. */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var x1frac = 1.0 / (Nfpow * cf); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Nfpow *= Nf;&amp;nbsp;&amp;nbsp; /* now equals Nf**2) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var x2frac = tf / (2.0 * Nfpow); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Nfpow *= Nf;&amp;nbsp;&amp;nbsp; /* now equals Nf**3) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var x3frac = 1.0 / (6.0 * Nfpow * cf); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Nfpow *= Nf;&amp;nbsp;&amp;nbsp; /* now equals Nf**4) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var x4frac = tf / (24.0 * Nfpow); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Nfpow *= Nf;&amp;nbsp;&amp;nbsp; /* now equals Nf**5) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var x5frac = 1.0 / (120.0 * Nfpow * cf); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Nfpow *= Nf;&amp;nbsp;&amp;nbsp; /* now equals Nf**6) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var x6frac = tf / (720.0 * Nfpow); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Nfpow *= Nf;&amp;nbsp;&amp;nbsp; /* now equals Nf**7) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var x7frac = 1.0 / (5040.0 * Nfpow * cf); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Nfpow *= Nf;&amp;nbsp;&amp;nbsp; /* now equals Nf**8) */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var x8frac = tf / (40320.0 * Nfpow); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Precalculate polynomial coefficients for x**n. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; -- x**1 does not have a polynomial coefficient. */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var x2poly = -1.0 - nuf2; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var x3poly = -1.0 - 2 * tf2 - nuf2; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var x4poly = 5.0 + 3.0 * tf2 + 6.0 * nuf2 - 6.0 * tf2 * nuf2 - 3.0 * (nuf2 * nuf2) - 9.0 * tf2 * (nuf2 * nuf2); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var x5poly = 5.0 + 28.0 * tf2 + 24.0 * tf4 + 6.0 * nuf2 + 8.0 * tf2 * nuf2; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var x6poly = -61.0 - 90.0 * tf2 - 45.0 * tf4 - 107.0 * nuf2 + 162.0 * tf2 * nuf2; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var x7poly = -61.0 - 662.0 * tf2 - 1320.0 * tf4 - 720.0 * (tf4 * tf2); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var x8poly = 1385.0 + 3633.0 * tf2 + 4095.0 * tf4 + 1575 * (tf4 * tf2); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var philambda = new GeoCoord(); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Calculate latitude */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 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); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Calculate longitude */ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 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); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return philambda; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;At the end do not forget to return Latitude and Longitude in degrees, rather, then in radians&lt;/p&gt; &lt;h3&gt;Azimuth calculation&lt;/h3&gt; &lt;p&gt;We done with coordinates, now azimuth&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;public static double GetAzimuth(WGSCoord c1, WGSCoord c2) { &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var lat1 = DegToRad(c1.Latitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var lon1 = DegToRad(c1.Longitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var lat2 = DegToRad(c2.Latitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var lon2 = DegToRad(c2.Longitude); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 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))))); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/blockquote&gt; &lt;h3&gt;Distance calculations&lt;/h3&gt; &lt;blockquote&gt; &lt;p&gt;public static double GetDistance(WGSCoord c1, WGSCoord c2) { &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var dLat = DegToRad(c2.Latitude - c1.Latitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var dLon = DegToRad(c2.Longitude - c2.Longitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 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); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a)); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return sm_a * c; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/blockquote&gt; &lt;h3&gt;Final destination coordinates, based on start coordinate, bearing and distance&lt;/h3&gt; &lt;blockquote&gt; &lt;p&gt;public static WGSCoord GetDestination(WGSCoord start, double distance, double azimuth) { &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 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)))); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 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)))); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return new WGSCoord(lon,lat); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/blockquote&gt; &lt;h3&gt;Midpoint calculation&lt;/h3&gt; &lt;blockquote&gt; &lt;p&gt;public static WGSCoord GetMidpoint(WGSCoord start, WGSCoord end) { &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var dLat = DegToRad(end.Latitude - start.Latitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var dLon = DegToRad(end.Longitude - start.Longitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var lat2 = DegToRad(end.Latitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var lat1 = DegToRad(start.Latitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var lon1 = DegToRad(start.Longitude); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var Bx = Math.Cos(lat2) * Math.Cos(dLon); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var By = Math.Cos(lat2) * Math.Sin(dLon); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var lat3 = Math.Atan2(Math.Sin(lat1) + Math.Sin(lat2), Math.Sqrt((Math.Cos(lat1) + Bx) * (Math.Cos(lat1) + Bx) + By * By)); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var lon3 = lon1 + Math.Atan2(By, Math.Cos(lat1) + Bx); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return new WGSCoord(RadToDeg(lon3), RadToDeg(lat3)); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/blockquote&gt; &lt;h3&gt;Rhumb lines calculation&lt;/h3&gt; &lt;p&gt;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.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;public static double GetRhumbDistance(WGSCoord start, WGSCoord end) { &lt;br /&gt;&amp;nbsp;&amp;nbsp; var dLat = DegToRad(end.Latitude - start.Latitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp; var dLon = DegToRad(end.Longitude - start.Longitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp; var lat1 = DegToRad(start.Latitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp; var lon1 = DegToRad(start.Longitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp; var lat2 = DegToRad(end.Latitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp; var lon2 = DegToRad(end.Longitude); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; var dPhi = Math.Log(Math.Tan(lat2 / 2 + Math.PI / 4) / Math.Tan(lat1 / 2 + Math.PI / 4)); &lt;br /&gt;&amp;nbsp;&amp;nbsp; var q = (Math.Abs(dLat) &amp;gt; 1e-10) ? dLat / dPhi : Math.Cos(lat1); &lt;br /&gt;&amp;nbsp;&amp;nbsp; if (Math.Abs(dLon) &amp;gt; Math.PI) { &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; dLon = dLon &amp;gt; 0 ? -(2 * Math.PI - dLon) : (2 * Math.PI + dLon); &lt;br /&gt;&amp;nbsp;&amp;nbsp; } &lt;br /&gt;&amp;nbsp;&amp;nbsp; return Math.Sqrt(dLat * dLat + q * q * dLon * dLon) * sm_a; &lt;br /&gt;}&lt;/p&gt; &lt;p&gt;public static double GetRhumbBearing(WGSCoord start, WGSCoord end) { &lt;br /&gt;&amp;nbsp;&amp;nbsp; var dLat = DegToRad(end.Latitude - start.Latitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp; var dLon = DegToRad(end.Longitude - start.Longitude); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; var lat1 = DegToRad(start.Latitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp; var lon1 = DegToRad(start.Longitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp; var lat2 = DegToRad(end.Latitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp; var lon2 = DegToRad(end.Longitude); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; var dPhi = Math.Log(Math.Tan(lat2 / 2 + Math.PI / 4) / Math.Tan(lat1 / 2 + Math.PI / 4)); &lt;br /&gt;&amp;nbsp;&amp;nbsp; if (Math.Abs(dLon) &amp;gt; Math.PI) { &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; dLon = dLon &amp;gt; 0 ? -(2 * Math.PI - dLon) : (2 * Math.PI + dLon); &lt;br /&gt;&amp;nbsp;&amp;nbsp; } &lt;br /&gt;&amp;nbsp;&amp;nbsp; return Math.Atan2(dLon, dPhi); &lt;br /&gt;}&lt;/p&gt; &lt;p&gt;public static WGSCoord GetRhumbDestination(WGSCoord start, WGSCoord end) { &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var lat1 = DegToRad(start.Latitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var lon1 = DegToRad(start.Longitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var lat2 = DegToRad(end.Latitude); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var lon2 = DegToRad(end.Longitude); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var d = GetRhumbDistance(start, end); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var b = GetRhumbBearing(start, end); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; lat2 = lat1 +&amp;nbsp; d * Math.Cos(b); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var dPhi = Math.Log(Math.Tan(lat2 / 2 + Math.PI / 4) / Math.Tan(lat1 / 2 + Math.PI / 4)); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var q = (Math.Abs(lat2 - lat1) &amp;gt; 1e-10) ? (lat2 - lat1) / dPhi : Math.Cos(lat1); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var dLon = d * Math.Sin(b) / q; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (Math.Abs(lat2) &amp;gt; Math.PI / 2) lat2 = lat2 &amp;gt; 0 ? Math.PI - lat2 : -Math.PI - lat2; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; lon2 = (lon1 + dLon + Math.PI) % (2 * Math.PI) - Math.PI; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return new WGSCoord(RadToDeg(lon2), RadToDeg(lat2)); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/blockquote&gt; &lt;h3&gt;Conversion between decimal degrees to degrees-minutes-seconds&lt;/h3&gt; &lt;blockquote&gt; &lt;p&gt;public static Pair&amp;lt;double,double&amp;gt; ToD_Mm(this double decCoord) { &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var degSeg = (int)decCoord; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return new Pair&amp;lt;double, double&amp;gt;(degSeg, decCoord % degSeg * 60); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now it&amp;#39;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&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;private static double dot(Vector v1, Vector v2) { return (v1.X * v2.X + v1.Y * v2.Y); }&lt;/p&gt;&lt;/blockquote&gt; &lt;h3&gt;Get the distance between two points&lt;/h3&gt; &lt;blockquote&gt; &lt;p&gt;private static double distance(Point p1, Point p2) { &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var a = Math.Pow(p1.X, 2) - Math.Pow(p2.X, 2); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var b = Math.Pow(p1.Y, 2) - Math.Pow(p2.Y, 2); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return Math.Sqrt(Math.Abs(a + b)); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/blockquote&gt; &lt;h3&gt;Get the shortest distance between point and line segment&lt;/h3&gt; &lt;blockquote&gt; &lt;p&gt;private static double distance(Point p1, Point p2, Point p) { &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var v = p1 - p2; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var w = p - p2; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var c1 = dot(w, v); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (c1 &amp;lt;= 0) return distance(p, p1); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var c2 = dot(v, v); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (c2 &amp;lt;= c1) return distance(p, p2); &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var b = c1 / c2; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Point Pb = p1 + b * v; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return distance(p, Pb); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Isn&amp;#39;t it enough math for now? It is. Have a nice day and be good people. Next time, we&amp;#39;ll speak about different path finding algorithms, edges, nodes and other fun math optimizations.&lt;/p&gt;&lt;img src="http://blogs.microsoft.co.il/aggbug.aspx?PostID=139757" width="1" height="1"&gt;</description><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/tutorial/default.aspx">tutorial</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Tips+and+Tricks/default.aspx">Tips and Tricks</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/source/default.aspx">source</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/C_2300_/default.aspx">C#</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/.NET+3.5/default.aspx">.NET 3.5</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Work+process/default.aspx">Work process</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Math/default.aspx">Math</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/GIS/default.aspx">GIS</category><category domain="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Algorithms/default.aspx">Algorithms</category></item></channel></rss>