Saturday, February 16, 2013

Learning to fly and Perl for METAR Decoding




One of the ways I have been occupying myself is with flight training for a private pilot license or more correctly called a private pilot certificate.  It's been off and on lately because I have had to take some time off to focus on other projects, but next month I will be resuming at as fast of a pace as my pocketbook will allow.  Since I needed some pilot/flying related activities to keep my interest during this down time I decided to write a Perl parsing script for aviation weather reports called METAR's.  But, first how did I find myself in the left seat of an airplane for the first time?  Well, read on.

About a year ago as a birthday present I was given a "discovery flight" by my parents.  A discovery flight is where a certified flight instructor sticks you in the pilot seat, guides you to take off and fly a plane  around for about an hour before landing at the airport where you took off from.  This is akin to the "visitation" rooms at the humane society where they let you play with the puppies and cats, all the while knowing that the puppy ( or aircraft, rather ) that you have been playing with is soon to become your highest priority.




That's how it happened with me anyway.  On my discovery flight I pushed the throttle forward.  We soon accelerated to 50 knots and when I pulled the yoke toward my chest, the Cessna 152 parted the runway and I became speechless; my breath had been taken thru amazement of the reality of flight.  It is one thing to understand the physics of flight and totally different to experience it.  General aviation or small aircraft flight isn't anything like the experience of large commercial airplanes.




In fact, I was so taken by this flying that I decided to continue flight lessons, working towards a private pilot certificate.  As I mentioned above I have experienced quite a few training delays along the way including taking time off to start Lakeside Electronics, LLC, so in the interim I wanted to come up with some activities related to flying.




Early on in my flight training I recognized the impact and importance of weather on general aviation or I should say, aviation in general.  There are even aviation specific weather reports such as METARs that pilots access for preflight weather planning.  You can look up a report for an airport near you using their ICAO station identifier code here http://aviationweather.gov/adds/metars/.

Here is an example METAR string from the airport I fly out of in Ann Arbor, Michigan: KARB 021653Z 34015G23KT 10SM OVC033 04/M03 A2996 RMK AO2 SLP150 T00441033.  

As you read the above report you can probably make out what some of the sub-strings mean.  For example, before researching I postulated that " OVC033 " meant overcast, but I did not fathom that the second half was referring to cloud height in 100's of feet.  Could " SLP150 " be something to do with slip?  Nope, Sea Level Pressure - in tens, units and tenths to be added to 1000 hPa no less.  In other words, 15.0 hPa + 1000 hPa = 1015.0 hPa.  However, if the string were a high number like SLP965 you would add that value ( 96.5hPa ) to 900 hPa instead for a reading of 996.5 hPa.  Yes, its a strange little protocol...

Almost certainly I needed to study up on how to interpret this METAR report.  Fortuitously, it would seem, I had concurrently begun learning the programming language Perl.  A perfect storm, it would seem, to write a Perl based parsing script for METAR weather reports and display these data in a more human readable format was afoot.

The goal of course is two fold.  A) To learn Perl and B) to memorize a method of METAR interpretation for when I need to read the raw string.  Quite obviously there are numerable ways to accomplish these tasks.  There also exists METAR decoders and even a Perl Module for this task, but this would violate my goals as set forth and thus decided to write my own Perl METAR parsing script.

When I turned to Google for information about the METAR protocol I found this page http://www.met.tamu.edu/class/metar/quick-metar.html helpful indeed.  If you recall, we discussed the sub-string for sea level pressure above.  There are too, other little gotchas along the way when trying to programmatically decode METAR reports.  For instance, some data are always reported, some data are optionally reported and within the raw string as a whole, the sometimes reported data is intermingled with the always reported data.  I also have reason to believe that the information contained in the remarks ( RMK ) section can partially exist as plain English, and so that is where my parsing stops - before we get to the optional remarks.  I may look into further coding of the remarks, but for now I am satisfied with the script.

This Perl script uses Curl to fetch a METAR string from weather.noaa.gov.  Then it parses the string, does some formatting and a couple of calculations for Celsius to Fahrenheit conversion then writes the data to a text file.  Using a program on my macbook called GeekTools I am able to display the contents of the Perl generated text file on my desktop.  It looks like this on my desktop.



Before continuing, I must say that you shouldn't use this script for flight planning and also, that it is largely untested.  Please report any bugs that you may find.  Part of my ongoing memorization of METAR syntax is to read the string and my scripts output and look for errors.

If you want to play along, install Geektools from the link in the previous paragraph.  You will also want to copy the Perl script from below.  Save the Perl script in a folder somewhere on your computer.  I named my folder "metar" and located in ~/Documents/Weather/.  Start up GeekTools and configure like so...



