Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

NATO Phonetics

3.90/5 (6 votes)
6 Jul 2009CPOL11 min read 38.1K   343  
Simple breaking-down of words into a series of NATO Phonetics to aid verbal communication.

Image 1

Introduction

This article is about compensating for verbal communication misunderstandings; sounds deceptive, but simple and easy. To give an example, suppose you are administering a system remotely, you need to check a command by talking to a technician on the other end of the telephone, and you are unsure if you got the spelling right maybe due to cultural aspects or an illness affliction (think - head cold which can dramatically alter your speech/accent/pronunciation) - you could end up making a mistake and possibly creating even more work.

Amendment (5th July 2009): As PIEBALDconsult has pointed out, the article shall also reflect how to solve this problem using Generics. I took the long way as to show how the problem can be solved instead of going down the shorter route.

To quote from Wikipedia's article on NATO Phonetics "The NATO phonetic alphabet, more formally the international radiotelephony spelling alphabet, is the most widely used spelling alphabet".

By employing the use of NATO Phonetics when spelling out such a command could save a lot of potential headaches. The above can also be used in other situations such as spelling your name or address.

Ok, it may not be a lot useful today since communication means have advanced with options like SMS, Instant PC Messaging, and Skype!

But it can be a useful backup should verbal communication over the phone fail due to different pronunciations, accents, speech impediments, and illnesses.

Background

NATO Phonetics is designed to make spelling easier by associating each letter of the alphabet with a to-use word.

Imagine trying to spell out on the phone 'rm -rf /', which is an example that I want to emphasize for how confusions can occur. To the receiver, it may sound like 'on -of /' or 'om -os', you just would not know! Personally, with my being deaf, I have difficulty with the letters 'r' and 'w'; this is what spurred me into the creation of this article. It uses a phonetic word instead of a letter, that is spoken instead; for the above example:

  • 'r' is 'Romeo'
  • 'm' is 'mike'
  • space is '<space>'
  • dash is '<dash>'
  • 'r' is 'romeo'
  • 'f' is 'foxtrot'
  • space is '<space'>
  • slash is '<forward-slash>'

Hence, the spoken words would be: "Romeo Mike space dash romeo foxtrot space forward-slash" implying 'rm -rf /' as in a destructive wiping of files under Unix.

Using the Code

Here, I will give a brief overview and description of the following for discussion in this article:

  • Collection used? Source in NATOListHandler.cs
  • Handling the collection. Source in NATOListHandler.cs
  • String resources. Source in NATOResx.resx
  • Parsing. Source in frmNATO.cs

After that, a small discussion of the user interface will be thrown into the mix:

  • Inputting the series of words
  • Using BackgroundWorker and ListView
  • Using the menu strip and status bar

All of the source can be found in frmNATO.cs.

Collection used?

The collection employed is a generic dictionary, see the example below in Figure 1.:

C#
private System.Collections.Generic.Dictionary<NATOPhoneticItemKey, 
        NATOPhoneticItem> _dicList;
Figure 1.

The key is of type NATOPhoneticKey; its respective and associated value is of type NATOPhoneticItem. See Figure 2 below for the implementation of both classes.

C#
public class NATOPhoneticItemKey {
    private char _chLetter;
    private string _sLetterKey;
    //
    public NATOPhoneticItemKey(char chLetter) {
        this._chLetter = chLetter;
        this._sLetterKey = 
          string.Format("NATOItmKey: {0}", this._chLetter);
    }
    //
    public char KeyLetter {
        get { return this._chLetter; }
    }
    //
    #region Override's...
    public override bool Equals(object obj) {
        NATOPhoneticItemKey rhs = 
          (NATOPhoneticItemKey)obj as NATOPhoneticItemKey;
        if (rhs != null) {
            if (this._sLetterKey.Equals(rhs._sLetterKey)) return true;
        }
        return false;
    }
    //
    public override int GetHashCode() {
        return this._sLetterKey.GetHashCode();
    }
    //
    public override string ToString() {
        return this._sLetterKey;
    }
    #endregion
}

public class NATOPhoneticItem {
    private NATOPhoneticItemKey _key;
    private string _sPhonetic;
    private string _sItemKey;
    //
    public NATOPhoneticItem(NATOPhoneticItemKey key, string sPhonetic) {
        this._key = key;
        this._sPhonetic = sPhonetic;
        this._sItemKey = string.Format("NATOItm: {0};{1}", 
                                this._key.KeyLetter, this._sPhonetic);
    }
    //
    public NATOPhoneticItemKey Key {
        get { return this._key; }
    }
    //
    public string Phonetic {
        get { return this._sPhonetic; }
    }
    //
    #region Override's...
    public override bool Equals(object obj) {
        NATOPhoneticItem rhs = 
           (NATOPhoneticItem)obj as NATOPhoneticItem;
        if (rhs != null) {
            if (this._key.Equals(rhs._key)) return true;
        }
        return false;
    }
    //
    public override int GetHashCode() {
        return this._sItemKey.GetHashCode();
    }
    //
    public override string ToString() {
        return this._sItemKey;
    }
    #endregion
}
Figure 2.

