A dual band aprs digipeater with enhanced telemetry capabilities.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

308 lines
14 KiB

#!/bin/bash
##################################################################################
# Background process started by the initializing script start_aprs_server.sh #
# #
# It processes all incomming messages for PE1RXF and sends acknowledges when #
# if asked for (via kissutil for ax0 and beacon for ax1. It also prosesses the #
# special telemetry messages from PE1RXF devices. #
# #
# (C)2021 M.T. Konstapel https://meezenest.nl/mees #
# #
# This file is part of PE1RXF-APRS-server. #
# #
# PE1RXF-APRS-server is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# PE1RXF-APRS-server is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with PE1RXF-APRS-server If not, see <https://www.gnu.org/licenses/>. #
# #
##################################################################################
# Filters out all APRS messages to PE1RXF (all suffixes) and saves these to file.
# Generate and send acknowledge message when incomming message ask for it (only for -1, -2 and -3 suffixes).
# Also, reads APRS message and when it is telemetry converts it to a format readeble by gnuplot
#
# The script aprs_utils.sh starts a filters means of tail | grep on the log file of aprx.
# The output of this command goes to this script via stdin. This script reads this line by line
# and sends an acknowledgement to the stations asking for one. This is done via kissutil, which
# is also started the script aprs_utils.sh
# Directory where kissutils expect files to send
APRS_SEND_MESSAGE_DIRECTORY=/home/marcel/ham/aprs_utils/aprs_files_to_transmit/
# Directory where APRS messages are stored
APRS_RECEIVED_MESSAGES_DIR=/home/marcel/ham/aprs_utils/aprs_log/
MESSAGE_FILE=aprs_received_messages.log
# Get current date YYYY-MM-DD
#CURRENT_DATE=$(date +"%Y-%m-%d")
#Read file line by line and send acknowledge if needed
while read LINE
do
#################
# Section: filter out APRS messages and generate acknowledge if needed
#################
# Get current date YYYY-MM-DD (do this every time, because a day may already have passed)
CURRENT_DATE=$(date +"%Y-%m-%d")
CURRENT_DATE_TIME=$(date +"%Y-%m-%d %H:%M:%S")
# figure out who sent the message
# Check channel 1 (internal radio)
#CALL="$(grep -o -P "(?<=PE1RXF-1 R \*).+?\>"<<<$LINE)"
CALL="$(grep -oP "(?<=PE1RXF-1 R ).+?\>"<<<$LINE)"
PORT="ax0"
# If call is empty, frame not from channel 1, maybe from channel 2?
if [ -z $CALL ]; then
#CALL="$(grep -o -P "(?<=PE1RXF-2 R \*).+?\>"<<<$LINE)"
CALL="$(grep -oP "(?<=PE1RXF-2 R ).+?\>"<<<$LINE)"
PORT="ax1"
fi
# If call is empty, frame not from channel 1 or 2, maybe from channel 3?
if [ -z $CALL ]; then
#CALL="$(grep -o -P "(?<=PE1RXF-2 R \*).+?\>"<<<$LINE)"
CALL="$(grep -oP "(?<=PE1RXF-3 R ).+?\>"<<<$LINE)"
PORT="ax2"
fi
# If call is empty, frame not from channel 1, 2 or 3, maybe from channel APRSIS?
if [ -z $CALL ]; then
#CALL="$(grep -o -P "(?<=PE1RXF-2 R \*).+?\>"<<<$LINE)"
CALL="$(grep -oP "(?<=APRSIS R ).+?\>"<<<$LINE)"
PORT="aprsis"
fi
# Lets do something with the message from channel 1 or 2
if [[ $CALL ]]; then
# Remove last character from string
CALL=${CALL::-1}
ACKNOWLEDGE_NUMBER="$(grep -o -P '(?<={).*'<<<$LINE)"
#Write messages to file with name "YYYY-MM-DD_messages_from_NOCALL.log (append)
#echo "$LINE" >> "$APRS_RECEIVED_MESSAGES_DIR""$CURRENT_DATE""_messages_from_""$CALL"".log"
# Get full path of message
if [ $PORT == "ax0" ]; then
#FULL_PATH="$(grep -o -P '(?<=PE1RXF-1 R \*).+?:'<<<$LINE)"
FULL_PATH="$(grep -oP "(?<=PE1RXF-1 R ).+?:"<<<$LINE)"
elif [ $PORT == "ax1" ]; then
#FULL_PATH="$(grep -o -P '(?<=PE1RXF-2 R \*).+?:'<<<$LINE)"
FULL_PATH="$(grep -oP "(?<=PE1RXF-2 R ).+?:"<<<$LINE)"
elif [ $PORT == "ax2" ]; then
#FULL_PATH="$(grep -o -P '(?<=PE1RXF-2 R \*).+?:'<<<$LINE)"
FULL_PATH="$(grep -oP "(?<=PE1RXF-3 R ).+?:"<<<$LINE)"
elif [ $PORT == "aprsis" ]; then
#FULL_PATH="$(grep -o -P '(?<=PE1RXF-2 R \*).+?:'<<<$LINE)"
FULL_PATH="$(grep -oP "(?<=APRSIS R ).+?:"<<<$LINE)"
fi
FULL_PATH="$(grep -o -P '(?<=\>).*'<<<$FULL_PATH)"
# Remove last character from string
FULL_PATH=${FULL_PATH::-1}
# Messages direct from sender do not contain a '*' in the path
DIRECT="$(grep '*' <<<$FULL_PATH)"
if [ -z $DIRECT ]; then
DIRECT="true"
else
unset DIRECT
fi
# First call in path is destination
DESTINATION="$(awk -F "," '{ print $1} ' <<<$FULL_PATH)"
# get the message
MESSAGE="$(grep -o -P '(?<=::).*'<<<$LINE)"
MESSAGE="$(grep -o -P '(?<=:).*'<<<$MESSAGE)"
# get the receipient of the message
RECEIPIENT="$(grep -o -P '(?<=::).+?:'<<<$LINE)"
RECEIPIENT=${RECEIPIENT::-1}
# Write message to file
echo "$CURRENT_DATE_TIME"",""$PORT"",""$CALL"",""$DESTINATION",\"$MESSAGE\" >> "$APRS_RECEIVED_MESSAGES_DIR""$MESSAGE_FILE"
#Send incomming message as email
#echo "$LINE" | /home/marcel/ham/aprs_utils/send_aprs_message_to_email.sh
#Generate acknowledge frame and put in in de directory where kissutil can find (ax0) it or (for channels ax1 and ax2) send it via beacon
if [[ $RECEIPIENT == "PE1RXF-1 " || $RECEIPIENT == "PE1RXF-2 " || $RECEIPIENT == "PE1RXF-3 " ]]; then
if [[ $ACKNOWLEDGE_NUMBER ]]; then
# if call is less than 9 characters, add spaces at the end (according to the APRS protocol)
printf -v FORMATTED_CALL %-9.9s "$CALL"
OWN_CALL="$(grep "PE1RXF"<<<$CALL)"
# Message on port ax0
if [ $PORT == "ax0" ]; then
# Local (own) stations do not need to be digipeated
if [ $OWN_CALL ]; then
APRS_FRAME="PE1RXF-1>APRX29::$FORMATTED_CALL:ack$ACKNOWLEDGE_NUMBER"
# Everyone else is digipeated
else
APRS_FRAME="PE1RXF-1>APRX29,WIDE2-2::$FORMATTED_CALL:ack$ACKNOWLEDGE_NUMBER"
fi
# Send acknowledge frame
APRS_TRANSMIT_FILE="acknowledge_""$FORMATTED_CALL""_message_""$ACKNOWLEDGE_NUMBER"
echo "$APRS_FRAME" > "$APRS_SEND_MESSAGE_DIRECTORY$APRS_TRANSMIT_FILE"
# Message on port ax1
elif [ $PORT == "ax1" ]; then
# Local (own) stations do not need to be digipeated
if [ $OWN_CALL ]; then
APRS_HEADER="APRX29"
APRS_FRAME="::$FORMATTED_CALL:ack$ACKNOWLEDGE_NUMBER"
args[0]=-d
args[1]="APRX29"
args[2]=-s
args[3]=ax1
args[4]=":$FORMATTED_CALL:ack$ACKNOWLEDGE_NUMBER"
# Everyone else is digipeated
else
APRS_HEADER="APRX29 WIDE2-2"
APRS_FRAME="::$FORMATTED_CALL:ack$ACKNOWLEDGE_NUMBER"
args[0]=-d
args[1]="APRX29 WIDE2-2"
args[2]=-s
args[3]=ax1
args[4]=":$FORMATTED_CALL:ack$ACKNOWLEDGE_NUMBER"
fi
# use beacon to send acknowledge frame
#echo "/usr/sbin/beacon -d '$APRS_HEADER' -s ax1 '$APRS_FRAME'"
#/usr/sbin/beacon -d "$APRS_HEADER" -s ax1 "$APRS_FRAME"
/usr/sbin/beacon "${args[@]}"
# Message on port ax2
elif [ $PORT == "ax2" ]; then
# Local (own) stations do not need to be digipeated
if [ $OWN_CALL ]; then
APRS_HEADER="APRX29"
APRS_FRAME="::$FORMATTED_CALL:ack$ACKNOWLEDGE_NUMBER"
args[0]=-d
args[1]="APRX29"
args[2]=-s
args[3]=ax2
args[4]=":$FORMATTED_CALL:ack$ACKNOWLEDGE_NUMBER"
# Everyone else is digipeated
else
APRS_HEADER="APRX29 WIDE2-2"
APRS_FRAME="::$FORMATTED_CALL:ack$ACKNOWLEDGE_NUMBER"
args[0]=-d
args[1]="APRX29 WIDE2-2"
args[2]=-s
args[3]=ax2
args[4]=":$FORMATTED_CALL:ack$ACKNOWLEDGE_NUMBER"
fi
# use beacon to send acknowledge frame
#echo "/usr/sbin/beacon -d '$APRS_HEADER' -s ax1 '$APRS_FRAME'"
#/usr/sbin/beacon -d "$APRS_HEADER" -s ax1 "$APRS_FRAME"
/usr/sbin/beacon "${args[@]}"
# Message on port aprsis, send ack on LoRa as this channel has the most iGATEs
elif [ $PORT == "aprsis" ]; then
# Local (own) stations do not need to be digipeated
if [ $OWN_CALL ]; then
APRS_HEADER="APRX29"
APRS_FRAME="::$FORMATTED_CALL:ack$ACKNOWLEDGE_NUMBER"
args[0]=-d
args[1]="APRX29"
args[2]=-s
args[3]=ax2
args[4]=":$FORMATTED_CALL:ack$ACKNOWLEDGE_NUMBER"
# Everyone else is digipeated
else
APRS_HEADER="APRX29 WIDE2-2"
APRS_FRAME="::$FORMATTED_CALL:ack$ACKNOWLEDGE_NUMBER"
args[0]=-d
args[1]="APRX29 WIDE2-2"
args[2]=-s
args[3]=ax2
args[4]=":$FORMATTED_CALL:ack$ACKNOWLEDGE_NUMBER"
fi
# use beacon to send acknowledge frame
#echo "/usr/sbin/beacon -d '$APRS_HEADER' -s ax1 '$APRS_FRAME'"
#/usr/sbin/beacon -d "$APRS_HEADER" -s ax1 "$APRS_FRAME"
/usr/sbin/beacon "${args[@]}"
fi
fi
fi
#################
# Section: process APRS telemetry to format readable by gnuplot
#################
# get date field
DATE="$(echo $LINE | cut -b 1-10)"
# get time field
TIME="$(echo $LINE | cut -b 12-16)"
# get telemetry fields and remove empty spaces and optional acknowledge message if it is present
#TELEMETRY="$(echo $LINE | cut -b 64- | sed 's/{.*//' | tr -d "[:blank:]")"
TELEMETRY="$(echo $LINE | cut -b 63- | sed 's/{.*//' | tr -d "[:blank:]")"
echo $LINE
echo $TELEMETRY
#Datafiled should have comma as field seperator, so asume this and put values in array for later testing if data is valid
IFS=',' read -r -a array <<< "$TELEMETRY"
# Test if message contains valid numbers. It could be an acknowledge or beacon etc.
TELEMETRY_ERROR=0
for index in "${!array[@]}"
do
if ! [[ ${array[index]} =~ ^-?[0-9]+([.][0-9]+)?$ ]] ; then
TELEMETRY_ERROR=1
fi
done
# If telemetry message is valid, store it (change APRS date/time (UTM) with local time)
if [ $TELEMETRY_ERROR -eq 0 ] ; then
# Get current date and time YYYY-MM-DD %H:%M for local time stamp (do this every time, because a day may already have passed)
LOCAL_DATE_TIME=$(date +"%Y-%m-%d %H:%M")
#Format line by combining all needed fields
DATA_STRING="$(echo "$LOCAL_DATE_TIME","$TELEMETRY")"
#| tr -d "[:blank:]")"
#Write messages to file with name "YYYY-MM-DD_telemetry_NOCALL.log (append)
echo "$DATA_STRING" >> "$APRS_RECEIVED_MESSAGES_DIR""$CURRENT_DATE""_telemetry_""$CALL"".dat"
#Write last telemetry message to file (overwrite) for current values
echo "$TELEMETRY" > "$APRS_RECEIVED_MESSAGES_DIR""latest_telemetry_""$CALL"".dat"
fi
fi
# end channel 1
done < /dev/stdin