The command in the above screen cap is "cat ~/Documents/Weather/metar/metar_datafile.txt" without quotes.  Change this as required for your particular path and file name.


Below is the code.  It is definitely a work in progress.  Every time I spot an error I correct it but I haven't seen any errors in a while now.  Let me know if you do.

  
#!/usr/bin/perl -w

# this program gathers METAR data from NOAA, decodes it and does some formatting before writing the data to a text file
# this file is monitored and displayed on my macbook desktop using GeekTools
#
# Under no circumstances should you use this for flight planning.  Also, no guarantee is made that this software even works at all.
#
# Below is an example of the metar_datafile.txt after the curl command.  There are 2 lines as you can see.
#
# 2012/10/27 11:53
# KARB 300153Z AUTO 35018G34KT 10SM OVC095 06/M05 A2978 RMK AO2 PK WND 35034/0144 SLP090 T00561050
#
#

#while( 1 ){
my $ICAO_STATION = "KARB"; # airport nearby

my $raw_metar_data = `curl --silent http://weather.noaa.gov/pub/data/observations/metar/stations/$ICAO_STATION.TXT`;

chomp $raw_metar_data;

my  ( $date_time, $current_metar_line ) = split /\n/,$raw_metar_data;

chomp $date_time;
chomp $current_metar_line;

# put the METAR string into an array 
my @metar_array = split / /, $current_metar_line;
my $array_index = 0;

open (MYFILE, '>metar_datafile.txt'); #open for write >> would be for apend

#
# print the full array we are about to decode separated by spaces
print MYFILE "@metar_array\n";

#
# print the station ID as read
print MYFILE "ICAO Station ID: $metar_array[ $array_index ]\n";
++$array_index;

#
# print the day of the metar transmission
my $day = substr( $metar_array[ $array_index ], 0, 2 );
print MYFILE "Day: $day";

# print ^st, ^nd, ^rd or ^th at the end of the date just for a bit of fun
#my $right_digit = substr( $day, 1, 1 );

#$day = "3";



if( ( scalar $day == 1 ) || ( scalar $day == 21 ) || ( scalar $day == 31 ) ){
 print MYFILE "st\n";
}
elsif( ( scalar $day == 2 ) || ( scalar $day == 22 ) ){
 print MYFILE "nd\n";
}
elsif( ( scalar $day == 3 ) || ( scalar $day == 23 ) ){
 print MYFILE "rd\n";
}else{
 print MYFILE "th\n";
}


#
# print the time of the metar transmission in zulu time aka 0 GMT
my $zulu_hours = substr( $metar_array[ $array_index ], 2, 2 );
my $zulu_minutes = substr( $metar_array[ $array_index ], 4, 2 );
print MYFILE "Report Time ( Zulu ): $zulu_hours:$zulu_minutes\n";

++$array_index;


#
# decode and print report type ( auto or corrected or none )
#
# test String
# $metar_array[ $array_index ] = "COR";


if( $metar_array[ $array_index ] eq "AUTO" ){
 print MYFILE "Report Autonomy: Automatic - No human intervention.\n";
 ++$array_index;
 }
elsif( $metar_array[ $array_index ] eq "COR" ){
 print MYFILE "Report Autonomy: Corected observations.\n";
 ++$array_index;
 } 
else{
 print MYFILE "Report Autonomy: Human observer or Automatic with human oversight.\n";
}


#
# decode and print wind data

print MYFILE "Wind Condition: ";

# the first three characters are either VBR or they contain wind direction
my $wind_direction = substr( $metar_array[ $array_index ], 0, 3 );

# the next two characters always contain wind speed
my $wind_speed = substr( $metar_array[ $array_index ], 3, 2 );

if( $wind_direction eq "VBR" ){
 print MYFILE "Direction variable at $wind_speed knots.\n";
 ++$array_index;
}
# else if the string contains a 'V' for variable wind over 6 knots...
elsif( $metar_array[ $array_index ] =~ /V/ ){
 print MYFILE "Wind from $wind_direction degrees at $wind_speed knots - ";
 my $dir1 = substr( $metar_array[ $array_index ], 9, 3 );
 my $dir2 = substr( $metar_array[ $array_index ], 13, 3 );
 print MYFILE "Variable between $dir1 and $dir2 degrees.\n";
 ++$array_index;
} 
# else if the string contains a 'G' for gust
elsif( $metar_array[ $array_index ] =~ /G/ ){
 print MYFILE "Wind from $wind_direction degrees at $wind_speed knots - ";
 my $gust_speed = substr( $metar_array[ $array_index ], 6, 2 );
 print MYFILE "Gusts to $gust_speed knots.\n";
 ++$array_index; 
}
else{
 print MYFILE "Wind from $wind_direction degrees at $wind_speed knots.\n";
 ++$array_index;
}


