From dac68c038ce8c45c1db9948d4a94be2e261daa78 Mon Sep 17 00:00:00 2001 From: thetechprepper Date: Sat, 16 Aug 2025 15:05:34 -0700 Subject: [PATCH 01/17] Initial voacapl install script --- scripts/install-voacap.sh | 52 +++++++++++++++++++++++++++++++++++++++ scripts/install.sh | 5 +++- tests/test-voacap.sh | 7 ++++++ 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100755 scripts/install-voacap.sh create mode 100755 tests/test-voacap.sh diff --git a/scripts/install-voacap.sh b/scripts/install-voacap.sh new file mode 100755 index 0000000..2e60a61 --- /dev/null +++ b/scripts/install-voacap.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# Author : Gaston Gonzalez +# Date : 5 June 2023 +# Updated : 16 August 2025 +# Purpose : Install Dire Wolf +# +# References: +# 1. https://github.com/jawatson/voacapl +# 2. https://github.com/jawatson/voacapl/wiki +# 3. http://www.greg-hand.com/hfwin32.html +set -e +trap 'last_command=$current_command; current_command=$BASH_COMMAND' DEBUG +trap 'et-log "\"${last_command}\" command failed with exit code $?."' ERR + +. ./env.sh + +APP=voacapl +VERSION=0.7.6 +REPO=https://github.com/thetechprepper/voacapl +REPO_SRC_DIR="/opt/src/${APP}" +INSTALL_DIR="/opt/${APP}-${VERSION}" +LINK_PATH="/opt/${APP}" + +et-log "Installing voacapl build dependencies..." +apt install gfortran -y + +[[ ! -e ${ET_SRC_DIR} ]] && mkdir ${ET_SRC_DIR} + +CWD_DIR=`pwd` + +if [[ ! -e ${REPO_SRC_DIR} ]]; then + cd ${ET_SRC_DIR} && git clone ${REPO} +fi + +cd ${REPO_SRC_DIR} +./configure --prefix=${INSTALL_DIR} +make && make install + +# patch prefix location +sed -i 's|__PREFIX__|/opt/voacapl-0.7.6|' makeitshfbc + +[[ -e /root/itshfbc ]] && rm -rf /root/itshfbc +[[ -e /etc/skel/itshfbc ]] && rm -rf /etc/skel/itshfbc + +./makeitshfbc && mv /root/itshfbc /etc/skel + +[[ -e ${LINK_PATH} ]] && rm ${LINK_PATH} +ln -s ${INSTALL_DIR} ${LINK_PATH} + +cd ${CWD_DIR} + +stow -v -d /opt ${APP} -t /usr/local diff --git a/scripts/install.sh b/scripts/install.sh index 51db9d1..655e658 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -2,7 +2,7 @@ # # Author : Gaston Gonzalez # Date : 16 March 2024 -# Updated : 13 August 2025 +# Updated : 16 August 2025 # Purpose : Main installer for EmComm Tools Community (ETC) . ./env.sh @@ -84,3 +84,6 @@ exitIfNotRoot # Install documentation tools ./install-dictionary.sh #./install-doc-tools.sh + +# Install prediction tools +./install-voacap.sh diff --git a/tests/test-voacap.sh b/tests/test-voacap.sh new file mode 100755 index 0000000..04836c3 --- /dev/null +++ b/tests/test-voacap.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# Author : Gaston Gonzalez +# Date : 16 August 2025 +# Purpose : Test VOACAP installation + +OUT=$(which voacapl) +exit $? From e54df6de10b3e3dd94ed09d617038bb8bea89ffb Mon Sep 17 00:00:00 2001 From: thetechprepper Date: Sat, 16 Aug 2025 15:42:14 -0700 Subject: [PATCH 02/17] voacap - Initial ported of VOACAP script from unreleased version of EmComm Tools R2. --- .../share/emcomm-tools/voacap/et-voacap.sh | 140 ++++++++++++++++++ .../share/emcomm-tools/voacap/fetch-ssn.sh | 15 ++ .../.local/share/emcomm-tools/voacap/rel.pl | 133 +++++++++++++++++ .../.local/share/emcomm-tools/voacap/ssn.txt | 18 +++ 4 files changed, 306 insertions(+) create mode 100755 overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh create mode 100755 overlay/etc/skel/.local/share/emcomm-tools/voacap/fetch-ssn.sh create mode 100755 overlay/etc/skel/.local/share/emcomm-tools/voacap/rel.pl create mode 100644 overlay/etc/skel/.local/share/emcomm-tools/voacap/ssn.txt diff --git a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh new file mode 100755 index 0000000..d66609d --- /dev/null +++ b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh @@ -0,0 +1,140 @@ +#!/bin/bash +# +# Author : Gaston Gonzalez +# Date : 23 May 2023 +# Updated : 16 August 2025 +# Purpose : Offline HF prediction using voacapl + +if [[ $# -ne 4 ]]; then + echo "Usage $(basename $0) " + echo " Callsign of transmitting station" + echo " Callsign of receiving station" + echo " Output power [5|100|500|1500]" + echo " Mode [AM|CW|FT8|SSB]" + exit 1 +fi + +ET_SSN="${HOME}/.local/share/emcomm-tools/voacap/ssn.txt" +if [[ ! -e ${ET_SSN} ]]; then + "Sunspot numbers not available: ${ET_SSN}. Exiting" + exit 1 +fi + +ET_VOA_WORKING_DIR=$HOME/itshfbc/run +ET_VOA_REPORT=${ET_VOA_WORKING_DIR}/voacapl.txt +INP=${ET_VOA_WORKING_DIR}/voacapx.dat +OUT=${ET_VOA_WORKING_DIR}/voacapx.out + +# Year = 2023 +YEAR=$(echo $(date '+%Y')) + +# Month (May) = 5.00 +MONTH=$(date +'%m') +MONTH_FMT=$(date +'%-m.00') + +####################################################################### +# TX Antenna +####################################################################### + +TX_STATION=$1 +TX_JSON=tx-station.json +curl -f -s http://localhost:1981/api/license?callsign=$TX_STATION > $TX_JSON +TL=$(cat $TX_JSON | jq .lat) +TK=$(cat $TX_JSON | jq .lon) + +TL1=$( awk -v n1=$TL -v n2=90 -v n3=-90 'BEGIN {if (n1n2) printf ("%s", "a"); else printf ("%.2f", n1);}' ) + +# add North or South (N/S) +TLA=$( awk -v n1=$TL1 -v n2=0 'BEGIN {if (n1n2) printf ("%s", "a"); else printf ("%.2f", n1);}' ) + +# add East or West (E/W) +TLO=$( awk -v n1=$TK1 -v n2=0 'BEGIN {if (n1 $RX_JSON +RL=$(cat $RX_JSON | jq .lat) +RK=$(cat $RX_JSON | jq .lon) + +echo "RX ($RX_STATION): $RL,$RK" +RL1=$( awk -v n1=$RL -v n2=90 -v n3=-90 'BEGIN {if (n1n2) printf ("%s", "a"); else printf ("%.2f", n1);}' ) + +# add North or South +RLA=$( awk -v n1=$RL1 -v n2=0 'BEGIN {if (n1n2) printf ("%s", "a"); else printf ("%.2f", n1);}' ) + +# add East or West +RLO=$( awk -v n1=$RK1 -v n2=0 'BEGIN {if (n1. (117.) +ssn=`grep "$YEAR $MONTH" ${ET_SSN} | awk '{print $5}' | cut -d"." -f1` + +# read more about fine-tuning your input file: +# http://www.voacap.com/voacapw.html +# http://www.voacap.com/frequency.html + +echo -e "\n" + +cat << END | tee $INP +LINEMAX 999 number of lines-per-page +COEFFS CCIR +TIME 1 24 1 1 +MONTH $YEAR $MONTH_FMT +SUNSPOT $ssn. +LABEL $TX$RX +CIRCUIT $TLA $TLO $RLA $RLO S 0 +SYSTEM 1. 155. 3.00 90. $MD 3.00 0.10 +FPROB 1.00 1.00 1.00 0.00 +ANTENNA 1 1 2 30 0.000[samples/sample.00 ] 0.0 $PW +ANTENNA 2 2 2 30 0.000[samples/sample.00 ] 0.0 0.0000 +FREQUENCY 3.60 5.30 7.1010.1014.1018.1021.1024.9028.20 0.00 0.00 +METHOD 30 0 +BOTLINES 8 12 21 +TOPLINES 1 2 3 4 6 +EXECUTE +QUIT + +END + +voacapl -s ~/itshfbc +echo "voacapl exited with $? using in=$INP and out=$OUT" + +(tail -n +27 $OUT | head -n 5 && grep -e'- REL' $OUT && grep -e'- S DBW' $OUT) | ./rel.pl > ${ET_VOA_REPORT} +cat ${ET_VOA_REPORT} diff --git a/overlay/etc/skel/.local/share/emcomm-tools/voacap/fetch-ssn.sh b/overlay/etc/skel/.local/share/emcomm-tools/voacap/fetch-ssn.sh new file mode 100755 index 0000000..f0b84c9 --- /dev/null +++ b/overlay/etc/skel/.local/share/emcomm-tools/voacap/fetch-ssn.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Author : Gaston Gonzalez +# Date : 16 August 2025 +# Purpose : Fetch SSNs for VOACAP prediction + +source /opt/emcomm-tools/bin/et-common + +FILE="ssn.txt" +URL="https://www.sidc.be/silso/FORECASTS/prediML.txt" +curl -s -f -L -o ${FILE} ${URL} + +if [[ $? -ne 0 ]]; then + echo -e "${RED}Failed to update ${FILE} from: ${URL}." + exit 1 +fi diff --git a/overlay/etc/skel/.local/share/emcomm-tools/voacap/rel.pl b/overlay/etc/skel/.local/share/emcomm-tools/voacap/rel.pl new file mode 100755 index 0000000..6448585 --- /dev/null +++ b/overlay/etc/skel/.local/share/emcomm-tools/voacap/rel.pl @@ -0,0 +1,133 @@ +#!/usr/bin/perl +# Author: Jari Perkiömäki OH6BG +# 12 June 2016: initial release +# 18 June 2016: added Long-Path predictions; use unpack for extracting table data +# 18 June 2016: added Power, Mode; small improvements +# + +use strict; +use warnings; + +my @relhour = (); +my @sdbwhour = (); +my @co = (); +my @mo = (); +my @pwr = (); +my @db = (); +my $km = 0; +my $mi = 0; +my $brn = 0; +my $fh; +my $voa; +my $mode; +my $path = "Short-Path"; + +# main loop +open $fh, '>>', \$voa or die $!; +process (); +close $fh; +print $voa; +exit; + +### end ### + +sub process { + my $format = 'A4 A5 A5 A5 A5 A5 A5 A5 A5 A5'; + while (<>) { + + s/^\s+|\s+$//g; + + if ( substr ($_, -1) eq "s" ) { @mo = split /\s+/; } + + elsif ( substr ($_, -2) eq "kW" ) { @pwr = split /\s+/; } + elsif ( substr ($_, -2) eq "dB" ) { + + @db = split /\s+/; + if ( $db[13] eq "24.0" ) { $mode = "CW"; } + elsif ( $db[13] eq "38.0" ) { $mode = "SSB"; } + else { $mode = "AM"; } + + } + + elsif ( /Long/ ) { $path = "Long-Path"; } + + elsif ( substr ($_, -1) =~ /\d/ ) { + + @co = split /\s+/; + $brn = sprintf "%d", $co[9] + 0.5; + $km = sprintf "%d", $co[12] + 0.5; + $mi = sprintf "%d", ($co[12] * 0.62137) + 0.5; + + } + + elsif ( substr ($_, -3) eq "REL" ) { push (@relhour, [ unpack( $format, $_ ) ] ); } + elsif ( substr ($_, -3) eq "DBW" ) { push (@sdbwhour,[ unpack( $format, $_ ) ] ); } + + } # while + + for ($fh) { + print $_ "VOACAP Prediction via $path. $mo[0] $mo[1]: SSN $mo[4] Power = $pwr[$#pwr], $mode\n"; + print $_ "TX ($co[0]$co[1], $co[2]$co[3]) to RX ($co[5]$co[6], $co[7]$co[8]): $km km, $mi mi, $brn deg\n\n"; + print $_ " | 01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|\n"; + } + + foreach my $freq (reverse 1..9) { + print $fh band($freq) . "|"; + foreach my $h (0..23) { print $fh makehourfreq( $h, $freq ); } + print $fh "|" . band($freq) . "\n"; + } + + for ($fh) { + print $_ " | 01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|\n\n"; + print $_ "A = 90 - 100% d = 25 - 49% * = REL 0%, but Signal Power over Noise\n"; + print $_ "B = 75 - 89% e = 10 - 24%\n"; + print $_ "C = 50 - 74% f = 1 - 9%\n\n"; + } + + @relhour = (); + @sdbwhour = (); + + } # process + +sub makehourfreq { + + my $h = $_[0]; + my $f = $_[1]; + my $r = $relhour[$h][$f]; + my $s = $sdbwhour[$h][$f]; + + if ( ($r == 0.00 && $s >= -167) ) { return " * "; } + else { return col($relhour[$h][$f]); } + + } + +sub band { + + my $b = $_[0]; + + if ($b == 1) { return "80"; } + if ($b == 2) { return "60"; } + if ($b == 3) { return "40"; } + if ($b == 4) { return "30"; } + if ($b == 5) { return "20"; } + if ($b == 6) { return "17"; } + if ($b == 7) { return "15"; } + if ($b == 8) { return "12"; } + if ($b == 9) { return "10"; } + + } + +sub col { + + my $rel = $_[0]; + + if ($rel == 0.00 ) { return " "; } + elsif ($rel >= 0.01 && $rel < 0.10 ) { return " f "; } + elsif ($rel >= 0.10 && $rel < 0.25 ) { return " e "; } + elsif ($rel >= 0.25 && $rel < 0.50 ) { return " d "; } + elsif ($rel >= 0.50 && $rel < 0.75 ) { return " C "; } + elsif ($rel >= 0.75 && $rel < 0.90 ) { return " B "; } + elsif ($rel >= 0.90 && $rel < 1.01 ) { return " A "; } + else { return " - "; } + + } \ No newline at end of file diff --git a/overlay/etc/skel/.local/share/emcomm-tools/voacap/ssn.txt b/overlay/etc/skel/.local/share/emcomm-tools/voacap/ssn.txt new file mode 100644 index 0000000..d7fd966 --- /dev/null +++ b/overlay/etc/skel/.local/share/emcomm-tools/voacap/ssn.txt @@ -0,0 +1,18 @@ +2025 02 2025.122 : 145.4 5.0 +2025 03 2025.204 : 145.5 10.2 +2025 04 2025.286 : 145.0 15.5 +2025 05 2025.371 : 143.1 20.7 +2025 06 2025.453 : 140.9 24.6 +2025 07 2025.538 : 137.3 26.5 +2025 08 2025.623 : 133.1 27.0 +2025 09 2025.705 : 129.4 27.3 +2025 10 2025.790 : 126.1 29.0 +2025 11 2025.873 : 123.2 30.0 +2025 12 2025.958 : 120.8 29.3 +2026 01 2026.042 : 118.0 28.2 +2026 02 2026.122 : 114.6 27.4 +2026 03 2026.204 : 110.4 25.8 +2026 04 2026.286 : 106.1 24.6 +2026 05 2026.371 : 102.1 23.8 +2026 06 2026.453 : 97.8 23.9 +2026 07 2026.538 : 94.0 25.1 From 3ea4c79a40bca99e22fda9c7fbf0f6ec383e446b Mon Sep 17 00:00:00 2001 From: thetechprepper Date: Sat, 16 Aug 2025 15:46:48 -0700 Subject: [PATCH 03/17] voacap - Added checks on the callsign location lookup --- .../skel/.local/share/emcomm-tools/voacap/et-voacap.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh index d66609d..9f1f13f 100755 --- a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh +++ b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh @@ -39,6 +39,11 @@ MONTH_FMT=$(date +'%-m.00') TX_STATION=$1 TX_JSON=tx-station.json curl -f -s http://localhost:1981/api/license?callsign=$TX_STATION > $TX_JSON +if [[ $? -ne 0 ]]; then + echo "Location not found for callsign: ${TX_STATION}. Exiting." + exit 1 +fi + TL=$(cat $TX_JSON | jq .lat) TK=$(cat $TX_JSON | jq .lon) @@ -60,6 +65,11 @@ TLO=$( awk -v n1=$TK1 -v n2=0 'BEGIN {if (n1 $RX_JSON +if [[ $? -ne 0 ]]; then + echo "Location not found for callsign: ${RX_STATION}. Exiting." + exit 1 +fi + RL=$(cat $RX_JSON | jq .lat) RK=$(cat $RX_JSON | jq .lon) From d3d7cbc137e54afc49747608cc547aef5c0c1434 Mon Sep 17 00:00:00 2001 From: thetechprepper Date: Sat, 16 Aug 2025 17:50:08 -0700 Subject: [PATCH 04/17] voacap - Replaced simple command line options to support get opt and 3 mode inputs: callsign, lat,lon and grid square. Grid square is pending et-api implementation --- .../share/emcomm-tools/voacap/et-voacap.sh | 140 +++++++++++++----- 1 file changed, 107 insertions(+), 33 deletions(-) diff --git a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh index 9f1f13f..97d6990 100755 --- a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh +++ b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh @@ -4,22 +4,113 @@ # Date : 23 May 2023 # Updated : 16 August 2025 # Purpose : Offline HF prediction using voacapl +source /opt/emcomm-tools/bin/et-common -if [[ $# -ne 4 ]]; then - echo "Usage $(basename $0) " - echo " Callsign of transmitting station" - echo " Callsign of receiving station" - echo " Output power [5|100|500|1500]" - echo " Mode [AM|CW|FT8|SSB]" - exit 1 -fi +lookup_station() { + local type="$1" # call | grid | latlon + local value="$2" # callsign | grid | "lat,lon" + local json_file="$3" + + case "$type" in + call) + curl -f -s "http://localhost:1981/api/license?callsign=${value}" > "$json_file" + if [[ $? -ne 0 ]]; then + echo "Location not found for callsign: ${value}. Exiting." + exit 1 + fi + ;; + grid) + echo "Grid not support not yet implemented" + exit 1 + + #curl -f -s "http://localhost:1981/api/geo/grid=${value}" > "$json_file" + #if [[ $? -ne 0 ]]; then + # echo "Location not found for grid: ${value}. Exiting." + # exit 1 + #fi + ;; + latlon) + # Write directly to JSON for consistency + local lat lon + IFS=',' read -r lat lon <<< "$value" + if [[ -z $lat || -z $lon ]]; then + echo "Invalid lat,lon format: $value. Expected 'lat,lon'. Exiting." + exit 1 + fi + jq -n --arg lat "$lat" --arg lon "$lon" '{lat: ($lat|tonumber), lon: ($lon|tonumber)}' > "$json_file" + ;; + *) + echo "Invalid station type: $type" + exit 1 + ;; + esac + + # Extract lat/lon regardless of type + local lat lon + lat=$(jq .lat "$json_file") + lon=$(jq .lon "$json_file") + + echo "$lat $lon" +} ET_SSN="${HOME}/.local/share/emcomm-tools/voacap/ssn.txt" if [[ ! -e ${ET_SSN} ]]; then - "Sunspot numbers not available: ${ET_SSN}. Exiting" + echo -e "${RED}Sunspot number file not available:${WHITE}${ET_SSN}${NC}" + echo -e "${YELLOW}Try running the following to fetch the sunspot numbers (requires Internet):${NC}" + echo -e "${WHITE}cd ${HOME}/.local/share/emcomm-tools/voacap${NC}" + echo -e "${WHITE}./fetch-ssn.sh${NC}" exit 1 fi +usage() { + echo "Usage: $(basename $0) [OPTIONS]" + echo + echo "Station options (choose one per TX and RX):" + echo " --tx-call CALLSIGN Transmitting station callsign" + echo " --tx-grid GRID Transmitting station Maidenhead grid" + echo " --tx-latlon LAT,LON Transmitting station coordinates in decimal degrees" + echo " --rx-call CALLSIGN Receiving station callsign" + echo " --rx-grid GRID Receiving station Maidenhead grid" + echo " --rx-latlon LAT,LON Receiving station coordinates in decimal degrees" + echo + echo "Other options:" + echo " -p POWER Output power [5|100|500|1500]" + echo " -m MODE Mode [AM|CW|JS8|SSB]" + echo + + exit 1 +} + +tx_type="" +tx_value="" +rx_type="" +rx_value="" +power="" +mode="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --tx-call) tx_type="call"; tx_value="$2"; shift 2 ;; + --tx-grid) tx_type="grid"; tx_value="$2"; shift 2 ;; + --tx-latlon) tx_type="latlon"; tx_value="$2"; shift 2 ;; + --rx-call) rx_type="call"; rx_value="$2"; shift 2 ;; + --rx-grid) rx_type="grid"; rx_value="$2"; shift 2 ;; + --rx-latlon) rx_type="latlon"; rx_value="$2"; shift 2 ;; + -p) power="$2"; shift 2 ;; + -m) mode="$2"; shift 2 ;; + -*|--*) echo "Unknown option: $1"; usage ;; + *) echo "Unexpected argument: $1"; usage ;; + esac +done + +# Validate required args +[[ -z $tx_type || -z $rx_type || -z $power || -z $mode ]] && usage + +#echo "TX ($tx_type): $tx_value" +#echo "RX ($rx_type): $rx_value" +#echo "Power: $power" +#echo "Mode: $mode" + ET_VOA_WORKING_DIR=$HOME/itshfbc/run ET_VOA_REPORT=${ET_VOA_WORKING_DIR}/voacapl.txt INP=${ET_VOA_WORKING_DIR}/voacapx.dat @@ -36,16 +127,8 @@ MONTH_FMT=$(date +'%-m.00') # TX Antenna ####################################################################### -TX_STATION=$1 -TX_JSON=tx-station.json -curl -f -s http://localhost:1981/api/license?callsign=$TX_STATION > $TX_JSON -if [[ $? -ne 0 ]]; then - echo "Location not found for callsign: ${TX_STATION}. Exiting." - exit 1 -fi - -TL=$(cat $TX_JSON | jq .lat) -TK=$(cat $TX_JSON | jq .lon) +read TL TK < <(lookup_station "$tx_type" "$tx_value" tx-station.json) +echo "TX Lat/Lon: $TL,$TK" TL1=$( awk -v n1=$TL -v n2=90 -v n3=-90 'BEGIN {if (n1n2) printf ("%s", "a"); else printf ("%.2f", n1);}' ) @@ -62,18 +145,9 @@ TLO=$( awk -v n1=$TK1 -v n2=0 'BEGIN {if (n1 $RX_JSON -if [[ $? -ne 0 ]]; then - echo "Location not found for callsign: ${RX_STATION}. Exiting." - exit 1 -fi +read RL RK < <(lookup_station "$rx_type" "$rx_value" rx-station.json) +echo "RX Lat/Lon: $RL,$RK" -RL=$(cat $RX_JSON | jq .lat) -RK=$(cat $RX_JSON | jq .lon) - -echo "RX ($RX_STATION): $RL,$RK" RL1=$( awk -v n1=$RL -v n2=90 -v n3=-90 'BEGIN {if (n1n2) printf ("%s", "a"); else printf ("%.2f", n1);}' ) # add North or South @@ -85,7 +159,7 @@ RK1=$( awk -v n1=$RK -v n2=180 -v n3=-180 'BEGIN {if (n1n2) printf ("% RLO=$( awk -v n1=$RK1 -v n2=0 'BEGIN {if (n1 Date: Sun, 17 Aug 2025 02:32:48 -0700 Subject: [PATCH 05/17] voacap - Added et-voacap wrapper script --- overlay/opt/emcomm-tools/bin/et-voacap | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100755 overlay/opt/emcomm-tools/bin/et-voacap diff --git a/overlay/opt/emcomm-tools/bin/et-voacap b/overlay/opt/emcomm-tools/bin/et-voacap new file mode 100755 index 0000000..10f085f --- /dev/null +++ b/overlay/opt/emcomm-tools/bin/et-voacap @@ -0,0 +1,14 @@ +#!/bin/bash +# +# Author : Gaston Gonzalez +# Date : 17 August 2025 +# Purpose : Wrapper script for VOACAP + +VOACAP_DIR=${HOME}/.local/share/emcomm-tools/voacap +CWD_DIR=$(pwd) + +cd ${VOACAP_DIR} + +./et-voacap.sh $@ + +cd ${CWD_DIR} From 414fe78907bfb7d9ca30f90837fe63d48d58eeaf Mon Sep 17 00:00:00 2001 From: thetechprepper Date: Sun, 17 Aug 2025 02:44:24 -0700 Subject: [PATCH 06/17] voacap - Added support for 20 watt power level --- .../share/emcomm-tools/voacap/et-voacap.sh | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh index 97d6990..17e5238 100755 --- a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh +++ b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh @@ -2,7 +2,7 @@ # # Author : Gaston Gonzalez # Date : 23 May 2023 -# Updated : 16 August 2025 +# Updated : 17 August 2025 # Purpose : Offline HF prediction using voacapl source /opt/emcomm-tools/bin/et-common @@ -74,7 +74,7 @@ usage() { echo " --rx-latlon LAT,LON Receiving station coordinates in decimal degrees" echo echo "Other options:" - echo " -p POWER Output power [5|100|500|1500]" + echo " -p POWER Output power [5|20|100|500|1500]" echo " -m MODE Mode [AM|CW|JS8|SSB]" echo @@ -158,18 +158,24 @@ RK1=$( awk -v n1=$RK -v n2=180 -v n3=-180 'BEGIN {if (n1n2) printf ("% # add East or West RLO=$( awk -v n1=$RK1 -v n2=0 'BEGIN {if (n1 Date: Sun, 17 Aug 2025 03:32:06 -0700 Subject: [PATCH 07/17] voacap - Enabled maidenhead grid square service --- .../.local/share/emcomm-tools/voacap/et-voacap.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh index 17e5238..e3db281 100755 --- a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh +++ b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh @@ -20,14 +20,11 @@ lookup_station() { fi ;; grid) - echo "Grid not support not yet implemented" - exit 1 - - #curl -f -s "http://localhost:1981/api/geo/grid=${value}" > "$json_file" - #if [[ $? -ne 0 ]]; then - # echo "Location not found for grid: ${value}. Exiting." - # exit 1 - #fi + curl -f -s "http://localhost:1981/api/geo/grid?gridSquare=${value}" | jq '{lat: .position.lat, lon: .position.lon}' > "$json_file" + if [[ $? -ne 0 ]]; then + echo "Error converting grid to lat/lon: ${value}. Exiting." + exit 1 + fi ;; latlon) # Write directly to JSON for consistency @@ -59,6 +56,7 @@ if [[ ! -e ${ET_SSN} ]]; then echo -e "${YELLOW}Try running the following to fetch the sunspot numbers (requires Internet):${NC}" echo -e "${WHITE}cd ${HOME}/.local/share/emcomm-tools/voacap${NC}" echo -e "${WHITE}./fetch-ssn.sh${NC}" + exit 1 fi From d8829ddea96c4dfe91629dc736b4d0f322e9e877 Mon Sep 17 00:00:00 2001 From: thetechprepper Date: Sun, 17 Aug 2025 03:58:26 -0700 Subject: [PATCH 08/17] voacap - Added error handling around API calls --- .../.local/share/emcomm-tools/voacap/et-voacap.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh index e3db281..2cbd890 100755 --- a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh +++ b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh @@ -4,6 +4,7 @@ # Date : 23 May 2023 # Updated : 17 August 2025 # Purpose : Offline HF prediction using voacapl +set -o pipefail source /opt/emcomm-tools/bin/et-common lookup_station() { @@ -22,7 +23,7 @@ lookup_station() { grid) curl -f -s "http://localhost:1981/api/geo/grid?gridSquare=${value}" | jq '{lat: .position.lat, lon: .position.lon}' > "$json_file" if [[ $? -ne 0 ]]; then - echo "Error converting grid to lat/lon: ${value}. Exiting." + echo "Error converting grid to lat/lon using value: ${value}. Exiting." exit 1 fi ;; @@ -125,8 +126,8 @@ MONTH_FMT=$(date +'%-m.00') # TX Antenna ####################################################################### -read TL TK < <(lookup_station "$tx_type" "$tx_value" tx-station.json) -echo "TX Lat/Lon: $TL,$TK" +result=$(lookup_station "$tx_type" "$tx_value" tx-station.json) +read TL TK <<< "$result" TL1=$( awk -v n1=$TL -v n2=90 -v n3=-90 'BEGIN {if (n1n2) printf ("%s", "a"); else printf ("%.2f", n1);}' ) @@ -143,8 +144,8 @@ TLO=$( awk -v n1=$TK1 -v n2=0 'BEGIN {if (n1n2) printf ("%s", "a"); else printf ("%.2f", n1);}' ) From 4930c5dc704df36a223b64915466f7b585f2aead Mon Sep 17 00:00:00 2001 From: thetechprepper Date: Wed, 20 Aug 2025 20:02:39 -0700 Subject: [PATCH 09/17] Post R5 Build 4 fixes - Added support for retry asset downloader --- overlay/opt/emcomm-tools/bin/et-common | 69 +++++++++++++++++++++++++- scripts/install-et-api.sh | 15 ++++-- 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/overlay/opt/emcomm-tools/bin/et-common b/overlay/opt/emcomm-tools/bin/et-common index bf45cf8..bc781dd 100755 --- a/overlay/opt/emcomm-tools/bin/et-common +++ b/overlay/opt/emcomm-tools/bin/et-common @@ -1,7 +1,7 @@ #!/bin/bash # Author : Gaston Gonzalez # Date : 1 February 2025 -# Updated : 7 June 2025 +# Updated : 20 August 2025 # Purpose : Common environment variables and functions # Standardized device paths for supported hardware @@ -74,3 +74,70 @@ start_and_wait_for_service() { exit_on_service_failure ${systemd_unit_name} } + +# download_with_retries downloads a file from a URL with retry and checksum support. +# +# This function attempts to download a file up to a maximum number of retries. +# If a checksum is provided, the downloaded file will be verified against it. +# On checksum mismatch, the file is deleted and the download retried. +# +# Args: +# url: The URL of the file to download. (required) +# filename: The local filename to save the file as. (required) +# checksum: Optional sha256 checksum string to validate the downloaded file. +# max_retries: Optional max number of retries (default: 3). +# wait_seconds: Optional wait time in seconds between retries (default: 2). +# +# Returns: +# 0 if the file was successfully downloaded (and checksum verified if given). +# 1 if arguments are missing, download fails after all retries, +# or checksum verification fails. +# +# Example: +# download_with_retries "https://example.com/file.tar.gz" "file.tar.gz" +# download_with_retries "https://example.com/file.tar.gz" "file.tar.gz" "abc123..." 5 3 +download_with_retries() { + local url="$1" + local filename="$2" + local checksum="$3" # expected checksum (sha256) + local max_retries="${4:-3}" # default = 3 retries + local wait_seconds="${5:-2}" # default = 2s wait between retries + + # Require at least URL and filename + if [[ -z "$url" || -z "$filename" ]]; then + echo "Usage: download_with_retries [checksum] [max_retries] [wait_seconds]" + return 1 + fi + + for ((i=1; i<=max_retries; i++)); do + echo "Attempt $i of $max_retries to download $url..." + + if curl -L -f -o "$filename" "$url"; then + echo "Download successful: $filename" + + if [[ -n "$checksum" ]]; then + echo "Verifying checksum..." + local file_checksum + file_checksum=$(sha256sum "$filename" | awk '{print $1}') + + if [[ "$file_checksum" == "$checksum" ]]; then + echo "Checksum OK" + return 0 + else + echo "Checksum mismatch (expected $checksum, got $file_checksum)" + rm -f "$filename" + fi + else + # no checksum provided, just succeed + return 0 + fi + else + echo "Download failed (attempt $i)." + fi + + sleep "$wait_seconds" + done + + echo "Download failed after $max_retries attempts." + return 1 +} diff --git a/scripts/install-et-api.sh b/scripts/install-et-api.sh index 29d517d..5ea12fb 100755 --- a/scripts/install-et-api.sh +++ b/scripts/install-et-api.sh @@ -7,6 +7,7 @@ trap 'last_command=$current_command; current_command=$BASH_COMMAND' DEBUG trap 'et-log "\"${last_command}\" command failed with exit code $?."' ERR . ./env.sh +. ../overlay/opt/emcomm-tools/bin/et-common APP=et-api VERSION=1.0.0 @@ -24,17 +25,23 @@ mkdir -v -p "${BIN_DIR}" "${DATA_DIR}" "${INDEX_DIR}" # Download and install et-api uber JAR URL="${BASE_URL}/${ET_API_JAR}" -et-log "Downloading ${APP}: ${URL}" -curl -s -L -o "${BIN_DIR}/${APP}" --fail ${URL} +download_with_retries ${URL} ${APP} +mv ${APP} ${BIN_DIR} chmod 755 "${BIN_DIR}/${APP}" # Download pre-built data sets -FILES=("faa.csv" "license.csv" "zip2geo.csv" "zip2geo-elevation.csv") +FILES=( + "faa.csv" + "license.csv" + "zip2geo.csv" + "zip2geo-elevation.csv" +) for file in "${FILES[@]}"; do URL="${BASE_URL}/${file}" et-log "Downloading data set: ${URL}" - curl -s -L -o "${DATA_DIR}/${file}" --fail ${URL} + download_with_retries ${URL} ${file} + mv ${file} ${DATA_DIR} done et-log "Applying permissions..." From e3edc70be0303bbd4ed415442a34e430b1d5a4b1 Mon Sep 17 00:00:00 2001 From: thetechprepper Date: Wed, 20 Aug 2025 20:09:53 -0700 Subject: [PATCH 10/17] Post R5 Build 4 fixes - Added download retry to et-aircraft-app --- scripts/install-et-aircraft.sh | 6 +++--- scripts/install-et-api.sh | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/install-et-aircraft.sh b/scripts/install-et-aircraft.sh index 806bd1b..7b7531d 100755 --- a/scripts/install-et-aircraft.sh +++ b/scripts/install-et-aircraft.sh @@ -1,12 +1,14 @@ #!/bin/bash # Author : Gaston Gonzalez # Date : 9 July 2025 +# Updated : 20 August 2025 # Purpose : Install et-aircraft-app set -e trap 'last_command=$current_command; current_command=$BASH_COMMAND' DEBUG trap 'et-log "\"${last_command}\" command failed with exit code $?."' ERR . ./env.sh +. ../overlay/opt/emcomm-tools/bin/et-common APP=et-aircraft-app VERSION=1.0.0 @@ -18,9 +20,7 @@ if [[ ! -e ${ET_DIST_DIR}/${DOWNLOAD_FILE} ]]; then URL="https://github.com/thetechprepper/et-aircraft-app/releases/download/${VERSION}/${DOWNLOAD_FILE}" - et-log "Downloading ${APP}: ${URL}" - curl -s -L -o ${DOWNLOAD_FILE} --fail ${URL} - + download_with_retries ${URL} ${DOWNLOAD_FILE} mv ${DOWNLOAD_FILE} ${ET_DIST_DIR} fi diff --git a/scripts/install-et-api.sh b/scripts/install-et-api.sh index 5ea12fb..4829230 100755 --- a/scripts/install-et-api.sh +++ b/scripts/install-et-api.sh @@ -1,6 +1,7 @@ #!/bin/bash # Author : Gaston Gonzalez # Date : 13 August 2025 +# Updated : 20 August 2025 # Purpose : Install EmComm Tools API set -e trap 'last_command=$current_command; current_command=$BASH_COMMAND' DEBUG From b7a57f408ab158a62a91a3a2b528a78869e006f3 Mon Sep 17 00:00:00 2001 From: thetechprepper Date: Wed, 20 Aug 2025 20:29:09 -0700 Subject: [PATCH 11/17] Post R5 Build 4 fixes - Added a more robust test case for the et-api installation as an experiment. --- tests/test-et-api.sh | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/tests/test-et-api.sh b/tests/test-et-api.sh index 919264a..b6d9bee 100755 --- a/tests/test-et-api.sh +++ b/tests/test-et-api.sh @@ -1,7 +1,23 @@ #!/bin/bash -# Author : Gaston Gonzalez -# Date : 13 August 2025 -# Purpose : Test et-api installation +# Author : Gaston Gonzalez +# Date : 13 August 2025 +# Updated : 20 August 2025 +# Purpose : Test et-api installation + +FILES=( + "faa.csv" + "license.csv" + "zip2geo.csv" + "zip2geo-elevation.csv" +) + +for file in "${FILES[@]}"; do + OUT=$(ls /opt/emcomm-tools-api/data/${file} 2>/dev/null) + if [[ $? -ne 0 ]]; then + echo -e "\t* Required file '$file' does not exist." + exit 1 + fi +done OUT=$(ls /opt/emcomm-tools-api/bin/et-api 2>/dev/null) exit $? From c7539c5b13f628d5ea4343cd759a9be81e994736 Mon Sep 17 00:00:00 2001 From: thetechprepper Date: Thu, 21 Aug 2025 18:23:57 -0700 Subject: [PATCH 12/17] voacap - Added power level argument check --- .../etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh index 2cbd890..b678368 100755 --- a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh +++ b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh @@ -2,7 +2,7 @@ # # Author : Gaston Gonzalez # Date : 23 May 2023 -# Updated : 17 August 2025 +# Updated : 21 August 2025 # Purpose : Offline HF prediction using voacapl set -o pipefail source /opt/emcomm-tools/bin/et-common @@ -174,7 +174,9 @@ elif [ "$PWR" = "500" ]; then elif [ "$PWR" = "1500" ]; then PW="1.2000" else - echo "Unsupported power level. Defaulting to 100 watts." + echo "Unsupported power level." + usage + exit 1 fi # Mode From 5d5981949526d65166d5ee5333de30a4241f3b8b Mon Sep 17 00:00:00 2001 From: thetechprepper Date: Sun, 24 Aug 2025 07:58:39 -0700 Subject: [PATCH 13/17] voacap - Added more robust error handling: 1) allow for mode to be in mixed case; 2) fail on callsign lookup, but provide user help. --- .../share/emcomm-tools/voacap/et-voacap.sh | 66 ++++++++++++------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh index b678368..5076984 100755 --- a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh +++ b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh @@ -2,9 +2,13 @@ # # Author : Gaston Gonzalez # Date : 23 May 2023 -# Updated : 21 August 2025 +# Updated : 24 August 2025 # Purpose : Offline HF prediction using voacapl +#set -e set -o pipefail +trap 'last_command=$current_command; current_command=$BASH_COMMAND' DEBUG +trap 'echo -e "${RED}\"${last_command}\" command failed with exit code $?.${NC}"' ERR + source /opt/emcomm-tools/bin/et-common lookup_station() { @@ -16,14 +20,14 @@ lookup_station() { call) curl -f -s "http://localhost:1981/api/license?callsign=${value}" > "$json_file" if [[ $? -ne 0 ]]; then - echo "Location not found for callsign: ${value}. Exiting." + echo -e "${RED}Location not found for callsign: ${value}.${NC}" >&2 exit 1 fi ;; grid) curl -f -s "http://localhost:1981/api/geo/grid?gridSquare=${value}" | jq '{lat: .position.lat, lon: .position.lon}' > "$json_file" if [[ $? -ne 0 ]]; then - echo "Error converting grid to lat/lon using value: ${value}. Exiting." + echo -e "${RED}Error converting grid to lat/lon using value: ${value}.${NC}" >&2 exit 1 fi ;; @@ -32,13 +36,13 @@ lookup_station() { local lat lon IFS=',' read -r lat lon <<< "$value" if [[ -z $lat || -z $lon ]]; then - echo "Invalid lat,lon format: $value. Expected 'lat,lon'. Exiting." + echo -e "${RED}Invalid lat,lon format: $value. Expected 'lat,lon'.${NC}" >&2 exit 1 fi jq -n --arg lat "$lat" --arg lon "$lon" '{lat: ($lat|tonumber), lon: ($lon|tonumber)}' > "$json_file" ;; *) - echo "Invalid station type: $type" + echo -e "${RED}Invalid station type: $type${NC}" >&2 exit 1 ;; esac @@ -53,7 +57,7 @@ lookup_station() { ET_SSN="${HOME}/.local/share/emcomm-tools/voacap/ssn.txt" if [[ ! -e ${ET_SSN} ]]; then - echo -e "${RED}Sunspot number file not available:${WHITE}${ET_SSN}${NC}" + echo -e "${RED}Sunspot number file not available:${WHITE}${ET_SSN}${NC}" echo -e "${YELLOW}Try running the following to fetch the sunspot numbers (requires Internet):${NC}" echo -e "${WHITE}cd ${HOME}/.local/share/emcomm-tools/voacap${NC}" echo -e "${WHITE}./fetch-ssn.sh${NC}" @@ -110,6 +114,28 @@ done #echo "Power: $power" #echo "Mode: $mode" +# Allow for case-insensitve match of the user-defined operating mode +MODE=$mode +case "${MODE,,}" in + js8) + MD="13.0" + ;; + cw) + MD="24.0" + ;; + ssb) + MD="38.0" + ;; + am) + MD="49.0" + ;; + *) + echo -e "${RED}Unknown mode: ${MODE}${NC}" >&2 + usage + exit 1 + ;; +esac + ET_VOA_WORKING_DIR=$HOME/itshfbc/run ET_VOA_REPORT=${ET_VOA_WORKING_DIR}/voacapl.txt INP=${ET_VOA_WORKING_DIR}/voacapx.dat @@ -126,7 +152,12 @@ MONTH_FMT=$(date +'%-m.00') # TX Antenna ####################################################################### -result=$(lookup_station "$tx_type" "$tx_value" tx-station.json) +if ! result=$(lookup_station "$tx_type" "$tx_value" tx-station.json); then + echo "Try specifying a grid or lat,lon for the transmitting station." + echo " --tx-grid GRID Transmitting station Maidenhead grid" + echo " --tx-latlon LAT,LON Transmitting station coordinates in decimal degrees" + exit 1 +fi read TL TK <<< "$result" TL1=$( awk -v n1=$TL -v n2=90 -v n3=-90 'BEGIN {if (n1n2) printf ("%s", "a"); else printf ("%.2f", n1);}' ) @@ -144,7 +175,12 @@ TLO=$( awk -v n1=$TK1 -v n2=0 'BEGIN {if (n1n2) printf ("%s", "a"); else printf ("%.2f", n1);}' ) @@ -179,20 +215,6 @@ else exit 1 fi -# Mode -MODE=$mode -MD="24.0" -echo "Mode: ${MODE}" -if [ "$MODE" = "JS8" ]; then - MD="13.0" -elif [ "$MODE" = "CW" ]; then - MD="24.0" -elif [ "$MODE" = "SSB" ]; then - MD="38.0" -elif [ "$opt" = "AM" ]; then - MD="49.0" -fi - # Format for 117 # NOTE: The card requires . (117.) ssn=`grep "$YEAR $MONTH" ${ET_SSN} | awk '{print $5}' | cut -d"." -f1` From 045d4418275129990a519dade2fec07b284934e2 Mon Sep 17 00:00:00 2001 From: thetechprepper Date: Sun, 24 Aug 2025 08:07:31 -0700 Subject: [PATCH 14/17] voacap - Only present alternate TX/RX suggestions on callsign lookups --- .../share/emcomm-tools/voacap/et-voacap.sh | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh index 5076984..cfc15df 100755 --- a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh +++ b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh @@ -153,9 +153,11 @@ MONTH_FMT=$(date +'%-m.00') ####################################################################### if ! result=$(lookup_station "$tx_type" "$tx_value" tx-station.json); then - echo "Try specifying a grid or lat,lon for the transmitting station." - echo " --tx-grid GRID Transmitting station Maidenhead grid" - echo " --tx-latlon LAT,LON Transmitting station coordinates in decimal degrees" + if [[ "$tx_type" == "call" ]]; then + echo "Try specifying a grid or lat,lon for the transmitting station." + echo " --tx-grid GRID Transmitting station Maidenhead grid" + echo " --tx-latlon LAT,LON Transmitting station coordinates in decimal degrees" + fi exit 1 fi read TL TK <<< "$result" @@ -176,9 +178,11 @@ TLO=$( awk -v n1=$TK1 -v n2=0 'BEGIN {if (n1 Date: Sun, 24 Aug 2025 08:27:45 -0700 Subject: [PATCH 15/17] voacap - Added validation for latitude values --- .../share/emcomm-tools/voacap/et-voacap.sh | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh index cfc15df..6ef0587 100755 --- a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh +++ b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh @@ -55,6 +55,32 @@ lookup_station() { echo "$lat $lon" } +validate_latitude() { + local lat="$1" + + if [[ -z "$lat" ]]; then + echo -e "${RED}Latitude can't be an empty value${NC}" >&2 + return 1 + fi + + if ! [[ "$lat" =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then + echo -e "${RED}Latitude must be a number${NC}" >&2 + return 1 + fi + + local latf + latf=$(printf "%.10f" "$lat" 2>/dev/null) || { + echo -e "${RED}Error converting latitude${NC}" >&2 + return 1 + } + + # Check range: -90 <= lat <= 90 + if (( $(echo "$latf < -90" | bc -l) )) || (( $(echo "$latf > 90" | bc -l) )); then + echo -e "${RED}Latitude must be in range between -90 and 90 degrees${NC}" >&2 + return 1 + fi +} + ET_SSN="${HOME}/.local/share/emcomm-tools/voacap/ssn.txt" if [[ ! -e ${ET_SSN} ]]; then echo -e "${RED}Sunspot number file not available:${WHITE}${ET_SSN}${NC}" @@ -162,6 +188,8 @@ if ! result=$(lookup_station "$tx_type" "$tx_value" tx-station.json); then fi read TL TK <<< "$result" +validate_latitude "$TL" || exit 1 + TL1=$( awk -v n1=$TL -v n2=90 -v n3=-90 'BEGIN {if (n1n2) printf ("%s", "a"); else printf ("%.2f", n1);}' ) # add North or South (N/S) @@ -187,6 +215,8 @@ if ! result=$(lookup_station "$rx_type" "$rx_value" rx-station.json); then fi read RL RK <<< "$result" +validate_latitude "$RL" || exit 1 + RL1=$( awk -v n1=$RL -v n2=90 -v n3=-90 'BEGIN {if (n1n2) printf ("%s", "a"); else printf ("%.2f", n1);}' ) # add North or South From 6c77261d6f15b976aad8e4bfd1dba3b80a58ac39 Mon Sep 17 00:00:00 2001 From: thetechprepper Date: Sun, 24 Aug 2025 08:40:00 -0700 Subject: [PATCH 16/17] voacap - Added validation for longitude values --- .../share/emcomm-tools/voacap/et-voacap.sh | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh index 6ef0587..2221ffc 100755 --- a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh +++ b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh @@ -4,7 +4,7 @@ # Date : 23 May 2023 # Updated : 24 August 2025 # Purpose : Offline HF prediction using voacapl -#set -e +set -e set -o pipefail trap 'last_command=$current_command; current_command=$BASH_COMMAND' DEBUG trap 'echo -e "${RED}\"${last_command}\" command failed with exit code $?.${NC}"' ERR @@ -81,6 +81,32 @@ validate_latitude() { fi } +validate_longitude() { + local lon="$1" + + if [[ -z "$lon" ]]; then + echo -e "${RED}Longitude can't be an empty value${NC}" >&2 + return 1 + fi + + if ! [[ "$lon" =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then + echo -e "${RED}Longitude must be a number${NC}" >&2 + return 1 + fi + + local lonf + lonf=$(printf "%.10f" "$lon" 2>/dev/null) || { + echo -e "${RED}Error converting longitude ${NC}" >&2 + return 1 + } + + # Check range: -180 <= lat <= 1800 + if (( $(echo "$lon < -180" | bc -l) )) || (( $(echo "$lon > 180" | bc -l) )); then + echo -e "${RED}Longitude must be in range between -180 and 180 degrees${NC}" >&2 + return 1 + fi +} + ET_SSN="${HOME}/.local/share/emcomm-tools/voacap/ssn.txt" if [[ ! -e ${ET_SSN} ]]; then echo -e "${RED}Sunspot number file not available:${WHITE}${ET_SSN}${NC}" @@ -189,6 +215,7 @@ fi read TL TK <<< "$result" validate_latitude "$TL" || exit 1 +validate_longitude "$TK" || exit 1 TL1=$( awk -v n1=$TL -v n2=90 -v n3=-90 'BEGIN {if (n1n2) printf ("%s", "a"); else printf ("%.2f", n1);}' ) @@ -216,6 +243,7 @@ fi read RL RK <<< "$result" validate_latitude "$RL" || exit 1 +validate_longitude "$RK" || exit 1 RL1=$( awk -v n1=$RL -v n2=90 -v n3=-90 'BEGIN {if (n1n2) printf ("%s", "a"); else printf ("%.2f", n1);}' ) From 9332808ed0c5c718468d4fe4a9e3b5601f793fec Mon Sep 17 00:00:00 2001 From: thetechprepper Date: Sun, 24 Aug 2025 08:43:50 -0700 Subject: [PATCH 17/17] voacap - Replaced exits with returns in lookup function --- .../skel/.local/share/emcomm-tools/voacap/et-voacap.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh index 2221ffc..e01b3d7 100755 --- a/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh +++ b/overlay/etc/skel/.local/share/emcomm-tools/voacap/et-voacap.sh @@ -21,14 +21,14 @@ lookup_station() { curl -f -s "http://localhost:1981/api/license?callsign=${value}" > "$json_file" if [[ $? -ne 0 ]]; then echo -e "${RED}Location not found for callsign: ${value}.${NC}" >&2 - exit 1 + return 1 fi ;; grid) curl -f -s "http://localhost:1981/api/geo/grid?gridSquare=${value}" | jq '{lat: .position.lat, lon: .position.lon}' > "$json_file" if [[ $? -ne 0 ]]; then echo -e "${RED}Error converting grid to lat/lon using value: ${value}.${NC}" >&2 - exit 1 + return 1 fi ;; latlon) @@ -37,13 +37,13 @@ lookup_station() { IFS=',' read -r lat lon <<< "$value" if [[ -z $lat || -z $lon ]]; then echo -e "${RED}Invalid lat,lon format: $value. Expected 'lat,lon'.${NC}" >&2 - exit 1 + return 1 fi jq -n --arg lat "$lat" --arg lon "$lon" '{lat: ($lat|tonumber), lon: ($lon|tonumber)}' > "$json_file" ;; *) echo -e "${RED}Invalid station type: $type${NC}" >&2 - exit 1 + return 1 ;; esac