Akademisk Radioklubb


Category: Guides (page 1 of 5)

A brief introduction to learning morse code

Demonstrating knowledge of Morse code was previously a part of the amateur radio exam in Norway. Licensees would get either LA, LB or LC licenses based on their proficiency (or lack of proficiency) in this. After 2005, the requirement was removed, and all licenses granted since then were LA (and recently, LB) licenses, regardless of the skill level in Morse code. Subsequently, since learning Morse code no longer was a requirement for the “class A” license, the general proficiency in telegraphy among ARK’s member mass went down.

Still, there is some general interest in learning Morse. It is considered in ARK to be a nice skill to have, as it is a popular mode of operation, and there are contests both in Norway and abroad that consists only or partially of telegraphy. After the license course lecture on Wednesday 14.02.18, Jens – LB6RH therefore took the initiative to hold a brief session on the history of Morse code and how it can be learned.

This post summarizes the contents of that session, as a benefit to those wanting to learn Morse code for amateur radio.

Morse code was invented in the middle of 19th century, and was popularized through its use in the telegraph. With the advent of radio communications, Morse code continued to see use. For amateur radio enthusiasts, Morse code is still very popular. G7VJRs blogpost on the distribution of modes on the air, as recorded by Club Log, shows that between 1960 and 2013 around 40% of all amateur radio contacts were by Morse code.

Why is Morse code still so popular?

The signal processing between your ears is greatstudies have shown that a trained operator is able to decode Morse code signals as low as 12-18 dB below the noise floor.
It is narrowband – The power needed to convey information is much lower compared to more wideband modes such as voice (SSB).
The hardware is simple – Single frequency transceivers can be yours for as little as 3$, such as the pixie kit that we used for our soldering course, more advanced stuff that is capable of around-the-world contact can be had for around 100$.
It is fun 😉

In order to quickly learn morse code there are some do’s and don’ts.


  • Listen to morse code at full speed


  • Start by learning how to send/key Morse code
  • Visualize the letters with dots and dashes

The method is based on the Koch method, which was invented in the 1930s as a way to rapidly train telegraphists. The idea is that your brain should learn to decode by reflex. In order to do this, the student should exercise at a speed where the dots and dashes of adjacent letters blur into each other. This forces the brain to learn the rythm and sound of a letter, rather than translating from dots and dashes.

For this to be most effective, it is suggested that the student starts with only the two letters, K and M. Once those are 90 % successfully copied at 20-30 words per minute (WPM) for 2 minutes a third letter is added, and so on. The Koch method also specifies the sequence in which the letters should be learned.

There are multiple tools and applications that help you learn through the Koch method. Underneath are some tools for those who are learning (still learning the letters) and those who want to master (improve speed, retention and understanding).


lcwo.net – Learn CW online (browser)

IZ2UUF Koch Trainer – Android App


qrq – Copy callsigns at high speeds

RufzXP – Copy callsigns at high speeds

MorseRunner – Contest/QSO simulation

ebook2cw – Listen to your favorite books in CW

In order to ease the process in the beginning, Farnsworth spacing may be added. This separates letters in groups with some spacing between each letter and each letter group. A typical group size is 5-7 letters, and a typical spacing is about as long as the group size.

With daily exercise it takes about 1-3 months to learn Morse code. We look forward to checking up the status of the course attendees in a couple of months’ time :-).

Plotting Norwegian ham radio contacts on a map using Pandas, Cartopy and Geopy

In the post about the Norwegian telephony contest, we plotted the contacts on a map of Norway. Plotting contacts during short time periods like this can give a good visualization on the propagation conditions. In this particular case, it could yield some pointers on how we might do better the next time we run a similar contest. Due to sparse settlement patterns in Norway, however, this mostly demonstrates that Norway is a rather empty country. Regardless, they are nice illustrations :-).

Here, we outline how we did this using Pandas in Python. The more specific details apply only to Norwegian ham radio contacts, as we have used Norway’s database over the address information for Norwegian licensees. It could be applicable to other countries as well, given similar databases and some small tweaking to the scripts.

We will go through it step by step. First, we import all necessary libraries. Pandas for convenient data wrangling:

import pandas as pd

The Nomantim submodule of Geopy for conversion of addresses to coordinates using OpenStreetMap:

from geopy.geocoders import Nominatim

Matplotlib and cartopy for plotting of the data points on a nice map:

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.io.shapereader as shpreader

We could also have used Basemap, but in my short experience, cartopy seems to produce maps that have a lower threshold for looking nice by accident, with a bit more convenient API. This is probably extremely subjective :-).