#
# decode and print visibility

print MYFILE "Visibility: ";

# remove "SM" from the end of the string leaving the visibility in statute miles
my $visibility = $metar_array[ $array_index ];
chop $visibility;
chop $visibility;

# if the string still contains an 'M' after the chops there is a special case
if( $visibility =~ /M/ ){
 print MYFILE "Less than 1/4 statute miles.\n";
} 
elsif( $visibility eq 9999 ){
 print MYFILE "Greater than maximum recorded value.\n";
}
else{
 print MYFILE "$visibility statute miles.\n";
}
++$array_index;



#
# decode and print runway visual range if reported
# this string should contain a forward slash if it contains runway visual data
#
# test string
# $metar_array[ $array_index ] = "R16/P20000VM211FT";

if( $metar_array[ $array_index ] =~ /\// ){
 
 print MYFILE "Runway Visual Range: ";
 
 my( $runway, $range ) = split /\//, $metar_array[ $array_index ];
 
 # remove the 'R' from the runway number
 $runway =~ s/.//;
 
 # remove "FT" from the range(s)
 chop $range;
 chop $range;
 
 # there is a 'V' in $range_a if the visual range is variable
 if( $range =~ /V/ ){
  my( $range_a, $range_b ) = split /V/, $range;
  print MYFILE "Runway $runway has a visual range between ";
  
  # check for the M or P modifier
  if( $range_a =~ /M/ ){
   #remove the 'M'
   $range_a =~ s/.//;
   print MYFILE "less than $range_a and ";
  }
  elsif( $range_a =~ /P/ ){
   # remove the 'P'
   $range_a =~ s/.//;
   print MYFILE "greater than $range_a and ";
  }
  else{
   print MYFILE "$range_a and ";
  }
  
  if( $range_b =~ /M/ ){
   #remove the 'M'
   $range_b =~ s/.//;
   print MYFILE "less than $range_b feet.\n";
  }
  elsif( $range_b =~ /P/ ){
   # remove the 'P'
   $range_b =~ s/.//;
   print MYFILE "greater than $range_b feet.\n";
  }
  else{
   print MYFILE "$range_b feet.\n";
  }
  
  
  
 }
 else{
  print MYFILE "Runway $runway has a visual range of ";
  
  # check for the M or P modifier
  if( $range =~ /M/ ){
   #remove the 'M'
   $range =~ s/.//;
   print MYFILE "less than $range feet.\n";
  }
  elsif( $range =~ /P/ ){
   # remove the 'P'
   $range =~ s/.//;
   print MYFILE "greater than $range feet.\n";
  }
  else{
   print MYFILE "$range feet.\n";
  }
    
 }
 ++$array_index;
}



#
# decode and print weather phenomena if it exists
#
# test string
#$metar_array[ $array_index ] = "+RAPRTS-DRPL";

my $weather_phenom = $metar_array[ $array_index ];

# if the current string does not contain cloud cover data then it is weather phenomena

if( $weather_phenom !~ "SCK" & $weather_phenom !~ "CLR" & $weather_phenom !~ "FEW" & $weather_phenom !~ "SCT" &
 $weather_phenom !~ "BKN" & $weather_phenom !~ "OVC" & $weather_phenom !~ "VV" )
 {
  print MYFILE "Weather Phenomena: ";
  
  my $string_index = 0;
  my $string_length = length $weather_phenom;
  
  while( $string_index < $string_length ){
  
   my $sub_string = substr( $weather_phenom, $string_index, 2 ); 
   
   if( $sub_string =~ /\+/ ){    
    print MYFILE "Heavy ";
    ++$string_index;
   }
   elsif( $sub_string =~ /\-/ ){
    print MYFILE "Light ";
    ++$string_index;
   }
   #else{
   # print MYFILE "Moderate ";
   #}
   
   $sub_string = substr( $weather_phenom, $string_index, 2 );
   
   # hash lookup
   
   %weather_type = ( "VC" => "Vicinity ",
        "MI" => "Shallow ",
        "PR" => "Partial ",
        "BC" => "Patches ",
        "DR" => "Low Drifting ",
        "BL" => "Blowing ",
        "SH" => "Showers ",
        "TS" => "Thunderstorm ",
        "FZ" => "Freezing ",
        "DZ" => "Drizzle ",
        "RA" => "Rain ",
        "SN" => "Snow ",
        "SG" => "Snow grains ",
        "IC" => "Ice crystals ",
        "PL" => "Ice Pellets ",
        "GR" => "Hail ",
        "GS" => "Small hail ",
        "UP" => "Unknown ",
        "BR" => "Mist ",
        "FG" => "Fog ",
        "FU" => "Smoke ",
        "VA" => "Volcanic ash",
        "DU" => "Widespread dust ",
        "SA" => "Sand ",
        "HZ"  => "Haze ",
        "PY" => "Spray ",
        "PO" => "Well developed dust/sand swirls ",
        "SQ" => "Squalls ",
        "FC" => "Funnel clouds including tornadoes or waterspouts ",
        "SS" => "Sandstorm ",
        "DS"  =>  "Duststorm ",
        );
        
   print MYFILE "$weather_type{ $sub_string }";
   
   # see if there is another weather code
   $string_index += 2;
  }
  
  print MYFILE "\n";
  ++$array_index;
}


