Skip to main content

MGL V6R ATC VHF Radio Control Protocol in Bash

The MGL V6R and V10, ATC VHF radios have a binary protocol (described in the V10 manual), in addition to the ubiquitous Garmin SL40 protocol.

The Emergency/Setup channel is 121.5 MHz.  Don't use it for testing...

In this example, I used the Bash let operator to do the calculations and printf is treated as a built-in function - TIMTOWTDI.

Frequency

Here is a simple Bash script that computes the frequency message (It doesn't check for out of range values!):

#! /bin/bash
echo MGL V6R ATC VHF Radio Protocol
echo

STX="02"        # Message header
DLE="05"        # Message header
CC="00"            # Active Frequency message ID = 00H, Standby Frequency message ID = 01H
KHZ="000000"    # Frequency in Kilohertz, decimal string (6 digits)
KHZH="000000"    # Frequency in Kilohertz, hexadecimal string (3 bytes)       
KHZ0="00"        # Frequency in Kilohertz, B0, LSB first
KHZ1="00"        # Frequency in Kilohertz, B1
KHZ2="00"        # Frequency in Kilohertz, B2, MSB last
SUM="00"        # XOR checksum from CC to last data byte, then XOR with 55H to invert some bits

if [ -n "$1" ]; then
KHZ=$1
echo "Frequency = $KHZ kHz Decimal"
echo

echo "Set serial port USB0 to 9600N81"
stty -F /dev/ttyUSB0 raw
stty -F /dev/ttyUSB0 9600
echo

echo "Compute frequency in hexadecimal"
printf -v KHZH "%06x" "$KHZ"
echo "Frequency MSB first = $KHZH Hex"

KHZ0=$(echo "$KHZH" | cut -c 5-6)
KHZ1=$(echo "$KHZH" | cut -c 3-4)
KHZ2=$(echo "$KHZH" | cut -c 1-2)
echo "Frequency LSB first = $KHZ0$KHZ1$KHZ2 Hex"
echo

# Bash by default handles numbers as decimal ASCII strings.
# A number starting with a zero is considered to be octal.
# A number starting with 0x is considered to be hexadecimal.
# The final modulo 256 reduces the answer to one byte
# and the printf makes it hexadecimal.
echo "Compute byte wide XOR checksum"
let "SUM=0x$CC ^ 0x$KHZ0 ^ 0x$KHZ1 ^ 0x$KHZ2"
let "SUM ^= 0x55"
let "SUM %= 256"
printf -v SUM "%02x" "$SUM"
echo "SUM = $SUM"
echo

# The format string "\x" tells echo to output each variable as an
# 8 bit binary value to the serial port,
# and not as two ASCII characters.
echo "Frequency message = $STX $DLE $CC $KHZ0 $KHZ1 $KHZ2 $SUM"
echo -en "\x$STX\x$DLE\x$CC\x$KHZ0\x$KHZ1\x$KHZ2\x$SUM" > /dev/ttyUSB0
echo

echo "La Voila!"
exit 1

fi
echo Example Emergency Frequency: f 121500
exit 0


I fixed a formatting bug in the checksum routine - these should work OK now.

Volume

Here are two more scripts to change the volume:

#! /bin/bash
# V6R Volume Down

echo MGL V6R ATC VHF Radio Protocol
echo

STX="02"        # Message header
DLE="05"        # Message header
CC="03"         # Volume DOWN message ID = 03H
D0="00"         # Unused
SUM="00"        # XOR checksum from CC to last data byte, then XOR with 55H to invert some bits

echo "Set serial port USB0 to 9600N81"
stty -F /dev/ttyUSB0 raw
stty -F /dev/ttyUSB0 9600
echo


# Bash by default handles numbers as decimal ASCII strings.
# A number starting with a zero is considered to be octal.
# A number starting with 0x is considered to be hexadecimal.
# The final modulo 256 reduces the answer to one byte
# and the printf makes it hexadecimal.
echo "Compute byte wide XOR checksum"
let "SUM=0x$CC ^ 0x$D0"
let "SUM ^= 0x55"
let "SUM %= 256"
printf -v SUM "%02x" "$SUM"
echo "SUM = $SUM"
echo

# The format string "\x" tells echo to output each variable as an
# 8 bit binary value to the serial port,
# and not as two ASCII characters.
echo "Volume Up message = $STX $DLE $CC $D0 $SUM"
echo -en "\x$STX\x$DLE\x$CC\x$D0\x$SUM" > /dev/ttyUSB0
echo

echo "La Voila!"
exit 1


and the other way:

#! /bin/bash
# V6R Volume Up

echo MGL V6R ATC VHF Radio Protocol
echo

STX="02"        # Message header
DLE="05"        # Message header
CC="02"         # Volume UP message ID = 02H
D0="00"         # Unused
SUM="00"        # XOR checksum from CC to last data byte, then XOR with 55H to invert some bits

echo "Set serial port USB0 to 9600N81"
stty -F /dev/ttyUSB0 raw
stty -F /dev/ttyUSB0 9600
echo


# Bash by default handles numbers as decimal ASCII strings.
# A number starting with a zero is considered to be octal.
# A number starting with 0x is considered to be hexadecimal.
# The final modulo 256 reduces the answer to one byte
# and the printf makes it hexadecimal.
echo "Compute byte wide XOR checksum"
let "SUM=0x$CC ^ 0x$D0"
let "SUM ^= 0x55"
let "SUM %= 256"
printf -v SUM "%02x" "$SUM"
echo "SUM = $SUM"
echo

# The format string "\x" tells echo to output each variable as an
# 8 bit binary value to the serial port,
# and not as two ASCII characters.
echo "Volume Down message = $STX $DLE $CC $D0 $SUM"
echo -en "\x$STX\x$DLE\x$CC\x$D0\x$SUM" > /dev/ttyUSB0
echo

echo "La Voila!"
exit 1


By implementing this in a Bash script, any problems can be fixed easily and one will probably only need to rearrange some variables in the final echo statements to make it work.

Zenity Press To Talk

Here is a more advanced script that uses Zenity to pretty things up and key the radio PTT over the serial port.  Note that if the radio 'hits you with a flat ignore', then you need to upgrade the firmware, since there is an old version around that has a PTT protocol bug:

#! /bin/bash
echo VHF PTT Script
echo Depends on: zenity, stty

echo "Set serial port USB0 to 9600N81"
stty -F /dev/ttyUSB0 raw
stty -F /dev/ttyUSB0 9600
sleep 1

# Message Protocol
# Repeat PTT message every 100 ms, timeout after 500 ms
STX="02"        # Message header
DLE="05"        # Message header
CC="0B"         # PTT message ID = 0BH
PTT="01"        # OFF=00H, ON=01H, default to ON
SUM="00"        # XOR checksum from CC to last data byte, then XOR with 55H to invert some bits

# Bash by default handles numbers as decimal ASCII strings.
# A number starting with a zero is considered to be octal.
# A number starting with 0x is considered to be hexadecimal.
# The final modulo 256 reduces the answer to one byte
# and the printf makes it hexadecimal.
let "SUM=0x$CC ^ 0x$PTT"
let "SUM ^= 0x55"
let "SUM %= 256"
printf -v SUM "%02x" "$SUM"
echo "SUM = $SUM"
echo "PTT message = $STX $DLE $CC $PTT $SUM"

# See the Zenity progress manual for details:
# https://help.gnome.org/users/zenity/stable/progress.html.en
PTT=0
(
while true; do
  echo $PTT
  echo "# PTT ON..."
  sleep 0.1

  let "PTT=$PTT+10"
  if [ "$PTT" -eq "100" ]; then
    PTT=0
  fi

  # Repeatedly send PTT ON message
  # The format string "\x" tells echo to output each variable as an
  # 8 bit binary value to the serial port,
  # and not as two ASCII characters.
  echo -en "\x$STX\x$DLE\x$CC\x$PTT\x$SUM" > /dev/ttyUSB0

done
) |
zenity --progress \
--title="VHF Radio" \
--text="PTT ON" \
--percentage=0

# Wait a little bit to avoid clipping the end of the transmission
sleep 0.2
echo "PTT OFF"

PTT=0
let "SUM=0x$CC ^ 0x$PTT"
let "SUM ^= 0x55"
let "SUM %= 256"
printf -v SUM "%02x" "$SUM"
echo "SUM = $SUM"
echo "PTT message = $STX $DLE $CC $PTT $SUM"
echo -en "\x$STX\x$DLE\x$CC\x$PTT\x$SUM" > /dev/ttyUSB0

# Wait for the dust to settle
sleep 0.1

exit 0

Zenity Frequency

This is a prettied script to set the frequency using Zenity sliders:

#! /bin/bash
echo VHF Frequency Script
echo Version: 0.1, 16 Sep 2015
echo Depends on: zenity, stty

zenity --question \
--title="VHF Radio" \
--text="Set VHF Radio Frequency?" \
--ok-label="OK" \
--cancel-label="Cancel"

if [ $? -eq 1 ]; then
  echo Cancel
  exit
fi
echo OK


# VHF frequency range: 118.000 to 136.975 MHz

MM=$( zenity --scale \
--title "VHF Radio" \
--text "Frequency 1XX MHz" \
--min-value=18 \
--max-value=36 \
--value=10 )

KK=$( zenity --scale \
--title "VHF Radio" \
--text "Frequency XX0 kHz" \
--min-value=0 \
--max-value=99 \
--value=10 )

K=$( zenity --scale \
--title "VHF Radio" \
--text "Frequency 00X kHz" \
--min-value=0 \
--max-value=9 \
--value=0 )

# Raw unvalidated frequency
echo Unvalidated Frequency = "1""$MM""$KK""$K"

# Valid frequencies must be divisible by 25 kHz
MMM="1""$MM"
KKK="$KK""$K"
echo MHz=$MMM
echo kHz=$KKK
let "MOD=$KKK % 25"
echo MOD=$MOD
let "KKK=$KKK - $MOD"
echo "Validated Frequency = "$MMM"".""$KKK" MHz"


echo Start a progress dialogue
zenity --progress \
--width=350 \
--title="VHF Radio" \
--text="Set VHF Radio Frequency...   " \

--pulsate &
PID=$!


echo MGL V6R ATC VHF Radio Protocol
echo

STX="02"        # Message header
DLE="05"        # Message header
CC="00"         # Active Frequency message ID = 00H, Standby Frequency message ID = 01H
KHZ="000000"    # Frequency in Kilohertz, decimal string (6 digits)
KHZH="000000"   # Frequency in Kilohertz, hexadecimal string (3 bytes)      
KHZ0="00"       # Frequency in Kilohertz, B0, LSB first
KHZ1="00"       # Frequency in Kilohertz, B1
KHZ2="00"       # Frequency in Kilohertz, B2, MSB last
SUM="00"        # XOR checksum from CC to last data byte, then XOR with 55H to invert some bits

# Decimal frequency
KHZ="$MMM""$KKK"
echo "Frequency = $KHZ kHz Decimal"
echo

echo "Set serial port USB0 to 9600N81"
stty -F /dev/ttyUSB0 raw
stty -F /dev/ttyUSB0 9600

# Wait for the dust to settle
sleep 1

echo "Compute frequency in hexadecimal"
printf -v KHZH "%06x" "$KHZ"
echo "Frequency MSB first = $KHZH Hex"

KHZ0=$(echo "$KHZH" | cut -c 5-6)
KHZ1=$(echo "$KHZH" | cut -c 3-4)
KHZ2=$(echo "$KHZH" | cut -c 1-2)
echo "Frequency LSB first = $KHZ0$KHZ1$KHZ2 Hex"
echo

# Bash by default handles numbers as decimal ASCII strings.
# A number starting with a zero is considered to be octal.
# A number starting with 0x is considered to be hexadecimal.
# The final modulo 256 reduces the answer to one byte
# and the printf makes it hexadecimal.
let "SUM=0x$CC ^ 0x$KHZ0 ^ 0x$KHZ1 ^ 0x$KHZ2"
let "SUM ^= 0x55"
let "SUM %= 256"
printf -v SUM "%02x" "$SUM"
echo "SUM = $SUM"
echo

# The format string "\x" tells echo to output each variable as an
# 8 bit binary value to the serial port,
# and not as two ASCII characters.
echo "Frequency message = $STX $DLE $CC $KHZ0 $KHZ1 $KHZ2 $SUM"
echo -en "\x$STX\x$DLE\x$CC\x$KHZ0\x$KHZ1\x$KHZ2\x$SUM" > /dev/ttyUSB0

# Wait for the dust to settle
sleep 1

echo Kill the progress dialogue
kill $PID

zenity --info \
--title "VHF Radio" \
--text "Frequency = "$MMM"".""$KKK" MHz" \
-- no-wrap

echo Done!


Use at your own peril though...

Interactive Scripts with 'chat'

A typical exchange with a radio or other embedded device goes something like:
  • Send a command
  • Get a response
  • If the response was good, then do an action
  • If the response was bad, then quit
Automating that with echo, cat and if statements is difficult.  The chat program is part of the pppd package and is probably installed by default.  You could use chat to do scripts more effectively than with standard bash commands.

http://www.tldp.org/HOWTO/PPP-HOWTO/x1219.html

For example, receive ERROR, then abort, receive nothing then send at&v, receive OK then set register 9 to 2, or timeout after 3 seconds:
$ chat -v TIMEOUT 3 ABORT ERROR '' at&v OK ats9=2 </dev/ttyUSB0 >/dev/ttyUSB0 

With a few lines like the above in a script, you can do most anything with a simple radio modem device.

Serial Port Redirection

When playing with serial ports in scripts, you will find that each time you open and close the port, it takes time, so it can drop characters and the buffer contents become unknown also.

A simple trick to avoid opening and closing the port with each line of code, is to use the exec program to do permanent redirection of the stdio files.  After that, any program that reads and writes to stdio will talk to the serial port.  This is perhaps better explained with an example:

DEV="/dev/ttyUSB0"
exec 3<&1 >"$DEV" <"$DEV"

File handle 0 is stdin, filehandle 1 is stdout and 2 is stderror, so 3<&1 means redirect stdout to a new handle 3 and the >"$DEV" means simultaneously also redirect it to the USB serial port and lastly, <"$DEV" means redirect the USB serial port to stdin.

From then on, your chat script doesn't have to add <"$DEV" >"$DEV" to the end of every line and if you need to echo a message to stdout, you can use handle 3 also.

More explanations on exec redirection here: http://wiki.bash-hackers.org/howto/redirection_tutorial

Serial Port Tips

www.aeronetworks.ca/2015/01/serial-port-io.html
www.aeronetworks.ca/2014/10/serial-ports-revisited.html
www.aeronetworks.ca/2014/01/crcs-and-serial-ports.html
www.aeronetworks.ca/2013/10/serial-port-tricks.html
www.aeronetworks.ca/2013/05/usb-serial-device-with-unknown-ids.html
www.aeronetworks.ca/2015/10/reading-and-parsing-data-from-serial.html
www.aeronetworks.ca/2013/05/compile-moxa-serial-widget-device.html

 
La voila!

Herman

Comments

Popular posts from this blog

Parasitic Quadrifilar Helical Antenna

This article was reprinted in OSCAR News, March 2018:  http://www.amsat-uk.org If you want to receive Satellite Weather Pictures , then you need a decent antenna, otherwise you will receive more noise than picture. For polar orbit satellites, one needs an antenna with a mushroom shaped radiation pattern .  It needs to have strong gain towards the horizon where the satellites are distant, less gain upwards where they are close and as little as possible downwards, which would be wasted and a source of noise.  Most satellites are spin stabilized and therefore the antenna also needs circular polarization, otherwise the received signal will flutter as the antennas rotate through nulls. The helical antenna, first proposed by Kraus in 1948, is the natural solution to circular polarized satellite communications.  It is a simple twisted wire - there seems to be nothing to it.  Various papers have been published on helix antennas, so the operation is pretty well understood. Therefore,

Weather Satellite Turnstile Antennas for the 2 meter Band

NEC2, 2 m band, 146 MHz, Yagi Turnstile Simulation and Build This article describes a Turnstile Antenna for the 2 meter band, 146 MHz amateur satcom, 137 MHz NOAA and Russian Meteor weather satellites.  Weather satellite reception is described here .  A quadrifilar helical antenna is described here .   Engineering, is the art of making what you need,  from what you can get. Radiation Pattern of the Three Element Yagi-Uda Antenna Once one combine and cross two Yagis, the pattern becomes distinctly twisted. The right hand polarization actually becomes visible in the radiation pattern plot, which I found really cool. Radiation Pattern of Six Element Turnstile Antenna Only a true RF Geek can appreciate the twisted invisible inner beauty of a herring bone antenna... Six Element Turnstile Antenna Essentially, it is three crosses on a stick.  The driven elements are broken in the middle at the drive points.  The other elements can go straight throug

Patch Antenna Design with NEC2

The older free Numerical Electromagnetic Code version 2 (NEC2) from Lawrence Livermore Lab assumes an air dielectric.  This makes it hard (but not impossible) for a radio amateur to experiment with Printed Circuit Board Patch antennas and micro strip lines. Air Spaced Patch Antenna Radiation Pattern You could use the free ASAP simulation program , which handles thin dielectrics, you could shell out a few hundred Dollars for a copy of NEC4 , You could buy GEMACS if you live in the USA, or you could add distributed capacitors to a NEC2 model with LD cards (hook up one capacitor in the middle of each element.), but that is far too much money/trouble for most. More information on driving an array antenna can be found here: https://www.aeronetworks.ca/2019/03/driving-quad-patch-array-antenna.htm l Air Dielectric Patch   The obvious lazy solution is to accept the limitation and make an air dielectric patch antenna. An advantage of using air dielectric, is that the antenn