Thursday 30 June 2011

Arduino - Triggered Function Generator

This is a triggered function generator / pulse generator for the Arduino Mega using Arduino 022.

The sketch is too long to present as a snippet, but it's downloadable from here.

Features


  • Configurable via commands from the Arduino IDE Serial Monitor.
  • Triggered by external digital input.
  • Output sequence includes an optional pulse of configurable duration (in ms) starting at lag 0.
  • Main output sequence has configurable start time and duration (in ms).
  • Includes a configurable internal test source which can be looped to the trigger input.
  • Configuration can be saved to EEPROM.
  • Automatically loads configuration from EEPROM at startup.
  • At the moment this design outputs a delayed pulse, but it can easily be extended to output any arbitrary digital waveform (function), i.e. to be a digital AWG (Arbitrary Waveform Generator).
  • With the addition of provision for analog output, either using PWM functionality or an external DAC, it could be easily extended to make an analog AWG.

Commands


The commands currently implemented are:

    BEEP ON/OFF       Switch beep on/off (stimulus input)
    HELP              Display help information (this command)
    INTMODE (string)  Set the interrupt mode
                      (options are LOW / CHANGE / RISING / FALLING)
    LAG (int)         Set the lag time (ms)
    MARKER (int)      Set the marker length (ms)
    OFF               Disable the output
    ON                Enable the output
    OUTPUT (int)      Set the output length (ms)
    PULSE (int)       Set the pulse length (ms)
    RESET             Reset to default values
    RESTORE           Restore configuration from EEPROM
    SAMPLE (int)      Set the sample frequency (KHz)
    SAVE              Save configuration to EEPROM
    STATUS            Display status information
    TEST (int)        Set the test stimulus interval on pin 12 (ms)

(int) signifies an integer parameter, separated by a space.
(string) signifies a string parameter, separated by a space.

Performance


Some screenshots of example output between 50 and 1000 ms lag:


50 ms lag


100 ms lag


200 ms lag


500 ms lag


1000 ms lag

The upper trace is the stimulus input, the positive edge of which triggers both the function generator and the oscilloscope.

Description


If anyone's interested, I can add some annotated snippets here describing the code.

Acknowledgements


Interrupt usage and tips in speaker_pcm by Michael Smith (michael@hurts.ca).

C# - String Helpers - Various

Usage


Various short and fairly self-explanatory string helpers.

Snippets


using System;
using System.Drawing;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;


namespace My.Helpers
{
    public class StringHelpers
    {
        public static int Count(string text, string pattern)
        {
            // Count how many occurrences of 'pattern' there are in 'text'.
            int result = 0;

            try
            {
                int start = 0;

                while (true)
                {
                    int index = text.IndexOf(pattern, start);
                    if (index < 0) break;

                    result++;
                    start = index + pattern.Length;
                }
            }
            catch (System.Exception Ex)
            {
                Log.WriteException("StringHelpers", "Count", Ex);
            }

            return result;
        }

        public static string Get(string text, string defaultValue)
        {
            if (!String.IsNullOrWhiteSpace(text))
                return text;
            else
                return defaultValue;
        }

        public static string GetFirstLine(string text)
        {
            string result = "";

            if (!String.IsNullOrWhiteSpace(text))
            {
                int index = text.IndexOf(Environment.NewLine);

                if (index < 0)
                    result = text;
                else
                    result = text.Substring(0, index);
            }

            return result;
        }

        public static string GetMD5Hash(string text)
        {
            var x = new MD5CryptoServiceProvider();
            byte[] bs = Encoding.UTF8.GetBytes(text);

            // Compute the MD5 hash value.
            bs = x.ComputeHash(bs);

            // Convert it to a hex string.
            var s = new System.Text.StringBuilder();

            foreach (byte b in bs)
                s.Append(b.ToString("x2").ToLower());

            return s.ToString();
        }

        public static string RegexExtract(string text, string pattern)
        {
            // Return the first part of 'text' that matches 'pattern' (if any), otherwise an empty string.
            try
            {
                Regex rx = new Regex(pattern);
                Match match = rx.Match(text);

                if (match.Success)
                    return match.Value;
            }
            catch (System.Exception Ex)
            {
                Log.WriteException("ServiceTester", "RegexMatch", Ex);
            }

            return "";
        }

        public static bool RegexMatch(string text, string pattern)
        {
            // Does 'text' match 'pattern'?
            try
            {
                Regex rx = new Regex(pattern);      // , RegexOptions.Compiled | RegexOptions.IgnoreCase);
                return rx.IsMatch(text);
            }
            catch (Exception Ex)
            {
                Log.WriteException("ServiceTester", "RegexMatch", Ex);
            }

            return false;
        }

        public static string Repeat(char ch, int count)
        {
            return new String(ch, count);
        }

        public static string Repeat(string text, int count)
        {
            var builder = new StringBuilder();

            for (int i = 0; i < count; i++)
                builder.Append(text);

            return builder.ToString();
        }

