Wednesday, August 24, 2022

Morse Code Library

Morse code has been on my mind every now and then for the past several years. I don't write as much code as I used to since I left the software development industry, but today I decided it was time to write functions to encode and decode morse.

First, let's define an array to map the encodings for each character.

  1. $morse = array(
  2. " " => "/",
  3. "A" => ".-",
  4. "B" => "-...",
  5. "C" => "-.-.",
  6. "D" => "-..",
  7. "E" => ".",
  8. "F" => "..-.",
  9. "G" => "--.",
  10. "H" => "....",
  11. "I" => "..",
  12. "J" => ".---",
  13. "K" => "-.-",
  14. "L" => ".-..",
  15. "M" => "--",
  16. "N" => "-.",
  17. "O" => "---",
  18. "P" => ".--.",
  19. "Q" => "--.-",
  20. "R" => ".-.",
  21. "S" => "...",
  22. "T" => "-",
  23. "U" => "..-",
  24. "V" => "...-",
  25. "W" => ".--",
  26. "X" => "-..-",
  27. "Y" => "-.--",
  28. "Z" => "--..",
  29. "0" => "-----",
  30. "1" => ".----",
  31. "2" => "..---",
  32. "3" => "...--",
  33. "4" => "....-",
  34. "5" => ".....",
  35. "6" => "-....",
  36. "7" => "--...",
  37. "8" => "---..",
  38. "9" => "----.",
  39. "." => ".-.-.-",
  40. "," => "--..--",
  41. "?" => "..--..",
  42. "'" => ".----.",
  43. "!" => "-.-.--",
  44. "/" => "-..-.",
  45. "(" => "-.--.",
  46. ")" => "-.--.-",
  47. "&" => ".-...",
  48. ":" => "---...",
  49. ";" => "-.-.-.",
  50. "=" => "-...-",
  51. "+" => ".-.-.",
  52. "-" => "-....-",
  53. "_" => "..--.-",
  54. "\"" => ".-..-.",
  55. "$" => "...-..-",
  56. "@" => ".--.-.",
  57. );

This covers almost all possible morse characters, with the exception of accented letters.

Next, we need a function to encode messages.

  1. function morseEncodeMessage($message)
  2. {
  3. global $morse;
  4. $result = "";
  5. for ($i = 0; $i <= strlen($message) - 1; $i++)
  6. {
  7. $character = strtoupper(substr($message, $i, 1));
  8. $result .= (array_key_exists($character, $morse) ? $morse[$character] : "........") . " ";
  9. }
  10. $result = rtrim($result);
  11. return $result;
  12. }
We're looping through the message, one character at a time. Lowercase characters are turned into uppercase characters. We check the global array to see if there's a corresponding key, and if so we get the value associated with that key. If the character can't be found, we convert it to a series of eight dots to indicate that an error has occurred. Each encoded character is separated by a space; an extra trailing space is removed before we return the result. Spaces between words are turned into forward slashes.

Now we need a way of decoding messages.

  1. function morseDecodeMessage($message)
  2. {
  3. global $morse;
  4. $result = "";
  5. $characters = explode(" ", $message);
  6. foreach ($characters as &$character)
  7. {
  8. $result .= in_array($character, $morse, true) ? array_search($character, $morse, true) : "ERROR";
  9. }
  10. unset($character);
  11. return $result;
  12. }
We start by splitting the encoded message into an array, using the spaces between each letter as the separator. Then we loop through the local array and check the global array for each value. If the value is found, we retrieve the key, otherwise we indicate that an error has occurred.

Finally, let's write a unit test to make sure our code is working properly.

  1. function morseTest($message)
  2. {
  3. $testInput = $message;
  4. echo "Input:   " . strtoupper($testInput) . "\n";
  5. $testEncoded = morseEncodeMessage($testInput);
  6. echo "Encoded: " . $testEncoded . "\n";
  7. $testDecoded = morseDecodeMessage($testEncoded);
  8. echo "Decoded: " . $testDecoded . "\n";
  9. echo "Result:  Test " . ($testDecoded == strtoupper($testInput) ? "successful" : "failed") . "!\n";
  10. }