Both of the types mentioned above are strongly typed. It is important to remember when deciding to use a collection, especially for lookups, that the Equals, GetHashCode, and ToString are overridden with your implementation; otherwise, the lookup will fail.

The reason I used the generic dictionary was to make it a quick and easy way to perform a lookup based on a character used.

  • NATOPhoneticItemKey holds an alpha numeric letter and punctuations [A-Z,0-9] etc.
  • NATOPhoneticItem holds the key and its associated phonetic.

Handling the Collection

There is a class NATOListHandler that looks after the creation and the loading of the dictionary. There is one method for Loading the dictionary and the function used to perform the lookups, namely LookUp - See Figure 3 below for implementation of both Load and LookUp.

C#
public void Load(NATOPhoneticItemKey key, NATOPhoneticItem item) {
    if (this._dicList == null)
        this._dicList = new Dictionary<NATOPhoneticItemKey,
                            NATOPhoneticItem>();
    this._dicList.Add((NATOPhoneticItemKey)key, 
                      (NATOPhoneticItem)item);
}

public NATOPhoneticItem LookUp(char letter) {
    NATOPhoneticItemKey key = new NATOPhoneticItemKey(letter);
    if (this._dicList.ContainsKey(
        new NATOPhoneticItemKey(letter))) return this._dicList[key];
    return null;
}
Figure 3.

In Figure 3, for the function LookUp, we are checking to see if the letter is in the dictionary by instantiating a new NATOPhoneticItemKey with the letter used. If the ContainsKey function returns true, we return back the NATOPhoneticItem found in the dictionary; otherwise, we return back a null.

String Resources

There are three string resources embedded into the application:

  • Alphabetical - letters from 'A' to 'Z'
  • Numerical - numbers from '0' to '9'
  • Punctuation - such as asterisk, backslash, forward-slash, comma etc.

Each of the string resources are semicolon delimited as for a simple reason, to make parsing easier, which will be covered in the next section below.

The code that is used for loading the resource strings before parsing is shown below in Figure 4, in the method LoadFromResource:

C#
/// <summary>
/// Extracts the resource and build up the dictionary of the entries
/// </summary>
private void LoadFromResource() {
    string sRes = 
      System.Reflection.Assembly.GetExecutingAssembly().GetName().Name +
      ".NATORes";
    System.Resources.ResourceManager rm = 
      new System.Resources.ResourceManager(sRes,
          System.Reflection.Assembly.GetExecutingAssembly());
    if (rm != null) {
        string sDataRes = rm.GetString("StringAlphaZulu");
        this.BuildUpList(sDataRes);
        //
        sDataRes = rm.GetString("StringZeroNine");
        this.BuildUpList(sDataRes);
        //
        sDataRes = rm.GetString("StringOther");
        this.BuildUpList(sDataRes);
        //
        rm.ReleaseAllResources();
    }
    //
    // Now tack on the space and semi-colon...
    // these two are exceptions...to avoid
    // confusion with the BuildUpList method...
    NATOPhoneticItemKey key = new NATOPhoneticItemKey(' ');
    this._natoList.Load(key, new NATOPhoneticItem(key, "<space>"));
    //
    key = new NATOPhoneticItemKey(';');
    this._natoList.Load(key, new NATOPhoneticItem(key, "<semi-colon>"));
}
Figure 4.

Parsing

The value of each resource is split up using string[] sDataResList = sResourceDataValue.Split(new char[] { ';' });.

Each entry in the sDataResList consists of a character followed by a phonetic, for example, as shown in Figure 5.

C#
sDataResList[0] = "aAlpha"
sDataResList[1] = "bBeta"
....
sDataResList[25] = "zZulu"
Figure 5.

The parsing is quite elementary, the zero'th offset in each entry is the key; by using ToCharArray method on the string to obtain the first letter, then using the Substring method, we can extract the rest of the entry from the first position in the string, which is the value.

After extracting the key/value, we invoke the Load method of the NATOListHandler with a new instance of the key and value as parameters, effectively building up the dictionary as we iterate through the list of entries. The code used for parsing is shown below in Figure 6 in the method BuildUpList.

