Tag: adif

  • Ham Radio log to interactive OpenStreetMap

    You might notice that there’s now a Ham Radio QSO Map lurking on the front page. Thanks to the WordPress OpenStreetMap plugin (which I’ve slightly abused before). Here’s a small piece of Perl which will take your ADIF log and convert it to a WP-OSM marker file.

    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 =>
      'https://glaikit.org/wp-content/plugins/osm/icons/';
    use constant QRZURL => 'http://qrz.com/db/';
    sub maidenhead2latlong;
    
    my ( $temp, @results ) = '';
    
    ### Fast forward past header
    while (<>) {
      last if m/<eoh>\s+$/i;
    }
    
    ### While there are records remaining...
    while (<>) {
      $temp .= $_;
    
      ### Process if end of record tag reached
      if (m/<eor>\s+$/i) {
        my %hash;
        $temp =~ s/\n//g;
        $temp =~ s/<eoh>.*//i;
        $temp =~ s/<eor>.*//i;
        my @arr = split( '<', $temp );
        foreach (@arr) {
          next if (/^$/);
          my ( $key, $val ) = split( '>', $_ );
          $key =~ s/:.*$//;
          $hash{ lc($key) } = $val unless ( $key eq '' );
        }
        push @results, \%hash;
        $temp = '';
      }
    }
    
    # generate OSM plugin file
    my @data = ();
    my ( $mygrid, $station_callsign ) = '';
    
    # output header
    print
      join( "\t", qw/lat lon title description icon iconSize iconOffset/ ),
      "\n";
    foreach (@results) {
      next unless ( exists( $_->{gridsquare} ) && exists( $_->{call} ) );
      $mygrid = $_->{my_gridsquare}
        if ( exists( $_->{my_gridsquare} ) );
      $station_callsign = $_->{station_callsign}
        if ( exists( $_->{station_callsign} ) );
    
      push @data, $_->{freq} . ' MHz' if ( exists( $_->{freq} ) );
      $data[$#data] .= ' (' . $_->{band} . ')' if ( exists( $_->{band} ) );
      push @data, $_->{mode} if ( exists( $_->{mode} ) );
      push @data, $_->{qso_date} . ' ' . $_->{time_on} . 'Z'
        if ( exists( $_->{qso_date} ) && exists( $_->{time_on} ) );
      my ( $lat, $long ) = maidenhead2latlong( $_->{gridsquare} );
      print join( "\t",
        $lat,
        $long,
        '<a href="' . QRZURL . $_->{call} . '">' . $_->{call} . '</a>',
        join( ' - ', @data ),
        MARKERDIR . 'wpttemp-green.png',
        '0,-24' ),
        "\n";
    
      @data = ();
    }
    
    # show home station last, so it's on top
    my ( $lat, $long ) = maidenhead2latlong($mygrid);
    print join( "\t",
      $lat,
      $long,
      '<a href="'
        . QRZURL
        . $station_callsign . '">'
        . $station_callsign . '</a>',
      'Home Station',
      MARKERDIR . 'wpttemp-red.png',
      '0,-24' ),
      "\n";
    
    exit;
    
    sub maidenhead2latlong {
    
      # convert a Maidenhead Grid location (eg FN03ir)
      #  to decimal degrees
      # this code could be cleaner/shorter/clearer
      my @locator =
        split( //, uc(shift) );    # convert arg to upper case array
      my $lat      = 0;
      my $long     = 0;
      my $latdiv   = 0;
      my $longdiv  = 0;
      my @divisors = ( 72000, 36000, 7200, 3600, 300, 150 )
        ;                          # long,lat field size in seconds
      my $max = ( $#locator > $#divisors ) ? $#divisors : $#locator;
    
      for ( my $i = 0 ; $i <= $max ; $i++ ) {
        if ( int( $i / 2 ) % 2 ) {    # numeric
          if ( $i % 2 ) {             # lat
            $latdiv = $divisors[$i];    # save for later
            $lat += $locator[$i] * $latdiv;
          }
          else {                        # long
            $longdiv = $divisors[$i];
            $long += $locator[$i] * $longdiv;
          }
        }
        else {                          # alpha
          my $val = ord( $locator[$i] ) - ord('A');
          if ( $i % 2 ) {               # lat
            $latdiv = $divisors[$i];    # save for later
            $lat += $val * $latdiv;
          }
          else {                        # long
            $longdiv = $divisors[$i];
            $long += $val * $longdiv;
          }
        }
      }
      $lat  += ( $latdiv / 2 );         # location of centre of square
      $long += ( $longdiv / 2 );
      return ( ( $lat / 3600 ) - 90, ( $long / 3600 ) - 180 );
    }
    

    You’ll need to update MARKERDIR to reflect your own WP-OSM installation. Mine might move, so if you don’t change it, and you don’t get markers, please don’t blame me.

    The basic code to include a map is like this:

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

    Note that, while this script generates links into the QRZ callsign database, it doesn’t hit that site unless you click a link.