#
# decode and print the cloud cover data ( always present )
#
# test string
#$metar_array[ $array_index ] = "BKN120";

print MYFILE "Cloud cover: ";

# if there are multiple cloud conditions which can occur...
while( $metar_array[ $array_index ] =~ /(SCK|CLR|FEW|SCT|BKN|OVC|VV)/ ){
my $sky = 0;
my $cloud_height = 0;
 

if( $metar_array[ $array_index ] =~ /VV/ ){

 $sky = substr( $metar_array[ $array_index ], 0, 2 );
 $cloud_height = substr( $metar_array[ $array_index ], 2, 3 );
}
else{

 $sky = substr( $metar_array[ $array_index ], 0, 3 );
 $cloud_height = substr( $metar_array[ $array_index ], 3, 3 );
}


%sky_condition = ( "SCK" => "Sky Clear ",
     "CLR" => "Clear sky",
     "FEW" => "Few clouds ",
     "SCT" => "Scattered clouds ",
     "BKN" => "Broken clouds ",
     "OVC" => "Overcast clouds ",
     "VV" => "Vertical visibility ",
     );
     
print MYFILE "$sky_condition{ $sky }";

# if $sky has clouds in it then report the cloud height.
# if skies are clear, do not report cloud height

if( $sky =~ /(FEW|SCT|BKN|OVC|VV)/ ){
# cloud height is given in hundreds of feet
$cloud_height *= 100;

print MYFILE " at $cloud_height feet. ";
}
++$array_index;
}   

#
# decode and print the temperature and dewpoint ( always present )

my( $temperature_c, $dewpoint_c ) = split /\//, $metar_array[ $array_index ];

# if it is a negative number
if( $temperature_c =~ /M/ ){
 # remove the 'M' from the temperature
 $temperature_c =~ s/.//;
 $temperature_c *= -1; # and make it a negative number
}

if( $dewpoint_c =~ /M/ ){
 # remove the 'M' from the dewpoint
 $dewpoint_c =~ s/.//;
 $dewpoint_c *= -1; # and make it a negative number
}

my $temperature_f = $temperature_c * 1.8 + 32;
my $dewpoint_f = $dewpoint_c * 1.8 + 32;

print MYFILE "\nTemperature: $temperature_c degrees C / Dewpoint: $dewpoint_c degrees C.\n";
print MYFILE "Temperature: $temperature_f degrees F / Dewpoint: $dewpoint_f degrees F.\n";

++$array_index;


#
# decode and print atmospheric pressure ( always present )
#
# test_string
#$metar_array[ $array_index ] = "Q1234"; 
#$metar_array[ $array_index ] = "A1234"; 

my $atm = $metar_array[ $array_index ];

# if our pressure is reported in mb aka hPa
if(  $atm =~ /Q/ ){

 # remove the 'Q' from the pressure
 $atm =~ s/.//;
 print MYFILE "Atmospheric Pressure: $atm hPa\n";

}

# if our pressure is reported in mb aka hPa
elsif(  $atm =~ /A/ ){

 # remove the 'A' from the pressure
 $atm =~ s/.//;
 
 my $whole_inhg = substr( $atm, 0, 2 );
 my $frac_inhg = substr( $atm, 2, 2 );
 
 print MYFILE "Atmospheric Pressure: $whole_inhg.$frac_inhg inHg\n";

}

++$array_index;



#
# decode and print


close (MYFILE); 

#sleep( 60 );
#}























Ok, onward and upward.  Clear for takeoff.

Wednesday, September 12, 2012

KD8TBW - My HAM Radio Call Sign

I recently took an FCC Exam for radio broadcast privileges.  I passed and as the title suggests my HAM or Amateur Radio Call Sign is KD8TBW or in the NATO Phonetic Alphabet  Kilo, Delta, Eight, Tango, Bravo, Whiskey. 

I decided to take the test after I reading the April 2012 issue of Nuts and Volts Magazine.  On page 68 was an article on how to get your HAM license and the opening question was " If you are interested in electronics and radio communications, why aren't you a HAM? ".  Well I couldn't answer that question and the article had a very compelling list of  "10 things you can do as a HAM " so I set out to change my unlicensed status.