C#
/// <summary>
/// This creates the key and value from
/// the string that is delimited by semi-colon
/// and shoves them into NATOListHandler
/// to take care of adding them to its internal collection.
/// </summary>
/// <param name="sResourceDataValue">The semi-colon delimited string</param>

private void BuildUpList(string sResourceDataValue) {
    string[] sDataResList = sResourceDataValue.Split(new char[] { ';' });
    foreach (string sData in sDataResList) {
        string sTrimmdData = 
          sData.TrimStart(new char[] { ' ' }).TrimEnd(new char[] { ' ' });
        if (sTrimmdData.Length > 1) {
            NATOPhoneticItemKey key = 
              new NATOPhoneticItemKey(sTrimmdData.ToCharArray()[0]);
            this._natoList.Load(key, new NATOPhoneticItem(key, 
                                sTrimmdData.Substring(1)));
        }
    }
}
Figure 6.

There are two particular exceptions, which is the space and the semicolon. See the above code for LoadFromResource (near the end of the method) as shown in Figure 4; these are hardcoded in place as it would fail the parsing mechanism itself.

Inputting the Series of Words

This input is restricted to 255 characters. The way it works is this: enter a string of words, click on the button 'Convert', see Figure 7 below for the snippet of code in the method btnConvert_Click. The listview gets cleared, sets the cursor to an hourglass, and invokes the RunWorkerAsync method of the BackgroundWorker component.

C#
private void btnConvert_Click(object sender, EventArgs e) {
    this.lstVwResults.Items.Clear();
    if (this.txtBoxDescr.TextLength > 0) {
        this.Cursor = System.Windows.Forms.Cursors.WaitCursor;
        this.bgWorkr.RunWorkerAsync(this.txtBoxDescr.Text);
    }
}
Figure 7.

Using BackgroundWorker and ListView

The BackgroundWorker does the processing of the input on a separate thread, passing in the contents of the textbox via the DoWorkEventArgs argument of the DoWork event handler. See Figure 8 below.

The string is checked to make sure it is not null or empty, we convert it to a character array, iterate the array in a foreach loop, and perform a lookup via the LookUp function which returns a NATOPhoneticItem.

A check is made to see if item (of type NATOPhoneticItem) contains a non-null value, then constructs a ListViewItem which consists of two fields, one for the actual NATO Phonetic word, the other for the character in the iteration of the character array, i.e., ch.

If item is null, then a question mark is used as a place-holder for that ListViewItem, as that means there is no associated phonetic word for that character. (See Figure 3 above for the implementation of the LookUp function found within NATOListHandler.cs.)

C#
private void bgWorkr_DoWork(object sender, DoWorkEventArgs e) {
    string sInputTxt = (string)e.Argument as string;
    // obtain the string from the
    // DoWorkEventArgs

    if (!string.IsNullOrEmpty(sInputTxt)){
    // Check if it is not empty or null
        // convert string to character array
        char[] chDescrList = sInputTxt.ToCharArray();
        int nItmFilldCnt = 0;
        // iterate through the char array.
        foreach (char ch in chDescrList) {
            NATOPhoneticItem item = 
              this._natoList.LookUp(char.ToLower(ch));
            ListViewItem lvi = new ListViewItem();
            if (item != null) {
                if (char.IsLower(ch))
                    lvi.Text = item.Phonetic.ToLower();
                else lvi.Text = item.Phonetic;
                lvi.SubItems.Add(ch.ToString());
            } else {
                // Not in the dictionary....oh well...
                lvi.Text = "?"; 
                lvi.SubItems.Add(ch.ToString());
            }
            //
            MethodInvoker mi = new MethodInvoker(delegate{ 
                this.lstVwResults.Items.Add(lvi); });
            if (this.InvokeRequired)
                AnonDelInvoke(this.lstVwResults, mi);
            else this.lstVwResults.Items.Add(lvi);
            nItmFilldCnt++;
        }
        if (nItmFilldCnt > 1) _bListFilled = true;
    }
}
Figure 8.

Loop around until there are no more elements in the array. The next bit after that handles the marshalling of updating the GUI (which is on the main thread) from the thread that BackgroundWorker is running on, by using an anonymous delegate and a type MethodInvoker; this in turn calls a static method AnonDelInvoke. See Figure 9 below to see the implementation of the method.

The final piece of code is to check for the local variable nFilldCnt; if is greater than 1, switch on a flag (a global static variable) _bListFilled. The reason is that, if the input text is deleted, it will wipe out the listview items and switch off the flag. This is handled by the txtBoxDescr_TextChanged event handler. That is switched on/off mutually in two different places. See Figure 10 below.