First, we use the N1MM database reader introduced in a previous post to read the logs into a Pandas dataframe. Since we don’t need all the fields, we remove everything except for the callsign, timestamp and band fields.

qsos = analyze_logs.get_n1mm_logs_in_path('path_to_logs/')[['Call', 'TS', 'Band']]

We then download the official Norwegian ham database, which exists as an Excel sheet and a CSV file. This can conveniently be read using Pandas.read_excel(...), but in order to automatize the joining of this information and the QSO log, we’ll also rename the Norwegian ‘Kallesignal’-column to have the same name as the corresponding column in the QSO log, ‘Call’.

lahamsin = pd.read_excel('Liste over norske radioamatører (Excel) 03.10.2017.xlsx').rename(columns={'Kallesignal': 'Call'})

From geopy, we can get a fine granularity of the coordinates down to address level, but we’ll limit ourselves to the postal code in order to heighten the probability of obtaining an actual coordinate match. The address field can have some weird stuff like a post box number. We add the postal code information to the QSO table by running

qsos = pd.merge(qsos, lahamsin[['Call', 'Postnr']], on='Call')

Each contact in the log will now have its corresponding postal code. For conversion to coordinates, we’ll be using geopy.Nominatim(...), which can look up coordinates from addresses using OpenStreetmap. Geopy also has support for various other map APIs, but these typically require API keys or user authentication. We bias the look-up to Norway in order to probably play a bit more nice with the server.

geocoder = Nominatim(format_string='%s, Norway', country_bias='Norway')

Next, we obtain all unique postal numbers from the QSO table in order to limit the number of look-ups we have to do.

addresses = qsos[['Postnr']].drop_duplicates()

We then look up each postal number against the OpenStreetmap API using Pandas.DataFrame.apply(), which can apply a function row by row to the dataframe. In this case,
geocoder.geocode(...) is used to look up coordinates from the postal code in each row, assuming Norway as the country.

addresses["geocoder_res"] = addresses.apply(lambda x: geocoder.geocode({'postalcode': x.Postnr, 'country': 'Norway'}), axis=1)

Latitudes and longitudes are extracted in a similar way.

addresses['longitude'] = addresses.apply(lambda x: x.geocoder_res.longitude if x.geocoder_res is not None else None, axis=1)
addresses['latitude'] = addresses.apply(lambda x: x.geocoder_res.latitude if x.geocoder_res is not None else None, axis=1)

Merging back the addresses by joining on the postal code, we finally get a complete QSO table including the longitude/latitude coordinates of each contact.

qsos = pd.merge(qsos, addresses.drop('geocoder_res', axis=1), on='Postnr')
qsos = addresses.drop(['Postnr'], axis=1)

Now that we have wrangled all the data, we can finally turn to plotting :-). It can probably be a good idea to save the data to a file in order to avoid having to look up the coordinates against OpenStreetMap every time the script is run.

Cartopy has in-built support for automatically downloading Natural Earth shapefiles when a specific shapefile is requested (and then avoid having to download it again by caching the file). The shapefiles contains various information like country borders, roads, railroads, …, and can be used to plot the borders of a specific country like e.g. Norway down to various resolutions.

We use the admin_0_countries shapefile from the cultural database at 10 meters resolution.

shapefilename = shpreader.natural_earth(resolution='10m',
category='cultural', name='admin_0_countries')

I have not yet found a nice way to find the geometry shape corresponding to Norway except for looping through all records and stopping at “Norway”. There are probably better ways than this :-).

reader = shpreader.Reader(shapefilename)
countries = list(reader.records())
for country in countries:
   if country.attributes['NAME_LONG'] == 'Norway':
      norway = country.geometry

Now that we have the country borders of Norway, we can finally set the borders of Norway and prepare a country map.

projection = ccrs.Mercator()
ax = plt.axes(projection=projection)
ax.add_geometries([norway], projection, facecolor=(0.8, 0.8, 0.8))

Plotting all contacts made on the 80 meters band can then be done using e.g.

band_80m = qsos[qsos.Band == 3.5]
ax.plot(band_80m.longitude.dropna().as_matrix(), band_80m.latitude.dropna().as_matrix(), 'o')

This image is from one of the periods during the Norwegian ham radio contest “Telefonitesten”, and contains the 40m contacts and 80m contacts with different marker colors and sizes.

This should serve as a nice example on how data from three different sources can be combined using Pandas.

Older posts