Nothing too complicated. Display the message before encoding, encode the message, display the encoded message, decode the message, display the decoded message, and compare the decoded message with the original.

If we execute the following:
morseTest("Hello world!");

We get this result:
Input:   HELLO WORLD!
Encoded: .... . .-.. .-.. --- / .-- --- .-. .-.. -.. -.-.--
Decoded: HELLO WORLD!
Result:  Test successful!

Saturday, May 26, 2018

Temperature Conversion Revisited

I recently completed another ASP.NET project and learned about the app_code folder, which lets you share code between pages. It's slightly similar to the #include directive in Classic ASP, but a lot more structured. I couldn't find much good documentation on it, so most of what I've learned has been through experimentation.
Any files placed into the app_code folder are automatically available from any page in your web application, so you can write: myNamespace.myClass.myFunction(someVariable) and the magic will happen. Alternatively, you can add Imports myNamespace.myClass in the top of your page and call myFunction without all the prefixing.
Though I'm still not a big fan of ASP.NET, this app_code thing is pretty slick and it's inspired me to write more reusable code. I had previously written a collection of functions for temperature conversion, which I've since grown unhappy with after reading Code Complete 2.0 by Steve McConnell because I named my functions like this: CelsiusToFahrenheit, instead of like this: FahrenheitFromCelsius.
I had been planning to write more conversion functions for various units of measurement, but was never satisfied enough with how it was turning out to make it publically available. Besides learning to appreciate ASP.NET, I'm also learning to appreciate object-oriented programming. This whole namespace/class combination with app_code seems to be appropriately suited for doing unit of measurement conversion.
So here's my plan. I've defined a namespace called UOM, which is the manufacturing industry standard abbreviation for unit of measure. Each type of measurement will be a class: temperature, distance, volume, mass, etc. Each class will accept string input so that you can specify both the numeric quantity and the unit of measure simultaneously. The class will use the specified unit of measure to convert values to a common unit of measure, and be able to return values in any equivalent unit of measure.

VB.NET

  1. Dim inside as Temperature
  2. inside = New Temperature("10 C")
  3. Response.Write(inside.getFahrenheit) ' Displays "50"
And so I proudly announce the release of the first installment in the UOM namespace: UOM.Temperature, which handles Celsius, Delisle, Fahrenheit, Kelvin, Newton, Rankine, Réaumur, and Rømer temperature scales.
Download the VB.NET source code for UOM.Temperature from Snipplr.com

Tuesday, September 20, 2011

Force SSL in VB.NET

Today I'm wrapping up a project to convert an existing web site to use SSL communication only. This required adding HTTPS detection in various places to prevent mixed-content errors. I'm not entirely happy with my solution for that yet, so I'm not going to go into that right now.

What I do want to talk about is actually forcing the user over to HTTPS once your site is SSL-ready. The code I had used for many years on classic ASP sites was not working reliably; it would redirect, but to the home page instead of the page you tried to access.

ASP.NET has beefed up the Request object with some additional information we didn't have back in the old days (Request.IsSecureConnection and Request.Url). We also have a new way of including our function libraries, the App_Code folder.

  1. NameSpace myApplication
  2.     Public Class myLibrary
  3.         Public Shared Sub ForceSSL()
  4.             If Not System.Web.HttpContext.Current.Request.IsSecureConnection Then
  5.                 System.Web.HttpContext.Current.Response.Redirect(System.Web.HttpContext.Current.Request.Url.AbsoluteUri.Replace("http://", "https://"))
  6.             End If
  7.         End Sub
  8.     End Class
  9. End NameSpace


Then from each page on the site I import my custom library and run the subroutine:

  1. Imports myApplication.myLibrary
  2. Sub Page_Load(sender as Object, e as EventArgs)
  3.     ForceSSL()
  4.     'Rest of code goes here...
  5. End Sub


Supposedly there's a way to do this without code by modifying some settings in IIS, but I didn't have any success with that. The particular situation I'm dealing with has some fairly severe cohesion/coupling issues with certain pages being accessed from different subdomains.

Download the VB.NET source code for ForceSSL from Snipplr.com