If you are wondering what HAM radio is, why you need a license or what cool things you can do as a HAM then head over to the American Radio Relay League ( ARRL ) website and take a look.  You'll discover many interesting activities that becoming a HAM will open up to you such as talking to astronauts aboard the ISS, bouncing radio signals off the moon or meteor showers and fox hunts with Tape Measure Yagi Antennas for radio direction finding.

You may also discover loosely related projects that don't require a HAM radio license.  For instance, together with my Dad, we have recently begun construction of a Quadrifilar Helicoidal Antenna for recieving APT messages from NOAA Weather Satellites, but more on that one in a later post.

I'm still pretty wet behind the ears with all this HAM radio stuff, but it is providing a good bit of entertainment so far.  I picked up a Kenwood TH-F6A Radio and I even joined a local HAM radio club called ARROW Communications in Ann Arbor.  I'm pretty sure I want to get into a mobile radio for my car so let me know if you have any suggestions on mobile setups.

Tuesday, September 4, 2012

Thor - A Programmable Brake Light Modulator

I have been rather busy developing products for my new company Lakeside Electronics, LLC and as such, haven't had much time to post anything in the way of projects over here.  This is OK however, because despite the massive amount of work involved in bringing a product like Thor to market, it turns out to be pretty fun too. 

Of course, I still have a few irons in the hobby fire, and with Halloween coming up there are bound to be some scary, or at a minimum, vaguely direful ideas cropping up in my yet-to-be-zombified mind.  But, first, I have some microcontrollers to program for a Boy Scout Troop.  I was recently contacted by (them) and I am quite pleased to help them out making Halloween Spooky Eyes.  Anytime someone is interested in learning new things I think it is fantastic.

Ok, so back to Thor - A Programmable Brake Light Modulator and Lakeside Electronics.  If you have been reading my recent posts you are probably already aware of what a brake light modulator is.  If you have any feedback, I'd like to hear it

And finally here is the video for Thor. 


Wednesday, August 1, 2012

More Flashing Brake Lights

It seems like summer is the season for brake light flashers and Greg, a reader of Pete's Blog just emailed me to show me what he has been up to.  After reading my original post Tenty LED Brake Lights which is a brake light modulator and LED light engine integrated onto perf board, Greg set out to make a version for his Yamaha V-Star 1100.

I'm impressed with Greg's craftsmanship which you can see in the photos below, but what you can't see is that he used an Arduino to program the ATTiny85 with the original code which can be found here.  This is cool because if you have an Arduino laying around, but no programmer, you can still make a modulating LED brake light for yourself.  Greg said the following links were useful to him in using his Arduino as a programmer http://hlt.media.mit.edu/?p=1229 and http://hlt.media.mit.edu/?p=1695 but ended up not changing the default 1 Mhz clock on the ATTiny85 and instead changed the delay between flashes in the code from 25 mS to 75 milliseconds.

Have a look at video and pics  below to see for yourself how cool Greg's flashing LED brake light turned out.  I really like the pattern the LED's make on the tail light lens.  If you have any pictures or video of a project you have made with the help of Pete's blog, send me an email, I'd like to see!












Wednesday, July 25, 2012

Betelgeuse Brake Light flasher by Lakeside Electronics, LLC

It's been a couple months since I have posted anything and that's because I've been hard at work developing a product and starting a company called Lakeside Electronics, LLC.  The first product available thru Lakeside Electronics is a brake light flasher to add conspicuity to motorcycles primarily, but also cars, trucks, trailers or even custom vehicles.  Actually, Betelgeuse - A Programmable LED Brake Lamp is an LED brake light using 4 Cree X-Lamp LED's with an integrated flash pattern controller.

Check out the video below to see some of the different flash or strobe patterns Betelgeuse ( pronounced BeetleJuice or Betel Juice ) can display.  Betelgeuse can even modulate the LED light in a way to look like old school filament bulbs.  If you are interested in purchasing Betelgeuse you can head over to the product page at LakesideElectronics.net and hit "add to cart".




Betelgeuse is really cool because it requires no wiring modifications, you just twist the bulb in and you are done.  It is fully configurable by using only your brake lever for changing and storing the flash pattern.  It also has a recording feature where you can record your own flash or strobe pattern by recording your brakes.  The recording time is fixed at 10 seconds which is plenty of time to tap a brake light message in Morse Code.  Of course, Betelgeuse can also act as a traditional LED brake light with no flashing too.

If you are interested in the full details you can download the Betelgeuse owners manual from the downloads page to see how capable this modulating brake light really is.

Currently Betelgeuse  comes in type 1157 dual filament type bases.  Of course, dual filament bulbs fit is several bases in addition to the 1157.  There are a couple versions of Betelgeuse brake light strobes in the works, so if you are interested keep checking back to see what's new.  I'm really excited about Betelgeuse - A Programmable LED Brake Lamp and I hope you are too.

