From c6d91a5b251014e0e2bf65d6153821457040fca1 Mon Sep 17 00:00:00 2001
From: Michael Oborne <mich146@hotmail.com>
Date: Thu, 26 Apr 2012 08:01:08 +0800
Subject: [PATCH] APM Planner - code only add yaw to config screen. intergrate
 adams changes this will force an update, but not the main exe

---
 .../ArdupilotMegaPlanner/ArduCopterConfig.xml |  66 ++++-
 .../ArdupilotMegaPlanner/ArdupilotMega.csproj |   1 +
 Tools/ArdupilotMegaPlanner/MainV2.cs          |  14 +-
 Tools/ArdupilotMegaPlanner/Msi/wix.pdb        | Bin 19968 -> 19968 bytes
 .../Utilities/CollectionExtensions.cs         |  47 +++
 .../Utilities/ParameterMetaDataConstants.cs   |  16 +-
 .../Utilities/ParameterMetaDataParser.cs      | 267 +++++++++++++-----
 .../Utilities/ParameterMetaDataRepository.cs  |  15 +-
 Tools/ArdupilotMegaPlanner/app.config         |   4 +
 .../bin/Release/ArduCopterConfig.xml          |  66 ++++-
 .../bin/Release/version.txt                   |   2 +-
 11 files changed, 424 insertions(+), 74 deletions(-)
 create mode 100644 Tools/ArdupilotMegaPlanner/Utilities/CollectionExtensions.cs

diff --git a/Tools/ArdupilotMegaPlanner/ArduCopterConfig.xml b/Tools/ArdupilotMegaPlanner/ArduCopterConfig.xml
index 2558e46e2..fda38f678 100644
--- a/Tools/ArdupilotMegaPlanner/ArduCopterConfig.xml
+++ b/Tools/ArdupilotMegaPlanner/ArduCopterConfig.xml
@@ -101,6 +101,70 @@ Too high = slow wobbles
         <STEP>0.001</STEP>
       </FIELD>
     </FIELDS>
+
+    <SUBHEAD>Yaw Angular Rate Control:</SUBHEAD>
+    <DESC>How much throttle is applied to rotate the copter at the desired speed.
+    </DESC>
+    <FIELDS>
+      <FIELD>
+        <NAME>P</NAME>
+        <PARAMNAME>RATE_YAW_P</PARAMNAME>
+        <RANGEMIN>0.001</RANGEMIN>
+        <RANGEMAX>5</RANGEMAX>
+        <STEP>0.001</STEP>
+      </FIELD>
+      <FIELD>
+        <NAME>I</NAME>
+        <PARAMNAME>RATE_YAW_I</PARAMNAME>
+        <RANGEMIN>0</RANGEMIN>
+        <RANGEMAX>5</RANGEMAX>
+        <STEP>0.001</STEP>
+      </FIELD>
+      <FIELD>
+        <NAME>D</NAME>
+        <PARAMNAME>RATE_YAW_D</PARAMNAME>
+        <RANGEMIN>0</RANGEMIN>
+        <RANGEMAX>5</RANGEMAX>
+        <STEP>0.001</STEP>
+      </FIELD>
+      <FIELD>
+        <NAME>IMAX</NAME>
+        <PARAMNAME>RATE_YAW_IMAX</PARAMNAME>
+        <RANGEMIN>0</RANGEMIN>
+        <RANGEMAX>50</RANGEMAX>
+        <STEP>1</STEP>
+      </FIELD>
+    </FIELDS>
+
+    <SUBHEAD>Yaw Stabilize Control:</SUBHEAD>
+    <DESC>
+      How fast the copter reacts to user or autopilot input.
+      Higher = more aggressive control.
+      Too high = slow wobbles
+    </DESC>
+    <FIELDS>
+      <FIELD>
+        <NAME>P</NAME>
+        <PARAMNAME>STB_YAW_P</PARAMNAME>
+        <RANGEMIN>0.001</RANGEMIN>
+        <RANGEMAX>10</RANGEMAX>
+        <STEP>0.001</STEP>
+      </FIELD>
+      <FIELD>
+        <NAME>I</NAME>
+        <PARAMNAME>STB_YAW_I</PARAMNAME>
+        <RANGEMIN>0</RANGEMIN>
+        <RANGEMAX>5</RANGEMAX>
+        <STEP>0.001</STEP>
+      </FIELD>
+      <FIELD>
+        <NAME>IMAX</NAME>
+        <PARAMNAME>STB_YAW_IMAX</PARAMNAME>
+        <RANGEMIN>0</RANGEMIN>
+        <RANGEMAX>50</RANGEMAX>
+        <STEP>1</STEP>
+      </FIELD>
+    </FIELDS>
   </Item>
   <!-- Loiter -->
   <Item>
