mirror of
https://github.com/thetechprepper/emcomm-tools-os-community.git
synced 2026-09-17 15:41:44 -04:00
Merge pull request #46 from thetechprepper/feature/voacap-cli
Merge initial voacap cli to R5 for build 5
This commit is contained in:
@@ -0,0 +1,315 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Author : Gaston Gonzalez
|
||||
# Date : 23 May 2023
|
||||
# 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() {
|
||||
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 -e "${RED}Location not found for callsign: ${value}.${NC}" >&2
|
||||
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
|
||||
return 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 -e "${RED}Invalid lat,lon format: $value. Expected 'lat,lon'.${NC}" >&2
|
||||
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
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Extract lat/lon regardless of type
|
||||
local lat lon
|
||||
lat=$(jq .lat "$json_file")
|
||||
lon=$(jq .lon "$json_file")
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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}"
|
||||
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|20|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"
|
||||
|
||||
# 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
|
||||
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
|
||||
#######################################################################
|
||||
|
||||
if ! result=$(lookup_station "$tx_type" "$tx_value" tx-station.json); then
|
||||
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"
|
||||
|
||||
validate_latitude "$TL" || exit 1
|
||||
validate_longitude "$TK" || exit 1
|
||||
|
||||
TL1=$( awk -v n1=$TL -v n2=90 -v n3=-90 'BEGIN {if (n1<n3 || n1>n2) printf ("%s", "a"); else printf ("%.2f", n1);}' )
|
||||
|
||||
# add North or South (N/S)
|
||||
TLA=$( awk -v n1=$TL1 -v n2=0 'BEGIN {if (n1<n2) { n1=substr(n1,2); printf ("%6sS", n1); } else printf ("%6sN", n1);}' )
|
||||
|
||||
TK1=$( awk -v n1=$TK -v n2=180 -v n3=-180 'BEGIN {if (n1<n3 || n1>n2) printf ("%s", "a"); else printf ("%.2f", n1);}' )
|
||||
|
||||
# add East or West (E/W)
|
||||
TLO=$( awk -v n1=$TK1 -v n2=0 'BEGIN {if (n1<n2) { n1=substr(n1,2); printf ("%7sW", n1); } else printf ("%7sE", n1);}' )
|
||||
|
||||
|
||||
#######################################################################
|
||||
# RX Antenna
|
||||
#######################################################################
|
||||
|
||||
if ! result=$(lookup_station "$rx_type" "$rx_value" rx-station.json); then
|
||||
if [[ "$rx_type" == "call" ]]; then
|
||||
echo "Try specifying a grid or lat,lon for the receiving station."
|
||||
echo " --rx-grid GRID Receiving station Maidenhead grid"
|
||||
echo " --rx-latlon LAT,LON Receiving station coordinates in decimal degrees"
|
||||
fi
|
||||
exit 1
|
||||
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 (n1<n3 || n1>n2) printf ("%s", "a"); else printf ("%.2f", n1);}' )
|
||||
|
||||
# add North or South
|
||||
RLA=$( awk -v n1=$RL1 -v n2=0 'BEGIN {if (n1<n2) { n1=substr(n1,2); printf ("%6sS", n1); } else printf ("%6sN", n1);}' )
|
||||
|
||||
RK1=$( awk -v n1=$RK -v n2=180 -v n3=-180 'BEGIN {if (n1<n3 || n1>n2) printf ("%s", "a"); else printf ("%.2f", n1);}' )
|
||||
|
||||
# add East or West
|
||||
RLO=$( awk -v n1=$RK1 -v n2=0 'BEGIN {if (n1<n2) { n1=substr(n1,2); printf ("%7sW", n1); } else printf ("%7sE", n1);}' )
|
||||
|
||||
# Power settings
|
||||
# Use 80% of user-defined power and express in killiwatts
|
||||
# TODO: Make calculation dynamic.
|
||||
PWR=$power
|
||||
PW="0.0800"
|
||||
echo "TX Power: ${PWR} watts"
|
||||
if [ "$PWR" = "5" ]; then
|
||||
PW="0.0040"
|
||||
elif [ "$PWR" = "20" ]; then
|
||||
PW="0.0160"
|
||||
elif [ "$PWR" = "100" ]; then
|
||||
PW="0.0800"
|
||||
elif [ "$PWR" = "500" ]; then
|
||||
PW="0.4000"
|
||||
elif [ "$PWR" = "1500" ]; then
|
||||
PW="1.2000"
|
||||
else
|
||||
echo "Unsupported power level."
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Format for 117
|
||||
# NOTE: The card requires <SSN>. (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}
|
||||
@@ -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
|
||||
+133
@@ -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 " - "; }
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 <url> <filename> [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
|
||||
}
|
||||
|
||||
Executable
+14
@@ -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}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
#!/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
|
||||
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 +26,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..."
|
||||
|
||||
Executable
+52
@@ -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
|
||||
+4
-1
@@ -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
|
||||
|
||||
@@ -1,7 +1,23 @@
|
||||
#!/bin/bash
|
||||
# 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 $?
|
||||
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
# Author : Gaston Gonzalez
|
||||
# Date : 16 August 2025
|
||||
# Purpose : Test VOACAP installation
|
||||
|
||||
OUT=$(which voacapl)
|
||||
exit $?
|
||||
Reference in New Issue
Block a user