Sunday, May 27, 2012

LED Stroboscope from a Clapper Lamp Prototype


A little while ago a writer for Popular Science magazine approached me asking if I would be interested in collaborating with them write a " How To " article based on my Clever Clapper post the other month.  Well, of course I would, that sounds awesome! And so, I made another Clapper lamp, this time leaving out the laser activated moon lamp so as to better fit the magazine space.

Clapper Demo



Party Mode Demo



Fast forward a few months and you can find in the June 2012 issue, the " Diy Clapper " article on page 80.  You can see an online version of the article and links to software etc on the Popular Science page here.

All in all, it was a pretty cool experience, but I am left with another clapper lamp now.  And worse, dueling clapper lamps that have no mechanism by which to synchronize themselves.  Two claps may or may not turn on both the new clapper lamp and original Clever Clapper leaving me with flip flopping lights until I have to resort to manual control of one or the other lamp.




The non-necessity of having multiple clap activated lamps in my house prompted me to make an LED Stroboscope with the latest lamp - utilizing as much of the hardware as possible.  I only added a potentiometer to set the strobe frequency.

I would not design a stroboscope with this hardware from scratch, but it is a good reuse of the lamp and circuit that would otherwise sit idle.  For instance, a fairly big drawback is the 8-bit timer in the ATTiny85.  The frequency step changes are quite noticeable and I would prefer a 16-bit timer like the ATMega328 has.

And of course with those extra uC pins I could add a display to show the frequency rather than relying on an oscilloscope for measuring the output frequency.  Having said that however, with a lack of crystal on the ATTiny85, using the internal RC oscillator instead, it is probably better to measure the frequency externally rather than an internally calculated frequency display.

You can see the hardware changes from the Clapper lamp outlined in the schematic below.  I disconnected a few connections and added a potentiometer, that's about it.




The software for the stroboscope can be downloaded here.  It isn't the prettiest of code as, but it should suffice until I can build another stroboscope with a more concentrated effort.

To operate the stroboscope you just turn it on and the lamp is strobing somewhere between ~4 Hz and ~31.3 kHz.  The maximum flash rate is high enough so as not to be noticed.  So you can use it as a reading lamp.  Pressing the tactile switch shuffles thru a loop, changing the timer presets thus decreasing the flash rate by 2 times, each press until it loops back to the beginning.  Find your range with the tact switch then turn the potentiometer to set the exact frequency required to stop motion.

In the video below,  there are on screen comments noting the points where the strobe frequency is a multiple of the fan speed.  It is advantageous to use a mark on symmetrical parts to determine when the strobe frequency is a multiple of the part frequency or spot on.  I marked this fan blade with a sharpie so I know that if the rotational motion appears stopped and I see two arrows ( sharpie marks ) then the strobe frequency is 2x the fan speed.  It should follow that one sharpie mark showing means the strobe is 1x the fan speed.  This is the basic principle of stroboscopes, but for a more in depth look have a look at this wikipedia entry.




I am pretty pleased with the results and knowing the limitations of this stroboscope I can still see it being useful to me.  Of course I look forward to making a more full featured model with improved timer hardware etc, but for now I can inspect all kinds of rotating or oscillating mechanical bits in pseudo slow motion.  I can also measure the speed of unknown objects easily as well as prototype some zoetropes or similar.

As an aside, have a look at this scope my Mother bought me out of the blue the other day - just in time to use it for this project too!  How cool is that?



Sunday, April 15, 2012

Tenty LED Brake Lights





UPDATE:
     This project was so overwhelmingly well received that it has spawned a commercial interest.  The version available for purchase varies somewhat in it's flash patterns and also in the ability to record and play back your own flash patterns.  If you would like to check it out take a look at Betelgeuse - A Programmable LED Brake Lamp at my new company Lakeside Electronics, LLC.



I Purchased a motorcycle about two weeks ago.  Interestingly, whenever I tell someone this news, they immediately proceed to tell me the most gruesome injuries and stomach turning plights that they or someone they know, has fallen victim to while motorcycling.  In some cases, these raconteur's briefly pause to look over their shoulder, presumably scanning for small children or otherwise offendable ears, before delivering the goriest details.

One commonality in these stories, aside from the macabre and arguably poor timing involved in telling them to me is that many accidents come down to a lack of visibility of motorcycles and their riders.  Less than Argus-eyed motorists often pull out into the path of a motorcycle and with insufficient time for evasive action, that quickly an accident has occurred.  Other times, drivers may focus on the car ahead of the motorcycle and in the event of stopping at a red light or similar, fail to leave enough room for the motorcycle, unfortunately rear ending him or her.