@@ -140,7 +204,7 @@ How much angle is applied to make the copter accelerate to the desired speed.
         <PARAMNAME>LOITER_LON_IMAX</PARAMNAME>
         <RANGEMIN>0</RANGEMIN>
         <RANGEMAX>50</RANGEMAX>
-        <STEP>0.1</STEP>
+        <STEP>1</STEP>
       </FIELD>
     </FIELDS>
     <SUBHEAD>Loiter Speed:</SUBHEAD>
diff --git a/Tools/ArdupilotMegaPlanner/ArdupilotMega.csproj b/Tools/ArdupilotMegaPlanner/ArdupilotMega.csproj
index def4fc453..b318087f8 100644
--- a/Tools/ArdupilotMegaPlanner/ArdupilotMega.csproj
+++ b/Tools/ArdupilotMegaPlanner/ArdupilotMega.csproj
@@ -226,6 +226,7 @@
     <Compile Include="Attributes\DisplayTextAttribute.cs" />
     <Compile Include="Attributes\PrivateAttribute.cs" />
     <Compile Include="CodeGen.cs" />
+    <Compile Include="Utilities\CollectionExtensions.cs" />
     <Compile Include="Utilities\ParameterMetaDataConstants.cs" />
     <Compile Include="Controls\BackstageView\BackstageView.cs">
       <SubType>UserControl</SubType>
diff --git a/Tools/ArdupilotMegaPlanner/MainV2.cs b/Tools/ArdupilotMegaPlanner/MainV2.cs
index f04826220..907bb8c6c 100644
--- a/Tools/ArdupilotMegaPlanner/MainV2.cs
+++ b/Tools/ArdupilotMegaPlanner/MainV2.cs
@@ -1733,8 +1733,18 @@ namespace ArdupilotMega
 
         static void DoUpdateWorker_DoWork(object sender, Controls.ProgressWorkerEventArgs e)
         {
-            ((ProgressReporterDialogue)sender).UpdateProgressAndStatus(-1, "Getting Base URL");
-            MainV2.updateCheckMain((ProgressReporterDialogue)sender);
+           // TODO: Is this the right place?
+           #region Fetch Parameter Meta Data
+           
+           var progressReporterDialogue = ((ProgressReporterDialogue) sender);
+           progressReporterDialogue.UpdateProgressAndStatus(-1, "Getting Updated Parameters");
+
+           ParameterMetaDataParser.GetParameterInformation();
+           
+           #endregion Fetch Parameter Meta Data
+
+           progressReporterDialogue.UpdateProgressAndStatus(-1, "Getting Base URL");
+           MainV2.updateCheckMain(progressReporterDialogue);
         }
 
         private static bool updateCheck(ProgressReporterDialogue frmProgressReporter, string baseurl, string subdir)