C#
public static void AnonDelInvoke(Control c, MethodInvoker mi) {
    c.Invoke((Delegate)mi);
}
Figure 9.

In the above Figure 9, two parameters are passed in:

  • c of type Control - the Windows control you want to perform a safe invoke on.
  • mi of type MethodInvoker - the delegate type that is to be used to perform the marshalling of updating the said control from the thread to the main GUI thread.

The magic of cross marshalling of GUI updating occurs in that one line; yup, so much cleaner and easier to use, eh? By calling Invoke on that control with a MethodInvoker delegate used, the control gets safely updated from another thread. Neat, huh?

C#
private void txtBoxDescr_TextChanged(object sender, EventArgs e) {
    if (this.txtBoxDescr.TextLength > 0) this.btnConvert.Enabled = true;
    else this.btnConvert.Enabled = false;
    if (_bListFilled && this.txtBoxDescr.TextLength == 0) {
        this.lstVwResults.Items.Clear();
        _bListFilled = false;
    }
}
Figure 10.

In the above Figure 10, this is where the mutually exclusive operation on the global flag, _bListFilled, gets triggered. Notice how the logic checks to see if _bListFilled has been set and that the input box is empty, causing the listview to be cleared and the global variable getting switched off!

Finally, the BackgroundWorker component fires an event RunWorkerCompleted in which it is handled to cater for reverting back the cursor to the default. See Figure 11 in the bgWorkr_RunWorkerCompleted method below.

C#
private void bgWorkr_RunWorkerCompletedobject sender,
    RunWorkerCompletedEventArgs e) {
    MethodInvoker mi = new MethodInvoker(
      delegate { this.Cursor = this._cursorDefault; });
    if (this.InvokeRequired) AnonDelInvoke(this, mi);
    else this.Cursor = this._cursorDefault;
}
Figure 11.

Again, notice how the marshalling of the thread is handled to update the cursor on the main GUI thread. The variable _cursorDefault is global, and is set at the form's constructor, immediately after the call to the InitializeComponent method.

Using the Menu Strip and the Status Bar

The WinForm is spiced up a little. The status bar strip actually has two panels, mutually exclusive when the main System Menu or the MenuStrip gets activated. The other panel is used when a control has the focus (see ctlEnter and ctlLeave). To get the System Menu selection, trap the WndProc messages to see what system menu option was selected; it then gets shown on the status bar using the appropriate label.

To get the MenuStrip selection, there are a few events that need to be trapped, namely:

  • MenuActivate - this event will be fired when the drop-down of the menu strip occurs
  • MenuDeactivate - this event will fire when the menu strip loses focus
  • MouseEnter - this event will fire when the mouse hovers over a menu item within the menu strip

Points of Interest

I was quite surprised at how easy it was to learn the NATO Phonetics (so much so, that I am armed and ready to spell out my name which has an awkward pronunciation to an untrained ear along with my slight speech impediment!), and of course, it will add more value to your verbal communication abilities.

I have devised a form template that handles all of the menu strip/status bar handling which can significantly speed up development efforts. Since leaving the .NET 1.1 framework behind me to move with the times, I have used Visual Studio 2008 Express, which is perfectly adequate for my needs. I have yet to figure out how to make the solutions default to .NET framework 2.0, because I personally found the .NET 2.0 framework to be more useful and neater then 1.1

I have used an official logo for NATO, which was a PNG format, and an icon editor called IcoFX (link is given below in the References section), which was able to make an icon from the PNG, which proved handy as I was well prepared to draw a mockup of the NATO logo. If there are restrictions to the usage of the logo, I will withdraw it from the source.

Not alone that, just for a kick, the compiled executable, which I copied across to a VirtualBox guest Operating System running Slackware Linux v12.2 with the latest and greatest build of Mono Runtime v2.4, has worked straight out of the box so to speak! No need for recompilation, neat eh? This VirtualBox guest was running X and Windowmaker. See Figure 12 below.

Okay, there were a few GUI glitches admittedly - the status bar failed to get updated sometimes, the colors turned out wrong, the screenshot speaks for itself there, but the core essence of the program actually worked.

NATOPhonetics_UnderMono.PNG

Figure 12.

The glitches were due to some parts of WinForms either not implemented or broken or that the X graphical interface handled the GUI drawing quite differently.

Finally

Right, that's the end of this article, so, feel free to post your comments at the end of this article and be sure to vote. All suggestions/ criticisms/ feedback is most welcome.

"Hotel alpha victor echo space foxtrot uniform november!" :)

References

Sources used...

History

  • First release - 29th June 2009.
  • Second release - 5th July 2009 - Thanks to PIEBALDconsult for pointing out something obvious in the message board. The article reflects it now. Cheers!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)