        public static string Tabify(string text, int tabWidth)
        {
            // Replaces each sequence of 'tabWidth' spaces with a tab.
            // Ignores smaller numbers of spaces (similar to VS2010).
            if (String.IsNullOrWhiteSpace(text))
                return "";

            string spaces = new String(' ', tabWidth);

            return text.Replace(spaces, "\t");
        }

        public static string Untabify(string text, int tabWidth)
        {
            if (String.IsNullOrWhiteSpace(text))
                return "";

            string spaces = new String(' ', tabWidth);

            return text.Replace("\t", spaces);
        }
    }
}

C# - String Extensions - Various

Usage


Various fairly self-explanatory short string extensions.

Snippet


using System;
using System.Collections.Generic;
using System.Linq;

namespace My.Helpers
{
    // String extension methods.

    public static class StringExtensions
    {
        public static int CompareTrimmed(this string text, string str, bool ignoreCase = true)
        {
            return String.Compare(text.Trim(), str.Trim(), ignoreCase);
        }

        public static bool ContainsIgnoreCase(this string text, string value)
        {
            return (text.IndexOf(value, System.StringComparison.OrdinalIgnoreCase) > 0);
        }

        public static bool ContainsIgnoreCase(this string text, string value, out int index)
        {
            index = text.IndexOf(value, System.StringComparison.OrdinalIgnoreCase);
            return (index >= 0);
        }

        public static bool EndsWithIgnoreCase(this string text, string value)
        {
            return text.EndsWith(value, System.StringComparison.CurrentCultureIgnoreCase);
        }

        public static string Left(this string text, int maxLength)
        {
            if (maxLength <= 0)
                return "";

            int count = Math.Min(maxLength, text.Length);

            return text.Substring(0, count);
        }

        public static string ReplaceIgnoreCase(this string text, string value1, string value2)
        {
            string work = text;
            int start = 0;
            var caseInsensitive = System.StringComparison.CurrentCultureIgnoreCase;

            while (true)
            {
                int i = work.IndexOf(value1, start, caseInsensitive);
                if (i < 0) break;

                work = work.Left(i) + value2 + work.Right(work.Length - i - value1.Length);

                start += value2.Length;
            }

            return work;
        }

        public static string Right(this string text, int maxLength)
        {
            if (maxLength <= 0)
                return "";

            int start = Math.Max(text.Length - maxLength, 0);
            int count = Math.Min(maxLength, text.Length - start);
            
            return text.Substring(start, count);
        }

        public static bool StartsWithIgnoreCase(this string text, string value)
        {
            return text.StartsWith(value, System.StringComparison.CurrentCultureIgnoreCase);
        }

        public static string StripNonNumeric(this string text)
        {
            return text.StripNonNumeric(false);
        }

        public static string StripNonNumeric(this string text, bool stopAtFirst)
        {
            string result = "";

            foreach (var c in text)
            {
                if ((c.CompareTo('0') >= 0) && (c.CompareTo('9') <= 0))
                    result += c;
                else
                {
                    if (stopAtFirst)
                        break;
                }
            }

            return result;
        }

        public static byte[] ToByteArray(this string text)
        {
            int count = text.Length;
            byte[] result = new byte[count];

            for (int i = 0; i < count; i++)
                result[i] = (byte)text[i];

            return result;
        }

        public static string ToCsv(this string text)
        {
            // Converts comma-delimited or space-delimited to comma-delimited.
            string[] fields;

            if (text.Contains(",") || !text.Contains(" "))
                return text;

            // 'text' contains one or more spaces.
            string result = "";
            fields = text.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

            foreach (string field in fields)
            {
                if (result != "")
                    result += ",";

                result += field.Trim();
            }

            return result;
        }

        public static int[] ToIntArray(this string text)
        {
            return Array.ConvertAll<string, int>(text.ToCsv().ToStringArray(), int.Parse);
        }

        public static List<int> ToListInt(this string text)
        {
            return text.ToIntArray().ToList<int>();
        }

        public static string[] ToStringArray(this string text)
        {
            string[] result = text.ToCsv().Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

            for (int i = 0; i < result.Count(); i++)
                result[i] = result[i].Trim();

            return result;
        }

        public static List<string> ToListString(this string text)
        {
            return text.ToStringArray().ToList<string>();
        }
    }
}

C# - String Extensions - AddMarkup

Usage


string foo = "I got this from Just Snippets!";
string bar = foo.AddMarkup("Just Snippets", "", "");

would set bar to "I got this from Just Snippets!".

Snippet


namespace My.Helpers
{
    // String extension methods.