diff --git a/Tools/ArdupilotMegaPlanner/Msi/wix.pdb b/Tools/ArdupilotMegaPlanner/Msi/wix.pdb
index e2bd4a8c51e6596bf41da25015857ed84be7ef85..a5d459167772c92af2f5da9b7b52010c246af54a 100644
GIT binary patch
delta 519
zcmZpe!`Lu~aRUpNgi-$te?|rdhBZrs_U{d9_u1F9(WYBUJ9)DV*Be2$vyyAL?Ugt4
z$+R#rs!pCPcbw_7$mAILEFkB&d^V%%W@ClvK$*>_mD)j6uxdIpQ>N(VJsNsUOc|n+
z-)N-)RR(J(1Ld}BNApdV7h_{d%quS6EMpbH!tz0KlhEV}y9Aa3c_8BsknuqZ$Z)V%
ztLGC0x`}}eM1VoOD~ttlEJ$1wCcu!H4`YJlq=A?nD!|C#pNEjk&r<?&jNx*cU^_ru
zMg{}3P#_JnkC7S3nfwCij=Yp4AX5#9L4b(?RZj;{tQRZ;l0yJSh9U-0pkuDW<qXVV
zvWyI>g0FybRdBgvB==`CqyP=FhZ@FY4Rjc?#>FfQ3>I*WIY=4}8EymRK%NHyCZN8}
OCmd{;@x-so6HWkYMOCH%

delta 481
zcmZpe!`Lu~aRUpN#QD?H{TUe;7_zx|7hk*>;NAH0Y=W(xPUvPCt~Y{g0X+Mso>JP(
zC)2{js4;o6+;Jv0(aACLSwPNl`D{jw&BhATfijy<E472DVAXVHrbA+z_h{%bF&z|}
z{6;Gcs4`eP87Q}1JDQK{lLgQ!AmC!yEMpbH!t$UdTX=GXT>{GqmKpMs?*JJLtif`V
z9qiTWV?sd+*+2vsq~^g`j0~(`aZ#86kPicl48bMRKn^=pfRQ0J86lTnq6Fj^!{vNa
z5pt=;APZskF)}kG7f*g+uLd+I2`H{M+0a2$`KWvcklhQ_#Kdq^9?E0@1)(UAxC&Ai
z&C2i!$gYB`Lv|knLkbfEgFO&~0F(l=8G+_4W?^7}h7nK>)%@E)IccEz!kaHRm@wms
IIhQA#0DA&g(f|Me

diff --git a/Tools/ArdupilotMegaPlanner/Utilities/CollectionExtensions.cs b/Tools/ArdupilotMegaPlanner/Utilities/CollectionExtensions.cs
new file mode 100644
index 000000000..6b435af40
--- /dev/null
+++ b/Tools/ArdupilotMegaPlanner/Utilities/CollectionExtensions.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace ArdupilotMega.Utilities
+{
+   public static class CollectionExtensions
+   {
+      /// <summary>
+      /// Performs the specified <paramref name="action"/> on each element of the <paramref name="enumerable"/>.
+      /// 
+      /// </summary>
+      /// <param name="enumerable">An enumerable instance.
+      ///             </param><param name="action"/>
+      public static void ForEach(this IEnumerable enumerable, Action<object> action)
+      {
+         foreach (object obj in enumerable)
+            action(obj);
+      }
+
+      /// <summary>
+      /// Performs the specified <paramref name="action"/> on each element of the <paramref name="enumerable"/>.
+      /// 
+      /// </summary>
+      /// <param name="enumerable">An enumerable instance.
+      ///             </param><param name="action"/>
+      public static void ForEach<T>(this IEnumerable enumerable, Action<T> action)
+      {
+         foreach (T obj in enumerable)
+            action(obj);
+      }
+
+      /// <summary>
+      /// Performs the specified <paramref name="action"/> on each element of the <paramref name="enumerable"/>.
+      /// 
+      /// </summary>
+      /// <typeparam name="T">The type contained in the <paramref name="enumerable"/>.
+      ///             </typeparam><param name="enumerable"/><param name="action"/>
+      public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
+      {
+         foreach (T obj in enumerable)
+            action(obj);
+      }
+   }
+}
diff --git a/Tools/ArdupilotMegaPlanner/Utilities/ParameterMetaDataConstants.cs b/Tools/ArdupilotMegaPlanner/Utilities/ParameterMetaDataConstants.cs
index 7f189aa06..fb29584b0 100644
--- a/Tools/ArdupilotMegaPlanner/Utilities/ParameterMetaDataConstants.cs
+++ b/Tools/ArdupilotMegaPlanner/Utilities/ParameterMetaDataConstants.cs
@@ -2,11 +2,23 @@
 {
    public sealed class ParameterMetaDataConstants
    {
-      public const string Delimeter = "@";
+      #region Markers
+
+      public const string ParamDelimeter = "@";
+      public const string PathDelimeter = ",";
       public const string Param = "Param";
+      public const string Group = "Group";
+      public const string Path = "Path";
+
+      #endregion
+
+      #region Meta Keys
+
       public const string DisplayName = "DisplayName";
       public const string Description = "Description";
       public const string Units = "Units";
       public const string Range = "Range";
+
+      #endregion
    }
-}
+}
\ No newline at end of file
diff --git a/Tools/ArdupilotMegaPlanner/Utilities/ParameterMetaDataParser.cs b/Tools/ArdupilotMegaPlanner/Utilities/ParameterMetaDataParser.cs
index 2c3d2c0e1..d4498f0f5 100644
--- a/Tools/ArdupilotMegaPlanner/Utilities/ParameterMetaDataParser.cs
+++ b/Tools/ArdupilotMegaPlanner/Utilities/ParameterMetaDataParser.cs
@@ -7,13 +7,15 @@ using System.Net;
 using System.Text.RegularExpressions;
 using System.Windows.Forms;
 using System.Xml;
+using ArdupilotMega.Utilities;
 using log4net;
 
 namespace ArdupilotMega.Utilities
 {
    public static class ParameterMetaDataParser
    {
-      private static readonly Regex _paramMetaRegex = new Regex(String.Format("{0}(?<MetaKey>[^:]+):(?<MetaValue>.+)", ParameterMetaDataConstants.Delimeter));
+      private static readonly Regex _paramMetaRegex = new Regex(String.Format("{0}(?<MetaKey>[^:]+):(?<MetaValue>.+)", ParameterMetaDataConstants.ParamDelimeter));
+      private static readonly Regex _parentDirectoryRegex = new Regex("(?<ParentDirectory>[../]*)(?<Path>.+)");
       private static readonly ILog log =
          LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
 
@@ -34,59 +36,85 @@ namespace ArdupilotMega.Utilities
 
                foreach (string parameterLocation in parameterLocations)
                {
-                  log.Info(parameterLocation);
+                  // Write the start element for this parameter location
+                  objXmlTextWriter.WriteStartElement(parameterLocation.ToLower().Contains("arducopter") ? MainV2.Firmwares.ArduCopter2.ToString() : MainV2.Firmwares.ArduPlane.ToString());
 
-                  var request = WebRequest.Create(parameterLocation);
+                  // Read and parse the content.
+                  string dataFromAddress = ReadDataFromAddress(parameterLocation);
+                  ParseGroupInformation(dataFromAddress, objXmlTextWriter, parameterLocation);
+                  ParseParameterInformation(dataFromAddress, objXmlTextWriter);
 
-                  // Plenty of timeout
-                  request.Timeout = 10000;
+                  // Write the end element for this parameter location
+                  objXmlTextWriter.WriteEndElement();
 
-                  // Set the Method property of the request to GET.
-                  request.Method = "GET";
-
-                  // Get the response.
-                  using (var response = request.GetResponse())
-                  {
-                     // Display the status.
-                     log.Info(((HttpWebResponse)response).StatusDescription);
+               }
+               
+               // Clear the stream
+               objXmlTextWriter.WriteEndDocument();
+               objXmlTextWriter.Flush();
+               objXmlTextWriter.Close();
+            }
+         }
+      }
 
-                     // Get the stream containing content returned by the server.
-                     using (var dataStream = response.GetResponseStream())
-                     {
-                        if (dataStream != null)
+      /// <summary>
+      /// Parses the group parameter information.
+      /// </summary>
+      /// <param name="fileContents">The file contents.</param>
+      /// <param name="objXmlTextWriter">The obj XML text writer.</param>
+      /// <param name="parameterLocation">The parameter location.</param>
+      private static void ParseGroupInformation(string fileContents, XmlTextWriter objXmlTextWriter, string parameterLocation)
+      {
+         var parsedInformation = ParseKeyValuePairs(fileContents, ParameterMetaDataConstants.Group);
+         if (parsedInformation != null && parsedInformation.Count > 0)
+         {
+            // node is the prefix of the parameter group here
+            parsedInformation.ForEach(node =>
+            {
+               // node.Value is a nested dictionary containing the additional meta data
+               // In this case we are looking for the @Path key
+               if (node.Value != null && node.Value.Count > 0)
+               {
+                  // Find the @Path key
+                  node.Value
+                     .Where(meta => meta.Key == ParameterMetaDataConstants.Path)
+                     // We might have multiple paths to inspect, so break them out by the delimeter
+                     .ForEach(path => path.Value.Split(new []{ ParameterMetaDataConstants.PathDelimeter }, StringSplitOptions.None)
+                        .ForEach(separatedPath =>
                         {
-                           // Open the stream using a StreamReader for easy access.
-                           using (var reader = new StreamReader(dataStream))
+                           // Match based on the regex defined at the top of this class
+                           Match pathMatch = _parentDirectoryRegex.Match(separatedPath);
+                           if (pathMatch.Success && pathMatch.Groups["Path"] != null && !String.IsNullOrEmpty(pathMatch.Groups["Path"].Value))
                            {
-                              // Write the start element for this parameter location
-                              objXmlTextWriter.WriteStartElement(parameterLocation);
+                              if (pathMatch.Groups["ParentDirectory"] != null && !String.IsNullOrEmpty(pathMatch.Groups["ParentDirectory"].Value))
+                              {
+                                 // How many directories from the original path do we have to move
+                                 int numberOfParentDirectoryMoves = pathMatch.Groups["ParentDirectory"].Value.Split(new string[] { "../" }, StringSplitOptions.None).Count();
 
-                              // Read and parse the content.
-                              ParseParameterInformation(reader.ReadToEnd(), objXmlTextWriter);
+                                 // We need to remove the http:// or https:// prefix to build the new URL properly
+                                 string httpTest = parameterLocation.Substring(0, 7);
+                                 int trimHttpPrefixIdx = httpTest == "http://" ? 7 : 8;
 
-                              // Write the end element for this parameter location
-                              objXmlTextWriter.WriteEndElement();
+                                 // Get the parts of the original URL
+                                 var parameterLocationParts = parameterLocation.Substring(trimHttpPrefixIdx, parameterLocation.Length - trimHttpPrefixIdx).Split(new char[] { '/' });
 
-                              // Close the reader
-                              reader.Close();
-                           }
+                                 // Rebuild the new url taking into account the numberOfParentDirectoryMoves
+                                 string urlAfterParentDirectory = string.Empty;
+                                 for (int i = 0; i < parameterLocationParts.Length && i < parameterLocationParts.Length - numberOfParentDirectoryMoves; i++)
+                                 {
+                                    urlAfterParentDirectory += parameterLocationParts[i] + "/";
+                                 }
 
-                           // Close the datastream
-                           dataStream.Close();
-                        }
-                     }
-
-                     // Close the response
-                     response.Close();
-                  }
+                                 // This is the URL of the file we need to parse for comments
+                                 string newPath = String.Format("{0}{1}{2}", parameterLocation.Substring(0, trimHttpPrefixIdx), urlAfterParentDirectory, pathMatch.Groups["Path"].Value);
 
+                                 // Parse the param info from the newly constructed URL
+                                 ParseParameterInformation(ReadDataFromAddress(newPath), objXmlTextWriter, node.Key);
+                              }
+                           }                                         
+                        }));
                }
-               
-               // Clear the stream
-               objXmlTextWriter.WriteEndDocument();
-               objXmlTextWriter.Flush();
-               objXmlTextWriter.Close();
-            }
+            });
          }
       }
 
@@ -97,8 +125,50 @@ namespace ArdupilotMega.Utilities
       /// <param name="objXmlTextWriter">The obj XML text writer.</param>
       private static void ParseParameterInformation(string fileContents, XmlTextWriter objXmlTextWriter)
       {
+         ParseParameterInformation(fileContents, objXmlTextWriter, string.Empty);
+      }
+
+      /// <summary>
+      /// Parses the parameter information.
+      /// </summary>
+      /// <param name="fileContents">The file contents.</param>
+      /// <param name="objXmlTextWriter">The obj XML text writer.</param>
+      /// <param name="parameterPrefix">The parameter prefix.</param>
+      private static void ParseParameterInformation(string fileContents, XmlTextWriter objXmlTextWriter, string parameterPrefix)
+      {
+         var parsedInformation = ParseKeyValuePairs(fileContents, ParameterMetaDataConstants.Param);
+         if(parsedInformation != null && parsedInformation.Count > 0)
+         {
+            parsedInformation.ForEach(node =>
+            {
+               objXmlTextWriter.WriteStartElement(String.Format("{0}{1}", parameterPrefix, node.Key));
+               if (node.Value != null && node.Value.Count > 0)
+               {
+                  node.Value.ForEach(meta =>
+                  {
+                     // Write the key value pair to XML
+                     objXmlTextWriter.WriteStartElement(meta.Key);
+                     objXmlTextWriter.WriteString(meta.Value);
+                     objXmlTextWriter.WriteEndElement();                 
+                  });
+               }  
+               objXmlTextWriter.WriteEndElement();
+            });
+         }
+      }
+
+      /// <summary>
+      /// Parses the parameter information.
+      /// </summary>
+      /// <param name="fileContents">The file contents.</param>
+      /// <param name="nodeKey">The node key.</param>
+      /// <returns></returns>
+      private static Dictionary<string, Dictionary<string, string>> ParseKeyValuePairs(string fileContents, string nodeKey)
+      {
+         var returnDict = new Dictionary<string, Dictionary<string, string>>();
+
          var indicies = new List<int>();
-         GetIndexOfMarkers(ref indicies, fileContents, ParameterMetaDataConstants.Delimeter + ParameterMetaDataConstants.Param, 0);
+         GetIndexOfMarkers(ref indicies, fileContents, ParameterMetaDataConstants.ParamDelimeter + nodeKey, 0);
 
          if(indicies.Count > 0)
          {
@@ -113,7 +183,7 @@ namespace ArdupilotMega.Utilities
                if(!String.IsNullOrEmpty(subStringToSearch))
                {
                   var metaIndicies = new List<int>();
-                  GetIndexOfMarkers(ref metaIndicies, subStringToSearch, ParameterMetaDataConstants.Delimeter, 0);
+                  GetIndexOfMarkers(ref metaIndicies, subStringToSearch, ParameterMetaDataConstants.ParamDelimeter, 0);
 
                   if(metaIndicies.Count > 0)
                   {
@@ -123,40 +193,43 @@ namespace ArdupilotMega.Utilities
                      // Match based on the regex defined at the top of this class
                      Match paramNameKeyMatch = _paramMetaRegex.Match(paramNameKey);
 
-                     if (paramNameKeyMatch.Success && paramNameKeyMatch.Groups["MetaKey"].Value == ParameterMetaDataConstants.Param)
+                     if (paramNameKeyMatch.Success && paramNameKeyMatch.Groups["MetaKey"].Value == nodeKey)
                      {
-                        objXmlTextWriter.WriteStartElement(paramNameKeyMatch.Groups["MetaValue"].Value.Trim(new char[] { ' ' }));
-
-                        // Loop through the indicies of the meta data found
-                        for (int x = 1; x < metaIndicies.Count; x++)
+                        string key = paramNameKeyMatch.Groups["MetaValue"].Value.Trim(new char[] {' '});
+                        var metaDict = new Dictionary<string, string>();
+                        if(!returnDict.ContainsKey(key))
                         {
-                           // This is the end index for a substring to search for parameter attributes
-                           // If we are on the last index in our collection, we will search to the end of the file
-                           var stopMetaIdx = (x == metaIndicies.Count - 1) ? subStringToSearch.Length : metaIndicies[x + 1];
+                           // Loop through the indicies of the meta data found
+                           for (int x = 1; x < metaIndicies.Count; x++)
+                           {
+                              // This is the end index for a substring to search for parameter attributes
+                              // If we are on the last index in our collection, we will search to the end of the file
+                              var stopMetaIdx = (x == metaIndicies.Count - 1) ? subStringToSearch.Length : metaIndicies[x + 1];
 
-                           // This meta param string
-                           var metaString = subStringToSearch.Substring(metaIndicies[x], (stopMetaIdx - metaIndicies[x]));
+                              // This meta param string
+                              var metaString = subStringToSearch.Substring(metaIndicies[x], (stopMetaIdx - metaIndicies[x]));
 
-                           // Match based on the regex defined at the top of this class
-                           Match metaMatch = _paramMetaRegex.Match(metaString);
+                              // Match based on the regex defined at the top of this class
+                              Match metaMatch = _paramMetaRegex.Match(metaString);
 
-                           // Test for success
-                           if (metaMatch.Success)
-                           {
-                              // Write the key value pair to XML
-                              objXmlTextWriter.WriteStartElement(metaMatch.Groups["MetaKey"].Value.Trim(new char[] { ' ' }));
-                              objXmlTextWriter.WriteString(metaMatch.Groups["MetaValue"].Value.Trim(new char[] { ' ' }));
-                              objXmlTextWriter.WriteEndElement();
+                              // Test for success
+                              if (metaMatch.Success)
+                              {
+                                 string metaKey = metaMatch.Groups["MetaKey"].Value.Trim(new char[] {' '});
+                                 if(!metaDict.ContainsKey(metaKey))
+                                 {
+                                    metaDict.Add(metaKey, metaMatch.Groups["MetaValue"].Value.Trim(new char[] { ' ' }));
+                                 }
+                              }
                            }
                         }
-
-                        // End this parameter node
-                        objXmlTextWriter.WriteEndElement();
+                        returnDict.Add(key, metaDict);
                      }
                   }
                }
             }
          }
+         return returnDict;
       }
 
       /// <summary>
@@ -188,5 +261,67 @@ namespace ArdupilotMega.Utilities
             }
          }
       }
+
+      /// <summary>
+      /// Reads the data from address.
+      /// </summary>
+      /// <param name="address">The address.</param>
+      /// <returns></returns>
+      private static string ReadDataFromAddress(string address)
+      {
+         string data = string.Empty;
+
+         log.Info(address);
+
+         // Make sure we don't blow up if the user is not connected or the endpoint is not available
+         try
+         {
+            var request = WebRequest.Create(address);
+
+            // Plenty of timeout
+            request.Timeout = 10000;
+
+            // Set the Method property of the request to GET.
+            request.Method = "GET";
+
+            // Get the response.
+            using (var response = request.GetResponse())
+            {
+               // Display the status.
+               log.Info(((HttpWebResponse)response).StatusDescription);
+
+               // Get the stream containing content returned by the server.
+               using (var dataStream = response.GetResponseStream())
+               {
+                  if (dataStream != null)
+                  {
+                     // Open the stream using a StreamReader for easy access.
+                     using (var reader = new StreamReader(dataStream))
+                     {
+                        // Store the data to return
+                        data = reader.ReadToEnd();
+
+                        // Close the reader
+                        reader.Close();
+                     }
+
+                     // Close the datastream
+                     dataStream.Close();
+                  }
+               }
+
+               // Close the response
+               response.Close();
+            }
+
+            // Return the data
+            return data;
+         }
+         catch (WebException ex)
+         {
+            log.Error(String.Format("The request to {0} failed.", address), ex);
+         }
+         return string.Empty;
+      }
    }
 }
diff --git a/Tools/ArdupilotMegaPlanner/Utilities/ParameterMetaDataRepository.cs b/Tools/ArdupilotMegaPlanner/Utilities/ParameterMetaDataRepository.cs
index 0697a76c0..738e68488 100644
--- a/Tools/ArdupilotMegaPlanner/Utilities/ParameterMetaDataRepository.cs
+++ b/Tools/ArdupilotMegaPlanner/Utilities/ParameterMetaDataRepository.cs
@@ -3,6 +3,7 @@ using System.Configuration;
 using System.IO;
 using System.Windows.Forms;
 using System.Xml.Linq;
+using System.Linq;
 
 namespace ArdupilotMega.Utilities
 {
@@ -32,7 +33,19 @@ namespace ArdupilotMega.Utilities
          {
             // Use this to find the endpoint node we are looking for
             // Either it will be pulled from a file in the ArduPlane hierarchy or the ArduCopter hierarchy
-            string endpointSearchString = (MainV2.cs.firmware == MainV2.Firmwares.ArduPlane) ? "arduplane" : "arducopter";
+            var element = _parameterMetaDataXML.Element(MainV2.cs.firmware.ToString());
+            if(element != null && element.HasElements)
+            {
+               var node = element.Element(nodeKey);
+               if(node != null && node.HasElements)
+               {
+                  var metaValue = node.Element(metaKey);
+                  if(metaValue != null)
+                  {
+                     return metaValue.Value;
+                  }
+               }
+            }
          }
          return string.Empty;
       }
diff --git a/Tools/ArdupilotMegaPlanner/app.config b/Tools/ArdupilotMegaPlanner/app.config
index 063dfdef7..9bb4229ef 100644
--- a/Tools/ArdupilotMegaPlanner/app.config
+++ b/Tools/ArdupilotMegaPlanner/app.config
@@ -8,6 +8,10 @@
   <appSettings>
     <add key="UpdateLocation"
       value="http://ardupilot-mega.googlecode.com/git/Tools/ArdupilotMegaPlanner/bin/Release/"/>
+    <add key="ParameterLocations"
+      value="http://ardupilot-mega.googlecode.com/git/ArduCopter/Parameters.pde"/>
+    <add key="ParameterMetaDataXMLFileName"
+      value="ParameterMetaData.xml"/>
   </appSettings>
   <log4net>
     <appender name="Console" type="log4net.Appender.ConsoleAppender">
diff --git a/Tools/ArdupilotMegaPlanner/bin/Release/ArduCopterConfig.xml b/Tools/ArdupilotMegaPlanner/bin/Release/ArduCopterConfig.xml
index 2558e46e2..fda38f678 100644
--- a/Tools/ArdupilotMegaPlanner/bin/Release/ArduCopterConfig.xml
+++ b/Tools/ArdupilotMegaPlanner/bin/Release/ArduCopterConfig.xml
@@ -101,6 +101,70 @@ Too high = slow wobbles
         <STEP>0.001</STEP>
       </FIELD>
     </FIELDS>
+
+    <SUBHEAD>Yaw Angular Rate Control:</SUBHEAD>
+    <DESC>How much throttle is applied to rotate the copter at the desired speed.
+    </DESC>
+    <FIELDS>
+      <FIELD>
+        <NAME>P</NAME>
+        <PARAMNAME>RATE_YAW_P</PARAMNAME>
+        <RANGEMIN>0.001</RANGEMIN>
+        <RANGEMAX>5</RANGEMAX>
+        <STEP>0.001</STEP>
+      </FIELD>
+      <FIELD>
+        <NAME>I</NAME>
+        <PARAMNAME>RATE_YAW_I</PARAMNAME>
+        <RANGEMIN>0</RANGEMIN>
+        <RANGEMAX>5</RANGEMAX>
+        <STEP>0.001</STEP>
+      </FIELD>
+      <FIELD>
+        <NAME>D</NAME>
+        <PARAMNAME>RATE_YAW_D</PARAMNAME>
+        <RANGEMIN>0</RANGEMIN>
+        <RANGEMAX>5</RANGEMAX>
+        <STEP>0.001</STEP>
+      </FIELD>
+      <FIELD>
+        <NAME>IMAX</NAME>
+        <PARAMNAME>RATE_YAW_IMAX</PARAMNAME>
+        <RANGEMIN>0</RANGEMIN>
+        <RANGEMAX>50</RANGEMAX>
+        <STEP>1</STEP>
+      </FIELD>
+    </FIELDS>
+
+    <SUBHEAD>Yaw Stabilize Control:</SUBHEAD>
+    <DESC>
+      How fast the copter reacts to user or autopilot input.
+      Higher = more aggressive control.
+      Too high = slow wobbles
+    </DESC>
+    <FIELDS>
+      <FIELD>
+        <NAME>P</NAME>
+        <PARAMNAME>STB_YAW_P</PARAMNAME>
+        <RANGEMIN>0.001</RANGEMIN>
+        <RANGEMAX>10</RANGEMAX>
+        <STEP>0.001</STEP>
+      </FIELD>
+      <FIELD>
+        <NAME>I</NAME>
+        <PARAMNAME>STB_YAW_I</PARAMNAME>
+        <RANGEMIN>0</RANGEMIN>
+        <RANGEMAX>5</RANGEMAX>
+        <STEP>0.001</STEP>
+      </FIELD>
+      <FIELD>
+        <NAME>IMAX</NAME>
+        <PARAMNAME>STB_YAW_IMAX</PARAMNAME>
+        <RANGEMIN>0</RANGEMIN>
+        <RANGEMAX>50</RANGEMAX>
+        <STEP>1</STEP>
+      </FIELD>
+    </FIELDS>
   </Item>
   <!-- Loiter -->
   <Item>
@@ -140,7 +204,7 @@ How much angle is applied to make the copter accelerate to the desired speed.
         <PARAMNAME>LOITER_LON_IMAX</PARAMNAME>
         <RANGEMIN>0</RANGEMIN>
         <RANGEMAX>50</RANGEMAX>
-        <STEP>0.1</STEP>
+        <STEP>1</STEP>
       </FIELD>
     </FIELDS>
     <SUBHEAD>Loiter Speed:</SUBHEAD>
diff --git a/Tools/ArdupilotMegaPlanner/bin/Release/version.txt b/Tools/ArdupilotMegaPlanner/bin/Release/version.txt
index cf279dbac..b68b098b7 100644
--- a/Tools/ArdupilotMegaPlanner/bin/Release/version.txt
+++ b/Tools/ArdupilotMegaPlanner/bin/Release/version.txt
@@ -1 +1 @@
-1.1.4498.32482
\ No newline at end of file
+1.1.4499.14296
\ No newline at end of file
-- 
GitLab