In the photo below, you can see that I am in one piece and the bike is also.  Let's see what we can do to help keep things that way.




To partially combat the lack of visibility I decided to make a replacement brake light for my bike; one that would strobe briefly when I applied the brakes then stay solid on.  I got this idea when I noticed a similar strobing effect to the brake lights on an ambulance that I was following near to, but not immediately behind.  Those lights really got me to notice the ambulance which I had largely ignored up until that point.

I would also want a way to be able to disable the brief flash before the brake light stays solid on.  This could be useful if the flash was ever to cause a safety hazard to other drivers or if I am ever stopped by the police for having this system I can easily disable the strobe part and still have a functioning brake light; I do not want to be stranded.  I wrote the software so that if you turn the key on while you have the brakes applied, the flash is disabled.  You must turn the key off, then back on, without the brake applied to restore the flash functionality.

Here is a video of the prototype Tenty LED Brake Light installed and operating.  In the video, I demonstrate the flashing brake light 3 times and then key off.  Then I disable the strobing part by turning the key on while holding the brake on, demonstrate that several times and key off again.  Finally I re-enable the strobing functionality by key on with the brake off.  For the purposes of the video, I lengthened the duration of the flash on and off times.  This is so the camera can better capture the flashes.  




In order to keep this system reversible should I ever have the need to put it back to stock, here is how I mounted the circuit board inside the tail light lens.  I bought some replacement bulbs at a local auto parts store, broke out the glass and desoldered the filament.  I replaced the filament by soldering wires to the tail light power, brake light power and ground.  I ran a file over the solder blobs just enough to level them out so they would make good contact with the socket.  I then filled the bayonet base with epoxy and inserted an 8-32 stainless steel screw into the base being careful not to touch the screw to any metal,  accidentally making the screw a conductor.  Below are a few pictures of this process.


 



 After installing the modified bayonet screw mounts I threaded two nuts on each and jammed them together.  This is what the back of the circuit board will rest on.  You can see several pictures of the final assembly below.











Circuit:

The circuit is basically a large array of really bright red LED's ( Light Engine ) and an ATTiny85 microcontroller to tell the LED light engine how to behave.  There are also two high brightness LED's that shine downward onto the license plate.  They are solid on all the time as required by the Michigan vehicle code.  The red LED's, made by Optek have a 100 degree viewing angle and output 8000 mlm each - there are 16 of them.  The brake light switch delivers it's ~12 V signal translated thru an 2n3904 transistor to pull a microcontroller pin low.  I usually use 14.4 V when doing calculations for operating voltage on 12V systems like this.  Total current draw with all LED's at maximum brightness is less than 500 mA.  I ordered the logic level mosfet IRLU3410PBF from mouser.com also, which is used for the PWM control of the light engine.  Below you will find a screen cap pic of the schematic.





Software

There are two modes of operation to this brake light - flashing and non flashing.  The non flashing brake light is activated by turning the key on while holding the brake and behaves just like a stock brake light would.  Flashing mode is enabled by default so key on with brake off and you are good to go.  In this mode, the LED light engine strobes briefly upon initial application of the brake then is a steady on light.  I set the period of the flashes to be higher for the videos so the camera could pick them up, you can see in the code below that the flash period is twice as fast in the operational version of the software.  Feel free to use the code as you see fit.


/*
Program Description: 

This program controls an LED light engine used as a brake light on a motorcycle.
When the brakes are applied the tail light flashes several times off then on before
being a steady on brake light.

The program also has a feature to disable the flashing brake light and the brake light will
behave like a standard lamp ie no flashing.  This mode is enabled ( flash mode disabled )
by turning the motorcycles key on while holding the brake on.  The state is reset after a power 
cycle.

Legal Note:  I read the entire vehicle code for my state.  It does not address brake light flashers, but
does prohibit " rotating, oscillating or flashing red lights ".  I believe a brake light flasher
differs from " rotating, oscillating and flashing " lights in the implied duration of operation.  
My state did not respond to emails I sent requesting clarification.  
Basically, modify your vehicle at your own risk.

Change Log
2012.4.21 - added 1 mS debounce to brake switch in main loop - Pete


A circuit description and other details can be found at http://petemills.blogspot.com

Filename: led_brakelight_main.c
Author: Pete Mills
petemills.blogspot.com
2012.4.15

Int. RC Osc. 8 MHz; Start-up time PWRDWN/RESET: 6 CK/14 CK + 64 ms

*/



//********** Includes **********

#include <avr/io.h>     
#include <util/delay.h>   



//********** Definitions **********

// Output to LED Light Engine

#define LED   PB0  
#define LED_PORT PORTB
#define LED_DDR  DDRB


// Input for Brake Switch

