Tag Archives: radio




TAFL — as a proper geodatabase





    never do this, okay?










Not really getting the Azimuthal Equidistant projection right






gs -sDEVICE=pdfwrite -sOutputFile=va3pid_world.pdf az_ini.ps -- az_proj.ps world.wdb

The format of the az_ini.ps file is complex, and I’m glad I’m an old PS hacker to be able to make head or tail of it.










This works rather well:




APRS is go!

Automatic Packet Reporting System — APRS — is rather clever. It’s a way of reporting position, status or messages via the amateur radio 2m band. Data is relayed via digipeaters, and routed to/from the internet APRS-IS system to any user worldwide.

It’s a little fiddly to set up, even with a very polished (read: $$) handheld radio like the Kenwood TH-D72A. I’m a bit disappointed that the purported SiRFstar III GPS in this radio takes forever to get a lock, but it’s a nice radio despite this.

The screenshot above shows aprs.fi‘s tracking of my handheld (VA3PID-7) last night as I walked to Toronto Mappy Hour.

Ham Radio log to interactive OpenStreetMap


Note that this program assumes you’ve downloaded your log from QRZ.com, as it requires the locator field for both inbound and outbound stations.

#!/usr/bin/perl -w
# adif2osm - convert ADIF log to OSM map file
# scruss.com / VA3PID - 2011/06/19

use strict;
use constant MARKERDIR =>
use constant QRZURL => 'http://qrz.com/db/';
sub maidenhead2latlong;

my ( $temp, @results ) = '';

### Fast forward past header
while (<>) {

### While there are records remaining...
while (<>) {
  $temp .= $_;

  ### Process if end of record tag reached
    my %hash;
    $temp =~ s/\n//g;
    foreach (@arr) {
      next if (/^$/);
    push @results, \%hash;
    $temp = '';

# generate OSM plugin file
my @data = ();
my ( $mygrid, $station_callsign ) = '';

# output header
foreach (@results) {
  $mygrid = $_->{my_gridsquare}
  $station_callsign = $_->{station_callsign}

  print join( "\t",
    join( ' - ', @data ),
    MARKERDIR . 'wpttemp-green.png',
    '0,-24' ),

  @data = ();

# show home station last, so it's on top
print join( "\t",
  '<a href="'
    . QRZURL
    . $station_callsign . '">'
    . $station_callsign . '</a>',
  'Home Station',
  MARKERDIR . 'wpttemp-red.png',
  '0,-24' ),


sub maidenhead2latlong {

  # convert a Maidenhead Grid location (eg FN03ir)
  #  to decimal degrees
  # this code could be cleaner/shorter/clearer
  my @locator =
  my $lat      = 0;
  my $long     = 0;
  my $latdiv   = 0;
  my $longdiv  = 0;
    ;                          # long,lat field size in seconds

      if ( $i % 2 ) {             # lat
      else {                        # long
        $longdiv = $divisors[$i];
    else {                          # alpha
      if ( $i % 2 ) {               # lat
        $lat += $val * $latdiv;
      else {                        # long
        $longdiv = $divisors[$i];
        $long += $val * $longdiv;
  $long += ( $longdiv / 2 );


The basic code to include a map is like this:

You’ll need to change the marker_file URL, too.


More radio amateur grid squares

Toronto, as understood by the Maidenhead Locator system

After yesterday’s post, I went a bit nuts with working out the whole amateur radio grid locator thing (not that I’m currently likely to use it, though). I’d hoped to provide a shapefile of the entire world, but that would be too big for the format’s 2GB file size limit.

What I can give you, though, is:

  • A Perl program that will generate a shapefile of an entire Maidenhead grid field, down to the subsquare level: make_grid.pl. You’ll need Geo::Shapelib to make this work. 324 (= 182) of these files would cover the whole world, and at 8MB or so a pop, things get unwieldy quickly.
  • A Google Earth KML file covering the whole world in 20° by 10° grid fields: Maidenhead_Locator_World_Grid. (If you’re feeling nerdy, here it is in Shapefile format: Maidenhead_Locator_World_Grid-shp).

If anyone would like their grid square in Google Earth format, let me know, or read on …

Making KML Files

Several people have asked, so here’s how you convert to KML. You’ll need the OGR toolkit installed, which comes in several open-source geo software bundles: FWTools/osgeo4w/QGis. Let’s assume we want to make the grid square ‘EN’.

    1. Run make_grid.pl:
      make_grid.pl en
    2. Convert to KML using ogr2ogr:
      ogr2ogr -f KML EN-maidenhead_grid.kml EN-maidenhead_grid.shp
    3. Alternatively, if you just want to extract a square (say EN82), you can use ogr2ogr’s ‘where’ clause to select just the geometry you want:
      ogr2ogr -f KML -where "Square='82'" EN82-maidenhead_grid.kml EN-maidenhead_grid.shp


Got a receiver inside my head – writing Spectrum Direct data as CSV

Industry Canada publishes the locations of all licensed radio spectrum users on Spectrum Direct. You can find all the transmitters/receivers near you by using its Geographical Area Search. And there are a lot near me:

While Spectrum Direct’s a great service, it has three major usability strikes against it:

  1. You can’t search by address or postal code; you need to know your latitude and longitude. Not just that, it expects your coordinates as a integer of the format DDMMSS.
  2. It’s very easy to overwhelm the system. Where I live, I can pretty much search for only 5km around me before the system times out.
  3. The output formats aren’t very useful. You can either get massively verbose XML, or very long line undelimited text, and neither of these are very easy to work with.

Never fear, Perl is here! I wrote a tiny script that glues together Dave O’Neill‘s Parse::SpectrumDirect::RadioFrequency module (which I wonder if you can guess what it does?) to Robbie Bow‘s  Text::CSV::Slurp module. The latter is used to blort out the former’s results to a CSV file that you can load into any GIS/mapping system.

Here’s the code:

#!/usr/bin/perl -w
# spectest.pl - generate CSV from Industry Canada Spectrum Direct data
# created by scruss on 02010/10/29 - for http://glaikit.org/

# usage: spectest.pl geographical_area.txt &gt; outfile.csv

use strict;
use Parse::SpectrumDirect::RadioFrequency;
use Text::CSV::Slurp;
use constant MINLAT =&gt; 40.0;    # all of Canada is &gt;40 deg N, for checking

my $prefetched_output = '';

# get the whole file as a string
while (&lt;&gt;) {
 $prefetched_output .= $_;

my $parser = Parse::SpectrumDirect::RadioFrequency-&gt;new();

# magically parse Spectrum Direct file
$parser-&gt;parse($prefetched_output) or die &quot;$!\n&quot;;
my $legend_hash = $parser-&gt;get_legend();    # get column descriptions
my @keys        = ();
foreach (@$legend_hash) {

 # retrieve column keys in order so the output will resemble input
 push @keys, $_-&gt;{key};

# get the data in a ref to an array of hashes
my $stations = $parser-&gt;get_stations();

my @good_stations = ();

# clean out bad values
foreach (@$stations) {
 next if ( $_-&gt;{Latitude} &lt; MINLAT );
 push @good_stations, $_;

# create csv file in memory then print it
my $csv = Text::CSV::Slurp-&gt;create(
 input       =&gt; \@good_stations,
 field_order =&gt; \@keys
print $csv;

The results aren’t perfect; QGis boaked on a file it made where one of the records appeared to have line breaks in it. It could filter out multiple pieces of equipment at the same call sign location. But it works, mostly, which is good enough for me.