    public static class StringExtensions
    {
        public static string AddMarkup(this string text, string value, string before, string after, bool ignoreCase = true)
        {
            string work = text;
            int start = 0;
            System.StringComparison caseSensitivity;
            int bump = value.Length + before.Length + after.Length;

            if (ignoreCase)
                caseSensitivity = System.StringComparison.CurrentCultureIgnoreCase;
            else
                caseSensitivity = System.StringComparison.CurrentCulture;

            while (true)
            {
                int i = work.IndexOf(value, start, caseSensitivity);
                if (i < 0) break;

                work = work.Left(i) + before + work.Substring(i, value.Length) + after
                    + work.Right(work.Length - i - value.Length);

                start += bump;
            }

            return work;
        }

C# - Byte Array Extensions - ToHexString

using System.Text;

namespace My.Helpers
{
    public static class ByteExtensions
    {
        public static string ToHexString(this byte[] array)
        {
            // Create a new StringBuilder to collect the bytes and create a string.
            var sBuilder = new StringBuilder();

            // Convert to a hex string.
            for (int i = 0; i < array.Length; i++)
                sBuilder.Append(array[i].ToString("x2"));

            // Return the hex string.
            return sBuilder.ToString();
        }
    }
}

ASP.NET MVC - Admin Helpers

A small helper to add or remove a role from an ASP.NET user.

using System.Web.Security;

namespace My.Helpers
{
    public class AdminHelpers
    {
        public static bool SetRole(string userName, string roleName, bool newState)
        {
            bool currState = Roles.IsUserInRole(userName, roleName);

            if (newState != currState)
            {
                if (newState)
                {
                    // Add the 'roleName' role to this user.
                    Roles.AddUserToRole(userName, roleName);
                }
                else
                {
                    // Remove the 'roleName' role from this user.
                    Roles.RemoveUserFromRole(userName, roleName);
                }
            }

            return true;
        }
    }
}

ASP.NET MVC Mini-Profiler - Linq to SQL

This is a useful free profiler for ASP.NET MVC from the people at StackOverflow.com.

Some advice re. usage is in my previous post.

This post shows one way to use it in ASP.NET MVC with Linq to SQL.


In A DbRepository


I'm assuming a repository-based approach similar to NerdDinner.

I usually let my repository classes share a 'DataContext' - each controller typically has a main repository which creates the data context as usual, but other repositories needed in the controller actions are created using the second constructor below, and therefore use the first repository's data context.

    public class MyDbRepository : IMyRepository
    {
        public MyDataContext DataContext { get; protected set; }

        // Constructors.

        public MyDbRepository()
        {
            MiniProfiler profiler = MiniProfiler.Current;

            // Grab the SQL connection string (or could get it from web.config, for example).
            DataContext = new MyDataContext();
            string connString = DataContext.Connection.ConnectionString;

            // Setup a profiled connection.
            var conn = new SqlConnection(connString);
            var profiledConn = MvcMiniProfiler.Data.ProfiledDbConnection.Get(conn, profiler);

            // Create another data context using the profiled connection.
            DataContext = new MyDataContext(profiledConn);
        }

        public MyDbRepository(IMyRepository repos)
        {
            // Repositories can share a data context object.
            DataContext = repos.DataContext;
        }

        // Public methods.

        // +++

    }

Wednesday 29 June 2011

ASP.NET MVC Mini-Profiler - First Impressions

This is a useful free profiler for ASP.NET MVC from the people at StackOverflow.com (a wonderful source of answers, and built using ASP.NET MVC).

The easiest way to install it in VS2010 is using NuGet

These snippets show one way to use it.

Site.Master (.ASPX view engine)

<script src="<%: Url.Content("https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js") %>"  type="text/javascript"></script>
<%= MvcMiniProfiler.MiniProfiler.RenderIncludes() %> 

Global.asax.cs

A simple approach is to start and stop the profiler for each separate web request, for example:

using MvcMiniProfiler; 

    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_BeginRequest()
        {
            if (Request.IsLocal)
                MiniProfiler.Start();
        }
        
        protected void Application_EndRequest()
        {
            if (Request.IsLocal)
                MiniProfiler.Stop();
        }
    }

In A Controller, for example


Create a separate MiniProfiler 'step' for any long processing steps that you want to profile:

var profiler = MiniProfiler.Current;
 
    // No need to check 'profiler' for null here, it's handled in profiler.Step()

    using (profiler.Step("Search"))
    {
        // Time-consuming stuff...
        DoLongSearch(xxx);
    }

Steps can be nested, and the profile output will show the step structure:

    using (profiler.Step("SearchAndUpdate"))
    {
        using (profiler.Step("Search"))
        {
            // Time-consuming stuff...
            DoSearch(xxx);
        }

        using (profiler.Step("Update"))
        {
            // Time-consuming stuff...
            DoUpdate(xxx);
        }
    }

Viewing the Output


When you navigate to a URL that causes the profiler to be called, the resulting view will contain a small MiniProfiler gadget at the top left. Click on it to get the timing detail.

Linq to SQL


You can use the MiniProfiler to profile database calls, including Linq to SQL - see this post. The profile information includes the elapsed time of each query and also warns of 'duplicate' SQL queries, both of which can help in optimizing application design.