Tuesday, May 10, 2011

Temperature Conversion Revisited

I recently completed another ASP.NET project and learned about the app_code folder, which lets you share code between pages. It's slightly similar to the #include directive in Classic ASP, but a lot more structured. I couldn't find much good documentation on it, so most of what I've learned has been through experimentation.

Any files placed into the app_code folder are automatically available from any page in your web application, so you can write: myNamespace.myClass.myFunction(someVariable) and the magic will happen. Alternatively, you can add Imports myNamespace.myClass in the top of your page and call myFunction without all the prefixing.

Though I'm still not a big fan of ASP.NET, this app_code thing is pretty slick and it's inspired me to write more reusable code. I had previously written a collection of functions for temperature conversion, which I've since grown unhappy with after reading Code Complete 2.0 by Steve McConnell because I named my functions like this: CelsiusToFahrenheit, instead of like this: FahrenheitFromCelsius.

I had been planning to write more conversion functions for various units of measurement, but was never satisfied enough with how it was turning out to make it publically available. Besides learning to appreciate ASP.NET, I'm also learning to appreciate object-oriented programming. This whole namespace/class combination with app_code seems to be appropriately suited for doing unit of measurement conversion.

So here's my plan. I've defined a namespace called UOM, which is the manufacturing industry standard abbreviation for unit of measure. Each type of measurement will be a class: temperature, distance, volume, mass, etc. Each class will accept string input so that you can specify both the numeric quantity and the unit of measure simultaneously. The class will use the specified unit of measure to convert values to a common unit of measure, and be able to return values in any equivalent unit of measure.

VB.NET

  1. Imports UOM
  2. Dim outside as Temperature
  3. outside = New Temperature("10 C")
  4. Response.Write(outside.getFahrenheit) 'Displays "50"

And so I proudly announce the release of the first installment in the UOM namespace: UOM.Temperature, which handles Celsius, Delisle, Fahrenheit, Kelvin, Newton, Rankine, Réaumur, and Rømer temperature scales.

Download the VB.NET source code for UOM.Temperature from Snipplr.com

Tuesday, September 14, 2010

Scalable Site Structure

If you've been developing web sites for a few years, you've probably developed a lot of sites with a structure similar to the following:

  • /css/
  • /images/
  • /scripts/
  • index.php
  • about.php
  • products.php

One of my colleagues likes to keep the root directory as clean as possible, so his site structures look like this:

  • /content/about.php
  • /content/products.php
  • /common/
  • /images/
  • index.php

Which I find slightly irritating because I prefer to keep my CSS separate from my JavaScript, and the /content/ folder is just taking the mess from one place and moving it to another.

Of course, the Holy Grail of site structures is to have clean URLs like this:

  • /about/
  • /css/
  • /images/
  • /products/
  • /scripts/
  • index.php

If you have a dynamic site, this is easily achieved using mod_rewrite, so /about/ becomes index.php?page=about, but sometimes the client doesn't want to pay for a dynamic site. In that case we use the Poor Man's Clean URLs™ wherein we actually create the /about/ and /products/ folders and put an index.php file in each with the static content. The PHP is then only really used to include a header, footer, navigation, etc.

As a site grows, performance and scalability become a (greater) concern. Popular site speed analysis tools often recommend using a content distribution network. Whether you outsource this or try to do it in-house, it sounds like a lot of work.

I hadn't had the privilege of working on a site that experienced enough traffic to be concerned about scalability until earlier this year. The company was running a national television commercial. They had hired another company to develop a micro-site on a separate server for the campaign, but viewers were being directed to the regular corporate site where a gigantic ad enticed them to visit the micro-site. If you're shaking your head right now, me too. The corporate web server was going down almost daily.

The web statistics package indicated that the most downloaded files were the product videos. I still don't know why they aren't using YouTube or Vimeo for this purpose. On a hunch, I copied the video files to the micro-site's web server and changed all the links. They experienced zero downtime after that.

This experience made me think about how to build scalability into a site from the beginning. Something in the back of my mind twigged a memory of reading something about serving static content from a cookieless domain. Cookies aside, it turns out that serving content from multiple subdomains has certain performance advantages.