#define BRAKE_SWITCH  PINB4  // bit is clear when brake switch is pressed
#define BRAKE_SWITCH_PORT PINB
#define BRAKE_SWITCH_DDR DDRB



// PWM Preset Values

#define TAIL_LIGHT_PWM  25
#define BRAKE_LIGHT_PWM  255



//********** Function Prototypes **********

void setup( void );
void brake_alert( uint8_t number_of_flashes );



//********** Global Variables **********

uint8_t disable_flash = 0; // if 1, the flashing part of the brake light will be disabled


int main(void)
{


setup();

_delay_ms( 5 );



// if the brake light is held on during boot up, disable the flashing
// flashing can only be disabled during boot up ( key on ) and is reset after power down ( key off )
// this could be useful if you suddenly learn your flashing light is causing a problem for other motorists 
// or citizens employed to monitor the adherence to legislation and cite violations for deviation from such 


if ( bit_is_clear( BRAKE_SWITCH_PORT, BRAKE_SWITCH ) )
{
 disable_flash = 1; 
}



 while(1)
 { 
  
  
  if ( bit_is_clear( BRAKE_SWITCH_PORT, BRAKE_SWITCH ) )  // if the brakes are applied
  {

   _delay_ms( 1 ); // filter time aka debounce

   if ( bit_is_clear( BRAKE_SWITCH_PORT, BRAKE_SWITCH ) )  // if the brakes are actually applied
   {  

   // if we are allowed to flash, do
   
   if( disable_flash == 0 ) 
   {
    brake_alert( 5 );
   }
  

   // if the brakes are still applied, hold the LED light engine to a brighter output
   // until you release the brakes
   
   while( bit_is_clear( BRAKE_SWITCH_PORT, BRAKE_SWITCH ) )
   {
    OCR0A = BRAKE_LIGHT_PWM;
   }
   
   _delay_ms( 100 ); // debounce the brake switch release ( break )
   
   }   

  }
  
  OCR0A = TAIL_LIGHT_PWM;  // restore the tail light on
  
  
  
  /*
  // demo mode
  for( ;; )
  {
  brake_alert(6);
  OCR0A = BRAKE_LIGHT_PWM;
  _delay_ms(2000);
  OCR0A = TAIL_LIGHT_PWM;
  _delay_ms(4000);
  }
  */
   
 }
}




//********** Functions **********

void setup(void)
{



 //********* Port Config *********

 LED_DDR |= ( 1 << LED);   // set PB0 to "1" for output 
 LED_PORT &= ~( 1 << LED );   // turn the led light engine off

 BRAKE_SWITCH_DDR &= ~( 1 << BRAKE_SWITCH );   // set BRAKE_SWITCH pin to 0 for input
 BRAKE_SWITCH_PORT |= ( 1 << BRAKE_SWITCH );   // write a 1 to BRAKE_SWITCH to enable the internal pullup



 //********** PWM Config *********
 
 TCCR0A |= ( ( 1 << COM0A1 ) | ( 1 << WGM01 ) | ( 1 << WGM00 ) ); // non inverting fast pwm
 TCCR0B |= ( 1 << CS00 ); // start the timer
 
 
}




void brake_alert( uint8_t number_of_flashes )
{
 // here we create a visual alert that the brakes have been applied
 // pass this function the number of flashes desired ( off then on = 1 )
 
 //uint8_t delay_between_flashes = 50; // for video demonstration purposes ( video aliasing issues ) 
 uint8_t delay_between_flashes = 25; // every day use
 uint8_t alert_pwm_min = 2;   // minimum and max values to flash between during the alert
 uint8_t alert_pwm_max = 255;
 
 
 for( int i = 0; i < number_of_flashes; i++ )
 {
  OCR0A = alert_pwm_min;
  _delay_ms( delay_between_flashes );
  OCR0A = alert_pwm_max;
  _delay_ms( delay_between_flashes );
 }

}


Legal Note:

I'm not too sure what to say about the legality of this project.  I read the entire vehicle code for my state.  There was no reference to brake light flashers for cars or motorcycles.  The vehicle code for Michigan does prohibit " rotating, oscillating or flashing lights " on non-emergency vehicles, however I believe my brake light does not qualify as the clear intent in the vehicle code was " rotating, oscillating or flashing " lights that continue to do so for an extended period of time as emergency or police vehicles do. Really, my device is no different than tapping your brake pedal several times before stopping your motorcycle.  In the end, I did send an email to the state of Michigan requesting clarification.  At the time of this writing there has been no response.  I think the bottom line is, don't modify your vehicle unless you are willing to accept full responsibility for any outcome.


Here are a couple pictures of the build process.  The abstract looking pictures are of the LED light engine shining thru my desk magnifier onto the ceiling.