mirror of
https://github.com/thetechprepper/emcomm-tools-os-community.git
synced 2026-09-17 15:41:44 -04:00
Merge branch 'thetechprepper:release/r5' into release/r5
This commit is contained in:
+1
@@ -0,0 +1 @@
|
|||||||
|
../et-service-et-api.service
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../et-service-mbtileserver.service
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 7 June 2025
|
||||||
|
# Purpose : User systemd unit file for EmComm Tools dump1090 service
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# Check status : systemctl --user status et-service-dump1090
|
||||||
|
# Start service : systemctl --user start et-service-dump1090
|
||||||
|
# Stop service : systemctl --user stop et-service-dump1090
|
||||||
|
# Reload this config : systemctl --user daemon-reload
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=EmComm Tools dump1090 service
|
||||||
|
# Disable conditional check for SDR device. Allow et-dump1090 to enforce.
|
||||||
|
#ConditionPathExists=/dev/et-sdr
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/opt/emcomm-tools/bin/et-dump1090
|
||||||
|
Restart=no
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 13 August 2025
|
||||||
|
# Purpose : User systemd unit file for EmComm Tools API
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# Check status : systemctl --user status et-service-et-api
|
||||||
|
# Start service : systemctl --user start et-service-et-api
|
||||||
|
# Stop service : systemctl --user stop et-service-et-api
|
||||||
|
# Reload this config : systemctl --user daemon-reload
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=EmComm Tools API
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/opt/emcomm-tools-api/bin/et-api
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 5 July 2025
|
||||||
|
# Purpose : User systemd unit file for EmComm Tools raster tile service
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# Check status : systemctl --user status et-service-mbtileservice
|
||||||
|
# Start service : systemctl --user start et-service-mbtileservice
|
||||||
|
# Stop service : systemctl --user stop et-service-mbtileservice
|
||||||
|
# Reload this config : systemctl --user daemon-reload
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=EmComm Tools raster tile service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/opt/emcomm-tools/bin/et-mbtileserver
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Copy your `.mbtiles` file here.
|
||||||
@@ -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
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 3 April 2025
|
||||||
|
# Updated : 24 April 2024
|
||||||
|
# Purpose : Creates a Jekyll doc site
|
||||||
|
set -e
|
||||||
|
source /opt/emcomm-tools/bin/et-common
|
||||||
|
|
||||||
|
function usage() {
|
||||||
|
echo "$(basename $0) <site-name>"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ $# -ne 1 ]]; then
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
SITE="$1"
|
||||||
|
|
||||||
|
rbenv local 3.3.8
|
||||||
|
gem install jekyll bundler && jekyll new ${SITE}
|
||||||
|
|
||||||
|
echo -e "${YELLOW}1. Move into the site directory: ${WHITE}cd ${SITE}${NC}"
|
||||||
|
echo -e "${YELLOW}2. Build and start site: ${WHITE}bundle exec jekyll serve${NC}"
|
||||||
|
echo -e "${YELLOW}3. Access site in web browser: ${WHITE}min http://localhost:4000${NC}"
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 2 April 2025
|
||||||
|
# Updated : 24 April 2025
|
||||||
|
# Purpose : Installs documentation tooling
|
||||||
|
set -e
|
||||||
|
|
||||||
|
RUBY_VERSION="3.3.8"
|
||||||
|
|
||||||
|
if [[ "${EUID}" -eq 0 ]]; then
|
||||||
|
echo "This script be run as your normal user, not as root."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -e ~/.rbenv ]]; then
|
||||||
|
echo "Install 'rbenv' into your home directory..."
|
||||||
|
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
|
||||||
|
|
||||||
|
echo "Adding 'rbenv' into your path..."
|
||||||
|
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
|
||||||
|
echo 'eval "$(rbenv init - bash)"' >> ~/.bash_profile
|
||||||
|
|
||||||
|
echo "Adding 'ruby-build' plugin..."
|
||||||
|
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
|
||||||
|
fi
|
||||||
|
|
||||||
|
source ~/.bash_profile
|
||||||
|
|
||||||
|
|
||||||
|
echo "Installing Ruby ${RUBY_VERSION}..."
|
||||||
|
rbenv install ${RUBY_VERSION}
|
||||||
|
rbenv global ${RUBY_VERSION}
|
||||||
|
|
||||||
|
echo "Using ruby version: $(ruby -v)"
|
||||||
|
echo "Using gem version: $(gem -v)"
|
||||||
|
|
||||||
|
ruby -v | grep "${RUBY_VERSION}"
|
||||||
|
if [[ $? -ne 0 ]]; then
|
||||||
|
"The documentation tool requires Ruby ${RUBY_VERSION}."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 21 May 2025
|
||||||
|
# Updated : 16 May 2025
|
||||||
|
# Purpose : udev rules for the Yaesu FTX-1 via USB-C
|
||||||
|
#
|
||||||
|
# Preconditions:
|
||||||
|
# 1. Yaesu FTX-1 is selected via et-radio
|
||||||
|
# 2. Yaesu FTX-1 is connected via USB-C cable
|
||||||
|
#
|
||||||
|
# Postconditions:
|
||||||
|
# 1. /dev/et-cat created
|
||||||
|
# 2. /dev/et-audio created
|
||||||
|
# 3. rigctld is started
|
||||||
|
#
|
||||||
|
# Note: As of 05/21/25 Yaesu has not published their CAT control command specification. This is a
|
||||||
|
# temporary integration that does not include CAT control support. It will only key the PTT
|
||||||
|
# and provide audio in/out. You MUST set the frequency and mode yourself.
|
||||||
|
|
||||||
|
# Special rule to ensure that the second serial interface (/dev/ttyUSB5) is tagged as ET_DEVICE=FTX-1 so
|
||||||
|
# that it can be used in the next CAT control rule.
|
||||||
|
KERNEL=="ttyUSB[0-9]*", SUBSYSTEM=="tty", PROGRAM="/opt/emcomm-tools/sbin/udev-tester.sh yaesu-ftx1", \
|
||||||
|
ATTRS{bInterfaceNumber}=="01", \
|
||||||
|
ENV{ET_DEVICE}="FTX-1"
|
||||||
|
|
||||||
|
# CAT control device will only match if PROGRAM above matches (exit status=0)
|
||||||
|
SUBSYSTEM=="tty", ENV{ET_DEVICE}=="FTX-1", \
|
||||||
|
ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea70",\
|
||||||
|
ACTION=="add", \
|
||||||
|
ENV{ET_SUBDEVICE}="CAT", GROUP="dialout", MODE="0660", SYMLINK+="et-cat", \
|
||||||
|
RUN+="/usr/bin/systemctl start rigctld"
|
||||||
|
|
||||||
|
# Audio device will only match if PROGRAM above matches (exit status=0)
|
||||||
|
SUBSYSTEM=="sound", ATTRS{idVendor}=="0d8c", ATTRS{idProduct}=="0016", \
|
||||||
|
PROGRAM="/opt/emcomm-tools/sbin/udev-tester.sh yaesu-ftx1", \
|
||||||
|
ACTION=="add", \
|
||||||
|
ENV{ET_DEVICE}="FTX-1", \
|
||||||
|
ATTR{id}="ET_AUDIO", \
|
||||||
|
ENV{ET_SUBDEVICE}="AUDIO", GROUP="audio", MODE="0660", SYMLINK+="et-audio"
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 31 May 2025
|
||||||
|
# Updated : 4 June 2025
|
||||||
|
# Purpose : Empty udev rule to prevent the BRLTTY subsystem from
|
||||||
|
# invoking /lib/udev/rules.d/85-brltty.rules. These rules
|
||||||
|
# hijack some USB devices used by ETC operators. The Bionics
|
||||||
|
# MicroFox-50 was one of these hijacked devices.
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 7 June 2025
|
||||||
|
# Purpose : udev rules for supported SDRs
|
||||||
|
#
|
||||||
|
# Supported Devices
|
||||||
|
# 1. RTL-SDR Blog v2
|
||||||
|
#
|
||||||
|
# Preconditions:
|
||||||
|
# 1. SDR connected via USB cable
|
||||||
|
#
|
||||||
|
# Postconditions:
|
||||||
|
# 1. /dev/et-sdr created
|
||||||
|
|
||||||
|
KERNEL=="swradio0", \
|
||||||
|
GROUP="dialout", MODE="0660", SYMLINK+="et-sdr"
|
||||||
Executable
+39
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 4 June 2025
|
||||||
|
# Update : 8 July 2025
|
||||||
|
# Purpose : Wrapper script around ADS-B integration
|
||||||
|
#
|
||||||
|
# Preconditions:
|
||||||
|
# 1. RTL-SDR v2 is connected and detected
|
||||||
|
#
|
||||||
|
# Postconditions:
|
||||||
|
# 1. dump1099 systemd service is launched
|
||||||
|
# 2. et-aircraft-app launched
|
||||||
|
|
||||||
|
source /opt/emcomm-tools/bin/et-common
|
||||||
|
|
||||||
|
CLEAN_UP=1
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
if [[ ${CLEAN_UP} -eq 1 ]]; then
|
||||||
|
et-log "Stopping dump1099 service"
|
||||||
|
systemctl --user stop et-service-dump1090
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Stop dump1099 on exit
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
if [[ ! -e ${ET_DEVICE_SDR} ]]; then
|
||||||
|
notify-user "No ${ET_DEVICE_SDR} detected. Reconnect your SDR."
|
||||||
|
CLEAN_UP=0
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
systemctl --user restart et-service-dump1090
|
||||||
|
if [[ $? -eq 0 ]]; then
|
||||||
|
start_and_wait_for_service et-service-dump1090
|
||||||
|
et-aircraft-app
|
||||||
|
fi
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# Author : Gaston Gonzalez
|
# Author : Gaston Gonzalez
|
||||||
# Date : 31 October 2024
|
# Date : 31 October 2024
|
||||||
# Updated : 11 November 2024
|
# Updated : 31 May 2024
|
||||||
# Purpose : Configures ET audio device with sane defaults
|
# Purpose : Configures ET audio device with sane defaults
|
||||||
#
|
#
|
||||||
# Preconditions
|
# Preconditions
|
||||||
@@ -85,6 +85,18 @@ update-config () {
|
|||||||
|
|
||||||
et-log "Applied amixer settings for ${ET_DEVICE_NAME}"
|
et-log "Applied amixer settings for ${ET_DEVICE_NAME}"
|
||||||
;;
|
;;
|
||||||
|
"FTX-1")
|
||||||
|
# Unmute Speaker a set volume. Adjust if remote station can't decode you. Your TX controls.
|
||||||
|
amixer -q -c ${AUDIO_CARD} sset Speaker Playback Switch 42% unmute
|
||||||
|
# Unmute Mic
|
||||||
|
amixer -q -c ${AUDIO_CARD} sset Mic Playback Switch 52% unmute
|
||||||
|
# Set "L/R Capture" to 19. Adjust if you can't decode received audio. Your RX controls.
|
||||||
|
amixer -q -c ${AUDIO_CARD} sset Mic Capture Switch 31% unmute
|
||||||
|
# Disable Auto Gain Control
|
||||||
|
amixer -q -c ${AUDIO_CARD} sset 'Auto Gain Control' mute
|
||||||
|
|
||||||
|
et-log "Applied amixer settings for ${ET_DEVICE_NAME}"
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
et-log "No ALSA configuration specified for this ET_DEVICE."
|
et-log "No ALSA configuration specified for this ET_DEVICE."
|
||||||
;;
|
;;
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Author : Gaston Gonzalez
|
# Author : Gaston Gonzalez
|
||||||
# Date : 1 February 2025
|
# Date : 1 February 2025
|
||||||
|
# Updated : 20 August 2025
|
||||||
# Purpose : Common environment variables and functions
|
# Purpose : Common environment variables and functions
|
||||||
|
|
||||||
|
# Standardized device paths for supported hardware
|
||||||
|
export ET_DEVICE_AUDIO="/dev/et-audio"
|
||||||
|
export ET_DEVICE_CAT="/dev/et-cat"
|
||||||
|
export ET_DEVICE_GPS="/dev/et-gps"
|
||||||
|
export ET_DEVICE_SDR="/dev/et-sdr"
|
||||||
|
|
||||||
export ESC="\x1B"
|
export ESC="\x1B"
|
||||||
export RED="${ESC}[1;31m"
|
export RED="${ESC}[1;31m"
|
||||||
export BLUE="${ESC}[1;34m"
|
export BLUE="${ESC}[1;34m"
|
||||||
@@ -11,7 +18,126 @@ export WHITE="${ESC}[97m"
|
|||||||
export YELLOW="${ESC}[1;33m"
|
export YELLOW="${ESC}[1;33m"
|
||||||
export NC="${ESC}[0m"
|
export NC="${ESC}[0m"
|
||||||
|
|
||||||
|
# Display an error message in the terminal
|
||||||
show-err-dialog() {
|
show-err-dialog() {
|
||||||
local message="$1"
|
local message="$1"
|
||||||
dialog --title "Notification" --msgbox "$message" 8 60
|
dialog --title "Notification" --msgbox "$message" 8 60
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Display a notification message as a UI popup in the desktop
|
||||||
|
notify-user() {
|
||||||
|
notify-send -t 5000 --app-name="EmComm Tools" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
exit_if_no_sdr() {
|
||||||
|
if [[ ! -e ${ET_DEVICE_SDR} ]]; then
|
||||||
|
et-log "No ${ET_DEVICE_SDR} detected."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
exit_on_service_failure() {
|
||||||
|
local systemd_unit_name="$1"
|
||||||
|
systemctl is-active --user --quiet "${systemd_unit_name}"
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
et-log "Can't start mode. ${systemd_unit_name} failed to start."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
start_and_wait_for_service() {
|
||||||
|
local timeout=15 # Total time to wait in seconds
|
||||||
|
local interval=2 # Interval between checks in seconds
|
||||||
|
local systemd_unit_name="$1"
|
||||||
|
local elapsed=0
|
||||||
|
|
||||||
|
et-log "Starting ${systemd_unit_name}..."
|
||||||
|
systemctl --user start ${systemd_unit_name}
|
||||||
|
|
||||||
|
et-log "Waiting for ${systemd_unit_name} to become active..."
|
||||||
|
|
||||||
|
while ! systemctl is-active --user --quiet "${systemd_unit_name}"; do
|
||||||
|
sleep "$interval"
|
||||||
|
elapsed=$((elapsed + interval))
|
||||||
|
if [ "$elapsed" -ge "$timeout" ]; then
|
||||||
|
et-log "Timeout reached. ${systemd_unit_name} failed to start."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
et-log "Waiting for ${systemd_unit_name} to become active..."
|
||||||
|
done
|
||||||
|
|
||||||
|
# Some applications even when started still need a bit of extra time to be available.
|
||||||
|
# Add a short think time of 2 seconds. The ARDOP modem requies extra time.
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# Do one more check to ensure that we are running, otherwise exit.
|
||||||
|
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
+55
@@ -0,0 +1,55 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 7 June 2025
|
||||||
|
# Purpose : Wrapper script around dump1090
|
||||||
|
#
|
||||||
|
# Preconditions:
|
||||||
|
# 1. RTL-SDR v2 is connected and detected
|
||||||
|
#
|
||||||
|
# Postconditions:
|
||||||
|
# 1. dump1099 map is updated with your position if available
|
||||||
|
# 2. dump1099 is launched with network and HTTP server support
|
||||||
|
|
||||||
|
source /opt/emcomm-tools/bin/et-common
|
||||||
|
|
||||||
|
DUMP1090_CONF_DIR="${HOME}/.local/share/emcomm-tools/dump1090"
|
||||||
|
DUMP1090_MAP_HTML="${DUMP1090_CONF_DIR}/gmap.html"
|
||||||
|
|
||||||
|
exit_if_no_sdr
|
||||||
|
|
||||||
|
if [[ ! -e ${DUMP1090_MAP_HTML} ]]; then
|
||||||
|
et-log "Map HTML not found: ${DUMP1090_MAP_HTML}"
|
||||||
|
notify-user "Map HTML not found: ${DUMP1090_MAP_HTML}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
CWD_DIR=$(pwd)
|
||||||
|
|
||||||
|
cd ${DUMP1090_CONF_DIR}
|
||||||
|
|
||||||
|
MY_LAT_LON=$(et-system-info et-gps)
|
||||||
|
if [[ $? -eq 0 ]]; then
|
||||||
|
if echo "${MY_LAT_LON}" | grep -Eq '^[-+]?[0-9]+(\.[0-9]+)?\s*,\s*[-+]?[0-9]+(\.[0-9]+)?$'; then
|
||||||
|
LAT=$(echo ${MY_LAT_LON} | cut -d "," -f1)
|
||||||
|
LON=$(echo ${MY_LAT_LON} | cut -d "," -f2)
|
||||||
|
ZOOM=10
|
||||||
|
|
||||||
|
sed -i "s/CenterLat=[^;]*;/CenterLat=${LAT};/g" ${DUMP1090_MAP_HTML}
|
||||||
|
sed -i "s/CenterLon=[^;]*;/CenterLon=${LON};/g" ${DUMP1090_MAP_HTML}
|
||||||
|
sed -i "s/setView(\[.*\], *[0-9]*);/setView([${LAT}, ${LON}], ${ZOOM});/" ${DUMP1090_MAP_HTML}
|
||||||
|
|
||||||
|
et-log "Updated map center with your current location."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
CMD="dump1090 --net --net-http-port 1090"
|
||||||
|
et-log "Starting dump1090 in ${DUMP1090_CONF_DIR} with: ${CMD}"
|
||||||
|
|
||||||
|
${CMD}
|
||||||
|
|
||||||
|
if [[ $? -ne 0 ]]; then
|
||||||
|
et-log "Error starting dump1090."
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd ${CWD_DIR}
|
||||||
Executable
+40
@@ -0,0 +1,40 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 5 July 2025
|
||||||
|
# Purpose : Wrapper script for starting the raster tile server
|
||||||
|
#
|
||||||
|
# Preconditions:
|
||||||
|
# 1. At least one .mbtiles files exist in the tileset directory
|
||||||
|
#
|
||||||
|
# Postconditions:
|
||||||
|
# 1. mbtileserver is started
|
||||||
|
# 2. Tile server available at http://localhost:8000/services
|
||||||
|
|
||||||
|
TILESERVER_HOME="${HOME}/.local/share/emcomm-tools/mbtileserver"
|
||||||
|
TILESERVER_TILE_DIR="${TILESERVER_HOME}/tilesets"
|
||||||
|
|
||||||
|
start() {
|
||||||
|
CWD_DIR=$(pwd)
|
||||||
|
|
||||||
|
cd ${TILESERVER_HOME}
|
||||||
|
CMD="mbtileserver"
|
||||||
|
et-log "Starting raster tile server in ${TILESERVER_HOME} with: ${CMD}"
|
||||||
|
|
||||||
|
${CMD}
|
||||||
|
|
||||||
|
cd ${CWD_DIR}
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ ! -e ${TILESERVER_TILE_DIR} ]]; then
|
||||||
|
et-log "Tile set directory does not exist: ${TILESERVER_TILE_DIR}."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ls ${TILESERVER_TILE_DIR}/*.mbtiles
|
||||||
|
if [[ $? -ne 0 ]]; then
|
||||||
|
et-log "Can't start tile server. No .mbtiles in ${TILESERVER_TILE_DIR}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
start
|
||||||
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}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"id": "yaesu-ftx1",
|
||||||
|
"vendor": "Yaesu",
|
||||||
|
"model": "FTX-1 (No CAT)",
|
||||||
|
"rigctrl": {
|
||||||
|
"id": "1",
|
||||||
|
"baud": "38400",
|
||||||
|
"ptt": "RTS",
|
||||||
|
"pttOnly": "true"
|
||||||
|
},
|
||||||
|
"notes": [
|
||||||
|
"Tune radio to desired frequency.",
|
||||||
|
"Long press [MODE], then set accordingly for HF or VHF/UHF.",
|
||||||
|
"For HF operation:",
|
||||||
|
"- First, select 'DATA-U'.",
|
||||||
|
"- Then, long press 'PRESET' and select 'FT8'.",
|
||||||
|
"- The 'PRESET' button should be highlighted in blue.",
|
||||||
|
"For VHF/UHF FM operation:",
|
||||||
|
"- First, select 'FM'.",
|
||||||
|
"- Then, long press 'PRESET' and select 'FT8'.",
|
||||||
|
"- The 'PRESET' button should be highlighted in blue."
|
||||||
|
],
|
||||||
|
"fieldNotes": [
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -64,11 +64,11 @@ PORT
|
|||||||
MINQUAL=201 ; Entries in the nodes table with qualities
|
MINQUAL=201 ; Entries in the nodes table with qualities
|
||||||
; greater or equal to MINQUAL will be sent on
|
; greater or equal to MINQUAL will be sent on
|
||||||
; this port. A value of 0 sends everything.
|
; this port. A value of 0 sends everything.
|
||||||
MAXFRAME=6 ; Max outstanding frames (1 thru 7)
|
MAXFRAME=2 ; Max outstanding frames (1 thru 7)
|
||||||
FRACK=5000 ; Level 2 timout in milliseconds
|
FRACK=5000 ; Level 2 timout in milliseconds
|
||||||
RESPTIME=1000 ; Level 2 delayed ack timer in milliseconds
|
RESPTIME=1000 ; Level 2 delayed ack timer in milliseconds
|
||||||
RETRIES=100 ; Level 2 maximum retry value
|
RETRIES=100 ; Level 2 maximum retry value
|
||||||
PACLEN=236 ; Default max packet length for this port
|
PACLEN=128 ; Default max packet length for this port
|
||||||
UNPROTO=FBB ; BTEXT broadcast addrs format:
|
UNPROTO=FBB ; BTEXT broadcast addrs format:
|
||||||
; DEST[,digi1[,digi2]]
|
; DEST[,digi1[,digi2]]
|
||||||
L3ONLY=0 ; 1=No user downlink connects on this port
|
L3ONLY=0 ; 1=No user downlink connects on this port
|
||||||
|
|||||||
@@ -73,8 +73,8 @@
|
|||||||
"ptt_ctrl": false
|
"ptt_ctrl": false
|
||||||
},
|
},
|
||||||
"gpsd": {
|
"gpsd": {
|
||||||
"enable_http": false,
|
"enable_http": true,
|
||||||
"allow_forms": false,
|
"allow_forms": true,
|
||||||
"use_server_time": false,
|
"use_server_time": false,
|
||||||
"addr": "localhost:2947"
|
"addr": "localhost:2947"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -73,8 +73,8 @@
|
|||||||
"ptt_ctrl": false
|
"ptt_ctrl": false
|
||||||
},
|
},
|
||||||
"gpsd": {
|
"gpsd": {
|
||||||
"enable_http": false,
|
"enable_http": true,
|
||||||
"allow_forms": false,
|
"allow_forms": true,
|
||||||
"use_server_time": false,
|
"use_server_time": false,
|
||||||
"addr": "localhost:2947"
|
"addr": "localhost:2947"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -68,8 +68,8 @@
|
|||||||
"ptt_ctrl": false
|
"ptt_ctrl": false
|
||||||
},
|
},
|
||||||
"gpsd": {
|
"gpsd": {
|
||||||
"enable_http": false,
|
"enable_http": true,
|
||||||
"allow_forms": false,
|
"allow_forms": true,
|
||||||
"use_server_time": false,
|
"use_server_time": false,
|
||||||
"addr": "localhost:2947"
|
"addr": "localhost:2947"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -73,8 +73,8 @@
|
|||||||
"ptt_ctrl": true
|
"ptt_ctrl": true
|
||||||
},
|
},
|
||||||
"gpsd": {
|
"gpsd": {
|
||||||
"enable_http": false,
|
"enable_http": true,
|
||||||
"allow_forms": false,
|
"allow_forms": true,
|
||||||
"use_server_time": false,
|
"use_server_time": false,
|
||||||
"addr": "localhost:2947"
|
"addr": "localhost:2947"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -73,8 +73,8 @@
|
|||||||
"ptt_ctrl": true
|
"ptt_ctrl": true
|
||||||
},
|
},
|
||||||
"gpsd": {
|
"gpsd": {
|
||||||
"enable_http": false,
|
"enable_http": true,
|
||||||
"allow_forms": false,
|
"allow_forms": true,
|
||||||
"use_server_time": false,
|
"use_server_time": false,
|
||||||
"addr": "localhost:2947"
|
"addr": "localhost:2947"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Author : Gaston Gonzalez
|
# Author : Gaston Gonzalez
|
||||||
# Date : 11 October 2024
|
# Date : 11 October 2024
|
||||||
# Updated : 8 December 2024
|
# Updated : 21 May 2025
|
||||||
# Purpose : Perform tests that can be used by the udev subsystem to aid
|
# Purpose : Perform tests that can be used by the udev subsystem to aid
|
||||||
# in rule development. This is intended to by used with the PROGRAM
|
# in rule development. This is intended to by used with the PROGRAM
|
||||||
# directive inside of a udev rule.
|
# directive inside of a udev rule.
|
||||||
@@ -13,6 +13,20 @@
|
|||||||
ET_HOME=/opt/emcomm-tools
|
ET_HOME=/opt/emcomm-tools
|
||||||
ACTIVE_RADIO="${ET_HOME}/conf/radios.d/active-radio.json"
|
ACTIVE_RADIO="${ET_HOME}/conf/radios.d/active-radio.json"
|
||||||
|
|
||||||
|
# Exit with a 0 exit status if, and only if, the currently selected radio is
|
||||||
|
# a Yaesu FTX-1.
|
||||||
|
test_yaesu_ftx1() {
|
||||||
|
if [ -L "${ET_HOME}/conf/radios.d/active-radio.json" ]; then
|
||||||
|
ID=$(cat "${ET_HOME}/conf/radios.d/active-radio.json" | jq -r .id)
|
||||||
|
|
||||||
|
if [[ "$ID" == "yaesu-ftx1" ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
# Exit with a 0 exit status if, and only if, the currently selected radio is
|
# Exit with a 0 exit status if, and only if, the currently selected radio is
|
||||||
# a Yaesu FT-891.
|
# a Yaesu FT-891.
|
||||||
test_yaesu_ft891() {
|
test_yaesu_ft891() {
|
||||||
@@ -82,6 +96,9 @@ if [ $# -ne 1 ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
case $1 in
|
case $1 in
|
||||||
|
yaesu-ftx1)
|
||||||
|
test_yaesu_ftx1
|
||||||
|
;;
|
||||||
yaesu-ft891)
|
yaesu-ft891)
|
||||||
test_yaesu_ft891
|
test_yaesu_ft891
|
||||||
;;
|
;;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# Author : Gaston Gonzalez
|
# Author : Gaston Gonzalez
|
||||||
# Date : 9 October 2024
|
# Date : 9 October 2024
|
||||||
# Updated : 28 October 2024
|
# Updated : 31 May 2025
|
||||||
# Purpose : Wrapper startup/shutdown script around systemd/rigctld
|
# Purpose : Wrapper startup/shutdown script around systemd/rigctld
|
||||||
|
|
||||||
ET_HOME=/opt/emcomm-tools
|
ET_HOME=/opt/emcomm-tools
|
||||||
@@ -41,8 +41,19 @@ start() {
|
|||||||
ID=$(cat ${ET_HOME}/conf/radios.d/active-radio.json | jq -r .rigctrl.id)
|
ID=$(cat ${ET_HOME}/conf/radios.d/active-radio.json | jq -r .rigctrl.id)
|
||||||
PTT=$(cat ${ET_HOME}/conf/radios.d/active-radio.json | jq -r .rigctrl.ptt)
|
PTT=$(cat ${ET_HOME}/conf/radios.d/active-radio.json | jq -r .rigctrl.ptt)
|
||||||
|
|
||||||
CMD="rigctld -m ${ID} -P ${PTT} "
|
# Special case for select radios that only need to key the PTT, but do
|
||||||
et-log "Starting rigctld in VOX mode with: ${CMD}"
|
# do not have CAT control support. This edge case was added for radios
|
||||||
|
# like the Yaesu FTX-1 Field before Yaesu published their CAT commands.
|
||||||
|
PTT_ONLY=$(cat ${ET_HOME}/conf/radios.d/active-radio.json | jq -r .rigctrl.pttOnly)
|
||||||
|
|
||||||
|
if [ "${PTT_ONLY}" = "true" ]; then
|
||||||
|
CMD="rigctld -m ${ID} -p ${CAT_DEVICE} -P ${PTT} "
|
||||||
|
et-log "Starting rigctld in PTT-only mode with: ${CMD}"
|
||||||
|
else
|
||||||
|
CMD="rigctld -m ${ID} -P ${PTT} "
|
||||||
|
et-log "Starting rigctld in VOX mode with: ${CMD}"
|
||||||
|
fi
|
||||||
|
|
||||||
$CMD
|
$CMD
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Version=1.0
|
||||||
|
Name=et-aircraft
|
||||||
|
Comment=dump1090 ADB-S tracker for EmComm Tools
|
||||||
|
Exec=/opt/emcomm-tools/bin/et-aircraft
|
||||||
|
Icon=et-aircraft-icon-black-192
|
||||||
|
Terminal=false
|
||||||
|
X-MultipleArgs=false
|
||||||
|
Type=Application
|
||||||
|
Categories=HamRadio;
|
||||||
|
StartupNotify=true
|
||||||
@@ -12,7 +12,7 @@ show-desktop-icons = false
|
|||||||
picture-uri = 'file:///usr/share/backgrounds/emcomm-tools-wallpaper.png'
|
picture-uri = 'file:///usr/share/backgrounds/emcomm-tools-wallpaper.png'
|
||||||
|
|
||||||
[org.gnome.shell]
|
[org.gnome.shell]
|
||||||
favorite-apps = ['emcomm-tools.desktop', 'js8call.desktop', 'org.gnome.Terminal.desktop', 'navit.desktop', 'min.desktop', 'org.gnome.Nautilus.desktop']
|
favorite-apps = ['emcomm-tools.desktop', 'js8call.desktop', 'et-aircraft.desktop', 'org.gnome.Terminal.desktop', 'navit.desktop', 'min.desktop', 'org.gnome.Nautilus.desktop']
|
||||||
last-selected-power-profile = 'performance'
|
last-selected-power-profile = 'performance'
|
||||||
|
|
||||||
[org.gnome.system.location]
|
[org.gnome.system.location]
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 1017 KiB |
@@ -2,17 +2,17 @@
|
|||||||
#
|
#
|
||||||
# Author : Gaston Gonzalez
|
# Author : Gaston Gonzalez
|
||||||
# Date : 16 March 2024
|
# Date : 16 March 2024
|
||||||
# Updated : 9 October 2024
|
# Updated : 8 June 2025
|
||||||
# Purpose : Configure GNOME
|
# Purpose : Configure GNOME
|
||||||
|
|
||||||
et-log "Configuring GNOME"
|
et-log "Configuring GNOME..."
|
||||||
|
|
||||||
cp ../overlay/usr/share/glib-2.0/schemas/90_ubuntu-settings.gschema.override \
|
cp ../overlay/usr/share/glib-2.0/schemas/90_ubuntu-settings.gschema.override \
|
||||||
/usr/share/glib-2.0/schemas/
|
/usr/share/glib-2.0/schemas/
|
||||||
|
|
||||||
cp ../overlay/usr/share/icons/emcomm-tools-icon-black-512.png \
|
cp ../overlay/usr/share/icons/*.png \
|
||||||
/usr/share/icons/
|
/usr/share/icons/
|
||||||
chmod 644 /usr/share/icons/emcomm-tools-*.png
|
chmod 644 /usr/share/icons/et-*.png
|
||||||
|
|
||||||
glib-compile-schemas /usr/share/glib-2.0/schemas/
|
glib-compile-schemas /usr/share/glib-2.0/schemas/
|
||||||
|
|
||||||
|
|||||||
Executable
+31
@@ -0,0 +1,31 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 6 July 2025
|
||||||
|
# Purpose : Downloads pre-rendered raster map tiles in mbtile format
|
||||||
|
|
||||||
|
BASE_URL="https://github.com/thetechprepper/emcomm-tools-os-community/releases/download"
|
||||||
|
RELEASE="emcomm-tools-os-community-20250401-r4-final-4.0.0"
|
||||||
|
TILESET_DIR="/etc/skel/.local/share/emcomm-tools/mbtileserver/tilesets"
|
||||||
|
|
||||||
|
OPTIONS=(
|
||||||
|
"us" "United States"
|
||||||
|
"ca" "Canada"
|
||||||
|
)
|
||||||
|
|
||||||
|
SELECTED_COUNTRY=$(dialog --clear --menu "Select a country" 15 40 5 "${OPTIONS[@]}" 2>&1 >/dev/tty)
|
||||||
|
EXIT_STATUS=$?
|
||||||
|
|
||||||
|
tput sgr 0 && clear
|
||||||
|
|
||||||
|
if [[ $EXIT_STATUS -eq 0 ]]; then
|
||||||
|
DOWNLOAD_FILE="osm-${SELECTED_COUNTRY}-zoom0to10-20250703.mbtiles"
|
||||||
|
DOWNLOAD_URL="${BASE_URL}/${RELEASE}/${DOWNLOAD_FILE}"
|
||||||
|
|
||||||
|
if [[ ! -e ${DOWNLOAD_FILE} ]]; then
|
||||||
|
et-log "Downloading ${DOWNLOAD_URL}"
|
||||||
|
curl -L -f -o ${DOWNLOAD_FILE} ${DOWNLOAD_URL}
|
||||||
|
fi
|
||||||
|
|
||||||
|
et-log "Moving ${DOWNLOAD_FILE} ${TILESET_DIR}"
|
||||||
|
mv -v ${DOWNLOAD_FILE} ${TILESET_DIR}
|
||||||
|
fi
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# Author : Gaston Gonzalez
|
# Author : Gaston Gonzalez
|
||||||
# Date : 29 December 2023
|
# Date : 29 December 2023
|
||||||
# Updated : 20 October 2024
|
# Updated : 24 April 2025
|
||||||
# Purpose : Install development tools
|
# Purpose : Install development tools
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
@@ -37,3 +37,10 @@ then
|
|||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install --no-install-recommends cubic -y
|
sudo apt install --no-install-recommends cubic -y
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# libyaml-dev - needed for Ruby via rbenv
|
||||||
|
et-log "Installing tools needed for ETC R5"
|
||||||
|
apt install \
|
||||||
|
libyaml-dev \
|
||||||
|
-y
|
||||||
|
|||||||
Executable
+12
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 22 July 2025
|
||||||
|
# Purpose : Install a small offline English dictionary
|
||||||
|
|
||||||
|
et-log "Installing offline dictionary..."
|
||||||
|
|
||||||
|
apt install \
|
||||||
|
dict \
|
||||||
|
dictd \
|
||||||
|
dict-gcide \
|
||||||
|
-y
|
||||||
Executable
+12
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 22 July 2025
|
||||||
|
# Purpose : Install documentation tools
|
||||||
|
|
||||||
|
apt install \
|
||||||
|
pandoc \
|
||||||
|
texlive \
|
||||||
|
texlive-latex-recommended \
|
||||||
|
texlive-fonts-recommended \
|
||||||
|
texlive-latex-extra \
|
||||||
|
-y
|
||||||
Executable
+60
@@ -0,0 +1,60 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 4 June 2025
|
||||||
|
# Updated : 6 July 2025
|
||||||
|
# Purpose : Install dump1090 (ADB-S aircraft tracking)
|
||||||
|
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=dump1090
|
||||||
|
VERSION=master
|
||||||
|
DOWNLOAD_FILE=dump1090-master.zip
|
||||||
|
BIN_FILE=dump1090
|
||||||
|
INSTALL_DIR="/opt/${APP}-${VERSION}"
|
||||||
|
INSTALL_BIN_DIR="${INSTALL_DIR}/bin"
|
||||||
|
LINK_PATH="/opt/${APP}"
|
||||||
|
DUMP1090_CONF_DIR="/etc/skel/.local/share/emcomm-tools/dump1090"
|
||||||
|
|
||||||
|
if [[ ! -e ${ET_DIST_DIR}/${DOWNLOAD_FILE} ]]; then
|
||||||
|
|
||||||
|
URL="https://github.com/thetechprepper/dump1090/archive/refs/heads/master.zip"
|
||||||
|
|
||||||
|
et-log "Downloading ${APP}: ${URL}"
|
||||||
|
curl -s -L -o ${DOWNLOAD_FILE} --fail ${URL}
|
||||||
|
|
||||||
|
mv -v ${DOWNLOAD_FILE} ${ET_DIST_DIR}
|
||||||
|
fi
|
||||||
|
|
||||||
|
CWD_DIR=`pwd`
|
||||||
|
|
||||||
|
# Only build once
|
||||||
|
if [[ ! -e "${ET_SRC_DIR}/${APP}/${BIN_FILE}" ]]; then
|
||||||
|
cd ${ET_SRC_DIR}
|
||||||
|
unzip -o ${ET_DIST_DIR}/${DOWNLOAD_FILE}
|
||||||
|
|
||||||
|
cd dump1090-master
|
||||||
|
|
||||||
|
et-log "Building ${APP}.."
|
||||||
|
make
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ ! -e ${INSTALL_BIN_DIR} ]] && mkdir -v -p ${INSTALL_BIN_DIR}
|
||||||
|
cp -v "${ET_SRC_DIR}/${APP}-master/${BIN_FILE}" ${INSTALL_BIN_DIR}
|
||||||
|
|
||||||
|
# Install HTML map
|
||||||
|
[[ ! -e ${DUMP1090_CONF_DIR} ]] && mkdir -v -p ${DUMP1090_CONF_DIR}
|
||||||
|
cp -v "${ET_SRC_DIR}/${APP}-master/gmap.html" ${DUMP1090_CONF_DIR}
|
||||||
|
|
||||||
|
[[ -e ${LINK_PATH} ]] && rm ${LINK_PATH}
|
||||||
|
ln -s ${INSTALL_DIR} ${LINK_PATH}
|
||||||
|
|
||||||
|
stow -v -d /opt ${APP} -t /usr/local
|
||||||
|
|
||||||
|
cd $CWD_DIR
|
||||||
|
|
||||||
|
et-log "Updating launcher icon to support PNP..."
|
||||||
|
cp -v ../overlay/usr/share/applications/et-aircraft.desktop /usr/share/applications/
|
||||||
|
|
||||||
Executable
+27
@@ -0,0 +1,27 @@
|
|||||||
|
#!/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
|
||||||
|
DOWNLOAD_FILE="${APP}_${VERSION}_amd64.deb"
|
||||||
|
|
||||||
|
et-log "Installing ${APP} ${VERSION}..."
|
||||||
|
|
||||||
|
if [[ ! -e ${ET_DIST_DIR}/${DOWNLOAD_FILE} ]]; then
|
||||||
|
|
||||||
|
URL="https://github.com/thetechprepper/et-aircraft-app/releases/download/${VERSION}/${DOWNLOAD_FILE}"
|
||||||
|
|
||||||
|
download_with_retries ${URL} ${DOWNLOAD_FILE}
|
||||||
|
mv ${DOWNLOAD_FILE} ${ET_DIST_DIR}
|
||||||
|
fi
|
||||||
|
|
||||||
|
DEBIAN_FRONTEND=noninteractive dpkg -i "${ET_DIST_DIR}/${DOWNLOAD_FILE}"
|
||||||
Executable
+51
@@ -0,0 +1,51 @@
|
|||||||
|
#!/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
|
||||||
|
ET_API_JAR="emcomm-tools-api-${VERSION}-SNAPSHOT.jar"
|
||||||
|
BASE_URL="https://github.com/thetechprepper/et-api-java/releases/download/${VERSION}"
|
||||||
|
|
||||||
|
INSTALL_DIR="/opt/emcomm-tools-api"
|
||||||
|
BIN_DIR="${INSTALL_DIR}/bin"
|
||||||
|
DATA_DIR="${INSTALL_DIR}/data"
|
||||||
|
INDEX_DIR="${INSTALL_DIR}/index"
|
||||||
|
|
||||||
|
et-log "Installing ${APP} ${VERSION}..."
|
||||||
|
|
||||||
|
mkdir -v -p "${BIN_DIR}" "${DATA_DIR}" "${INDEX_DIR}"
|
||||||
|
|
||||||
|
# Download and install et-api uber JAR
|
||||||
|
URL="${BASE_URL}/${ET_API_JAR}"
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
for file in "${FILES[@]}"; do
|
||||||
|
URL="${BASE_URL}/${file}"
|
||||||
|
|
||||||
|
et-log "Downloading data set: ${URL}"
|
||||||
|
download_with_retries ${URL} ${file}
|
||||||
|
mv ${file} ${DATA_DIR}
|
||||||
|
done
|
||||||
|
|
||||||
|
et-log "Applying permissions..."
|
||||||
|
chgrp -v -R ${ET_GROUP} ${DATA_DIR} ${INDEX_DIR}
|
||||||
|
chmod -v 775 ${DATA_DIR} ${INDEX_DIR}
|
||||||
|
chmod -v 664 ${DATA_DIR}/*.csv
|
||||||
Executable
+18
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 26 April 2025
|
||||||
|
# Updated : 30 April 2025
|
||||||
|
# Purpose : Installs misc GIS tools from apt
|
||||||
|
set -e
|
||||||
|
trap 'last_command=$current_command; current_command=$BASH_COMMAND' DEBUG
|
||||||
|
trap 'et-log "\"${last_command}\" command failed with exit code $?."' ERR
|
||||||
|
|
||||||
|
et-log "Installing GIS tools..."
|
||||||
|
|
||||||
|
apt install \
|
||||||
|
gpsbabel \
|
||||||
|
gpsbabel-gui \
|
||||||
|
sqlite3 \
|
||||||
|
qgis \
|
||||||
|
-y
|
||||||
Executable
+44
@@ -0,0 +1,44 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 24 April 2025
|
||||||
|
# Upated : 25 April 2025
|
||||||
|
# Purpose : Install mbtileserver
|
||||||
|
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=mbtileserver
|
||||||
|
VERSION=0.11.0
|
||||||
|
DOWNLOAD_FILE="${APP}_v${VERSION}_linux_amd64.zip"
|
||||||
|
INSTALL_DIR="/opt/${APP}-${VERSION}"
|
||||||
|
INSTALL_BIN_DIR="${INSTALL_DIR}/bin"
|
||||||
|
LINK_PATH="/opt/${APP}"
|
||||||
|
|
||||||
|
et-log "Installing ${APP} ${VERSION}..."
|
||||||
|
|
||||||
|
if [[ ! -e ${ET_DIST_DIR}/${DOWNLOAD_FILE} ]]; then
|
||||||
|
|
||||||
|
URL="https://github.com/consbio/mbtileserver/releases/download/v${VERSION}/${DOWNLOAD_FILE}"
|
||||||
|
|
||||||
|
et-log "Downloading ${APP}: ${URL}"
|
||||||
|
curl -s -L -o ${DOWNLOAD_FILE} --fail ${URL}
|
||||||
|
|
||||||
|
mv ${DOWNLOAD_FILE} ${ET_DIST_DIR}
|
||||||
|
fi
|
||||||
|
|
||||||
|
CWD_DIR=`pwd`
|
||||||
|
|
||||||
|
[[ ! -e ${INSTALL_BIN_DIR} ]] && mkdir -v -p ${INSTALL_BIN_DIR}
|
||||||
|
cd ${INSTALL_BIN_DIR}
|
||||||
|
unzip "${ET_DIST_DIR}/${DOWNLOAD_FILE}"
|
||||||
|
mv "mbtileserver_v${VERSION}_linux_amd64" ${APP}
|
||||||
|
chmod 755 ${APP}
|
||||||
|
|
||||||
|
[[ -e ${LINK_PATH} ]] && rm ${LINK_PATH}
|
||||||
|
ln -s ${INSTALL_DIR} ${LINK_PATH}
|
||||||
|
|
||||||
|
stow -v -d /opt ${APP} -t /usr/local
|
||||||
|
|
||||||
|
cd $CWD_DIR
|
||||||
Executable
+40
@@ -0,0 +1,40 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 25 April 2025
|
||||||
|
# Purpose : Install mbutil to allow for creation of offline tilesets
|
||||||
|
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=mbutil
|
||||||
|
VERSION=0.3.0
|
||||||
|
DOWNLOAD_FILE="${APP}-${VERSION}.tar.gz"
|
||||||
|
SRC_DIR="${ET_SRC_DIR}/${APP}-${VERSION}"
|
||||||
|
INSTALL_DIR="/opt/${APP}-${VERSION}"
|
||||||
|
INSTALL_BIN_DIR="${INSTALL_DIR}/bin"
|
||||||
|
LINK_PATH="/opt/${APP}"
|
||||||
|
|
||||||
|
et-log "Installing ${APP} ${VERSION}..."
|
||||||
|
|
||||||
|
if [[ ! -e "${ET_DIST_DIR}/${DOWNLOAD_FILE}" ]]; then
|
||||||
|
|
||||||
|
URL="https://github.com/mapbox/mbutil/archive/refs/tags/v${VERSION}.tar.gz"
|
||||||
|
|
||||||
|
et-log "Downloading ${APP}: ${URL}"
|
||||||
|
curl -s -L -o ${DOWNLOAD_FILE} --fail ${URL}
|
||||||
|
|
||||||
|
mv ${DOWNLOAD_FILE} ${ET_DIST_DIR}
|
||||||
|
fi
|
||||||
|
|
||||||
|
CWD_DIR=`pwd`
|
||||||
|
|
||||||
|
et-log "Unpacking ${APP} ${VERSION} source..."
|
||||||
|
tar -xzf "${ET_DIST_DIR}/${DOWNLOAD_FILE}"
|
||||||
|
mv "${APP}-${VERSION}" ${ET_SRC_DIR} && cd ${SRC_DIR}
|
||||||
|
|
||||||
|
et-log "Installing ${APP} ${VERSION} globally..."
|
||||||
|
python setup.py install
|
||||||
|
|
||||||
|
cd $CWD_DIR
|
||||||
Executable
+18
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 25 April 2025
|
||||||
|
# Purpose : Installs Python 2
|
||||||
|
set -e
|
||||||
|
trap 'last_command=$current_command; current_command=$BASH_COMMAND' DEBUG
|
||||||
|
trap 'et-log "\"${last_command}\" command failed with exit code $?."' ERR
|
||||||
|
|
||||||
|
et-log "Installing Python 2..."
|
||||||
|
|
||||||
|
apt install \
|
||||||
|
python2 \
|
||||||
|
python-setuptools \
|
||||||
|
-y
|
||||||
|
|
||||||
|
# Set Python 2 as the default
|
||||||
|
update-alternatives --install /usr/bin/python python /usr/bin/python2 1
|
||||||
Executable
+12
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 22 May 2024
|
||||||
|
# Updated : 4 June 2025
|
||||||
|
# Purpose : Install RF prediction and analysis tools
|
||||||
|
set -e
|
||||||
|
|
||||||
|
et-log "Installing RF prediction and analysis tools..."
|
||||||
|
apt install \
|
||||||
|
splat \
|
||||||
|
-y
|
||||||
Executable
+12
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 4 June 2025
|
||||||
|
# Purpose : Install SDR tools
|
||||||
|
set -e
|
||||||
|
|
||||||
|
et-log "Installing SDR tools..."
|
||||||
|
apt install \
|
||||||
|
rtl-sdr \
|
||||||
|
librtlsdr-dev \
|
||||||
|
-y
|
||||||
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
|
||||||
+29
-2
@@ -2,8 +2,8 @@
|
|||||||
#
|
#
|
||||||
# Author : Gaston Gonzalez
|
# Author : Gaston Gonzalez
|
||||||
# Date : 16 March 2024
|
# Date : 16 March 2024
|
||||||
# Updated : 18 March 2025
|
# Updated : 16 August 2025
|
||||||
# Purpose : Main installer for EmComm Tools OS Community
|
# Purpose : Main installer for EmComm Tools Community (ETC)
|
||||||
|
|
||||||
. ./env.sh
|
. ./env.sh
|
||||||
. ./functions.sh
|
. ./functions.sh
|
||||||
@@ -60,3 +60,30 @@ exitIfNotRoot
|
|||||||
[ ! -z "${ET_EXPERT}" ] && ./download-wikipedia.sh
|
[ ! -z "${ET_EXPERT}" ] && ./download-wikipedia.sh
|
||||||
|
|
||||||
./install-wine.sh
|
./install-wine.sh
|
||||||
|
|
||||||
|
# Install GIS applications and dependencies
|
||||||
|
./install-mbtileserver.sh
|
||||||
|
./install-python.sh
|
||||||
|
./install-mbutil.sh
|
||||||
|
./install-gis-tools.sh
|
||||||
|
|
||||||
|
# Install RF analysis and prediction tools
|
||||||
|
./install-rf-analysis-tools.sh
|
||||||
|
|
||||||
|
# Install SDR tools
|
||||||
|
./install-sdr-tools.sh
|
||||||
|
./install-dump1090.sh
|
||||||
|
|
||||||
|
# Install ETC custom maps
|
||||||
|
./download-et-maps.sh
|
||||||
|
|
||||||
|
# Install ETC applications
|
||||||
|
./install-et-api.sh
|
||||||
|
./install-et-aircraft.sh
|
||||||
|
|
||||||
|
# Install documentation tools
|
||||||
|
./install-dictionary.sh
|
||||||
|
#./install-doc-tools.sh
|
||||||
|
|
||||||
|
# Install prediction tools
|
||||||
|
./install-voacap.sh
|
||||||
|
|||||||
Executable
+7
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 5 May 2025
|
||||||
|
# Purpose : Test dump1090 installation
|
||||||
|
|
||||||
|
OUT=$(which dump1090)
|
||||||
|
exit $?
|
||||||
Executable
+8
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 9 July 2025
|
||||||
|
# Updated : 13 August 2025
|
||||||
|
# Purpose : Test et-aircraft installation
|
||||||
|
|
||||||
|
OUT=$(which et-aircraft-app)
|
||||||
|
exit $?
|
||||||
Executable
+23
@@ -0,0 +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 : 30 April 2025
|
||||||
|
# Purpose : Test GPSBabel installation
|
||||||
|
|
||||||
|
OUT=$(which gpsbabel && which gpsbabelfe)
|
||||||
|
exit $?
|
||||||
Executable
+7
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 24 April 2025
|
||||||
|
# Purpose : Test mbtileserver installation
|
||||||
|
|
||||||
|
OUT=$(which mbtileserver)
|
||||||
|
exit $?
|
||||||
Executable
+7
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 25 April 2025
|
||||||
|
# Purpose : Test mb-util installation
|
||||||
|
|
||||||
|
OUT=$(which mb-util)
|
||||||
|
exit $?
|
||||||
Executable
+7
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 25 November 2025
|
||||||
|
# Purpose : Test Python installation
|
||||||
|
|
||||||
|
OUT=$(python --version 2>&1 | grep "2.7")
|
||||||
|
exit $?
|
||||||
Executable
+7
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 25 April 2025
|
||||||
|
# Purpose : Test QGIS installation
|
||||||
|
|
||||||
|
OUT=$(which qgis)
|
||||||
|
exit $?
|
||||||
Executable
+7
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Author : Gaston Gonzalez
|
||||||
|
# Date : 25 April 2025
|
||||||
|
# Purpose : Test SQLite installation
|
||||||
|
|
||||||
|
OUT=$(sqlite3 -version | grep "^3[.]")
|
||||||
|
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