Any discussion of clean URLs would not be complete without mentioning www.example.com vs. example.com. Dropping the www seems to be the "cool" thing to do, but my personal feeling is that www.example.com is more descriptive of what you will find there. The mail server is mail.example.com, so the web server should be www.example.com.

So here's the site structure I'm planning to use for future projects:

  • /css/ - css.example.com
  • /images/ - images.example.com
  • /images/logos/ - images.example.com/logos/
  • /images/products/ - images.example.com/products/
  • /js/ - js.example.com
  • /www/ - www.example.com
  • /www/about/ - www.example.com/about/
  • /www/products/ - www.example.com/products/
  • /www/index.php - the actual home page
  • /index.php - file which redirects to www.example.com

The subdomains can be hosted from the primary web server while traffic is small, and migrated to dedicated servers when the traffic increases. The URLs to the images, scripts, and stylesheets don't change, so the PHP files don't need to be updated. Use plenty of folders to keep things organized. You can see I made folders at images.example.com to keep the logos separate from product pictures. If there will be a lot of pictures for each product, I would make folders within the products folder to keep them segregated.

Sunday, September 5, 2010

Roman Numerals, Part 4

Keith Alexander of Albuquerque, New Mexico writes:
“I like your Roman Numeral library. I needed a function to test for Roman numerals, so I wrote this one.
  1. // Check to see if the string is a Roman Numeral
  2. // NOTE: this doesn't check for fractions, overbars, the Bede "N" (zero) etc.
  3. // NOTE: It also doesn't check for a well-formed Roman Numeral.
  4. function is_roman_numeral( $roman )
  5. {
  6.     // Strip every non-word character
  7.     // - A-Z, 0-9, apostrophe and understcore are what's left
  8.     $roman = preg_replace( "/[^A-Z0-9_']/iu", "", $roman );
  9.     // if it contains anything other than MDCLXVI, then it's not a Roman Numeral
  10.     $result = preg_match( "/[^MDCLXVI]/u", $roman );
  11.     if( $result )
  12.     {
  13.         return FALSE;
  14.     }
  15.     return TRUE;
  16. }

Who knows if blogger is going to show it properly. If not, just contact me and I'll send it you in email or something. Anyway, it's something I wrote in 5 minutes. If you want to add it to your library, modified or otherwise, please feel free.”

Thanks for writing in Keith, and sorry for the late response. There are two ways to validate a Roman number, using regular expressions like you did, and converting back to an Arabic number (if the conversion fails, it's not a Roman number).

I'm not sure about using a regular expression to remove non-word characters. My gut tells me that anything containing such characters should fail validation as a Roman number. Also, I would reverse the match and eliminate the if statement by directly returning the result of the match.

PHP

  1. function isRoman($roman)
  2. {
  3.     return preg_match("/[MDCLXVI]/u", $roman);
  4. }

ASP

  1. function isRoman(roman)
  2.     dim regEx
  3.     set regEx = new RegExp
  4.     with regEx
  5.         .IgnoreCase = true
  6.         .Global = true
  7.         .Pattern = "[MDCLXVI]"
  8.     end with
  9.     if regEx.Test(roman) then
  10.         isRoman = true
  11.     else
  12.         isRoman = false
  13.     end if
  14.     set regEx = nothing
  15. end function


Calculating annual salary for employees paid hourly

Last time I explained how to calculate the annual salary for an employee who is paid by the hour. Today I'm going to show you by writing a function for it, and we'll write one for monthly salary too.

  1. function annualSalary(someYear, hourlyPay)
  2.     const hoursPerDay = 8
  3.     annualSalary = 0
  4.     for i = 1 to 12
  5.         annualSalary = annualSalary + WorkingDays(someYear, i) * hoursPerDay * hourlyPay
  6.     next
  7. end function
  8. function monthlySalary(someYear, someMonth, hourlyPay)
  9.     const hoursPerDay = 8
  10.     monthlySalary = 0
  11.     monthlySalary = monthlySalary + WorkingDays(someYear, someMonth) * hoursPerDay * hourlyPay
  12. end function


View ASP implementation on Snipplr