ASL3 callsigns instead of node numbers

I would like to know if anyone has successfully been able to run write_node_callsigns.sh with ASL3. I am legally blind and have been using this feature on one of my HamVoip nodes. Many thanks, Steve WB4IZC

#!/bin/bash
# N5LSN
# Make app_rpt telemetry use ASL callsigns instead of node numbers
# Intended for use on ASL3
# Directories and files
SRCDIR="/var/log/asterisk"
DESTDIR="/usr/share/asterisk/sounds/en/rpt/nodenames"
RPTSOUNDS="/usr/share/asterisk/sounds/en/rpt"
LETTERS="/usr/share/asterisk/sounds/en/letters"
NUMBERS="/usr/share/asterisk/sounds/en/digits"
PREV_DB="/tmp/previous_astdb.txt"
# Start time tracking
start_time=$(date +%s%3N)
# Usage instructions
usage() {
    cat << EOF
Usage: write_node_callsigns.sh options
OPTIONS:
   -h        Show this message
   -a        Process all nodes
   -i        Include node number with call
   -n node   Process a single node
   -d path   Specify destination directory (default: /usr/share/asterisk/sounds/en/rpt/nodenames)
   -v        Verbose output
   -f        Force run without user confirmation
Examples:
    ./write_node_callsigns.sh -a               # Process all nodes
    ./write_node_callsigns.sh -n 40000         # Process single node 40000
    ./write_node_callsigns.sh -f               # Force execution without confirmation
EOF
}
STRING=""
VERBOSE=""
INCNODE=""
FORCE_RUN=0
MAX_PREVIEW=10
# Create directories if missing
ensure_directory_exists() {
    if [ ! -d "$1" ]; then
        echo "Creating directory: $1"
        mkdir -p "$1" || { echo "Failed to create directory $1"; exit 1; }
    fi
}
# Find the correct audio file (.gsm or .ulaw)
find_audio_file() {
    basepath=$1
    if [ -f "${basepath}.gsm" ]; then
        echo "${basepath}.gsm"
    elif [ -f "${basepath}.ulaw" ]; then
        echo "${basepath}.ulaw"
    else
        echo ""
    fi
}
# Process each character in the callsign and form the audio filenames
make_call() {
    local foo=${1,,}
    STRING=""
    for (( i=0; i<${#foo}; i++ )); do
        local char=${foo:$i:1}
        case $char in
            [0-9]) FILENAME=$(find_audio_file "$NUMBERS/$char") ;;
            "/")   FILENAME=$(find_audio_file "$LETTERS/slash") ;;
            "-")   FILENAME=$(find_audio_file "$LETTERS/dash") ;;
            [a-z]) FILENAME=$(find_audio_file "$LETTERS/$char") ;;
        esac
        if [ -n "$FILENAME" ]; then
            STRING="$STRING $FILENAME"
        else
            echo "Error: Audio file for '$char' not found."
        fi
    done
}
# Handle .ulaw files with sox
process_file() {
    local file=$1
    if [[ "$file" == *.ulaw ]]; then
        echo "-t raw -e u-law -r 8000 -c 1 $file"
    else
        echo "$file"
    fi
}
# Concatenate the audio files into the final output and measure time
write_call() {
    local output_file="$DESTDIR/$f1.gsm"
    local start=$(date +%s%3N)  # Get start time in milliseconds
    # Generate processed file paths for sox
    local processed_files=""
    for file in $STRING; do
        processed_files="$processed_files $(process_file $file)"
    done
    # Execute sox to concatenate files (always overwrite)
    sox $processed_files $output_file
    local end=$(date +%s%3N)  # Get end time in milliseconds
    local duration=$((end - start))  # Calculate processing time
    # Output in the required format
    echo "[$(printf "%04d" $duration)ms] - $f1 - $f2"
}
# Load previous database into associative array for fast lookup
declare -A previous_callsigns
load_previous_database() {
    if [ -f "$PREV_DB" ]; then
        while IFS='|' read -r node callsign _; do
            # Ensure the node ID is valid (non-empty)
            if [ -n "$node" ]; then
                previous_callsigns["$node"]="$callsign"
            fi
        done < "$PREV_DB"
    fi
}
# Compare current astdb.txt with previous_db.txt to find new or modified nodes
compare_databases() {
    echo "Comparing databases..."
    new_nodes=()  # Array to store nodes that are new or changed
    changes=()    # Array to store changes for preview
    declare -A latest_node_callsigns  # To handle duplicate node numbers
    while IFS='|' read -r f1 f2 _; do
        # Skip lines starting with a semicolon or empty lines
        [[ "$f1" =~ ^\; ]] || [ -z "$f1" ] && continue
        # Handle duplicates by keeping the last occurrence of each node
        latest_node_callsigns["$f1"]="$f2"
    done < "$SRCDIR/astdb.txt"
    # Now compare the latest callsigns with the previous database
    for node in "${!latest_node_callsigns[@]}"; do
        current_callsign="${latest_node_callsigns[$node]}"
        old_callsign="${previous_callsigns[$node]}"
        if [ -z "$old_callsign" ]; then
            # New node
            new_nodes+=("$node|$current_callsign")
            changes+=("$node: NEW -> $current_callsign")
        elif [ "$old_callsign" != "$current_callsign" ]; then
            # Callsign has changed
            new_nodes+=("$node|$current_callsign")
            changes+=("$node: $old_callsign -> $current_callsign")
        fi
    done
}
# Prompt the user to confirm before proceeding, show the first 10 nodes with changes
confirm_processing() {
    local node_count=$1
    if [ $FORCE_RUN -eq 1 ]; then
        return  # Skip confirmation if forced to run
    fi
    echo "$node_count nodes need to be processed."
    echo "Preview of changes:"
    local i=0
    for change in "${changes[@]}"; do
        echo "  $change"
        ((i++))
        if [ $i -ge $MAX_PREVIEW ]; then
            echo "  ...and more."
            break
        fi
    done
    read -p "Continue? [y/n]: " response
    case "$response" in
        [yY][eE][sS]|[yY])
            echo "Starting processing..."
            ;;
        *)
            echo "Aborting."
            exit 0
            ;;
    esac
}
# Update the previous_db.txt file with the current astdb.txt data
update_previous_db() {
    cp "$SRCDIR/astdb.txt" "$PREV_DB"
}
# Format the total execution time into the appropriate unit (seconds, minutes, hours)
format_total_time() {
    local total_time_ms=$1
    if (( total_time_ms < 1000 )); then
        # Less than 1 second: display in milliseconds
        echo "${total_time_ms}ms"
    elif (( total_time_ms < 60000 )); then
        # Less than 1 minute: display in seconds
        local total_time_sec=$(echo "scale=1; $total_time_ms / 1000" | bc)
        echo "${total_time_sec}s"
    elif (( total_time_ms < 3600000 )); then
        # Less than 1 hour: display in minutes
        local total_time_min=$(echo "scale=1; $total_time_ms / 60000" | bc)
        echo "${total_time_min}min"
    else
        # More than 1 hour: display in hours
        local total_time_hr=$(echo "scale=1; $total_time_ms / 3600000" | bc)
        echo "${total_time_hr}h"
    fi
}
# Main processing logic for new or modified nodes
process_nodes() {
    # Load the previous database into memory for fast lookups
    load_previous_database
    # Compare current and previous databases
    compare_databases
    local node_count=${#new_nodes[@]}
    if [ $node_count -eq 0 ]; then
        echo "No new or changed nodes to process."
        return
    fi
    # Confirm processing with user
    confirm_processing "$node_count"
    # Process new or modified nodes
    for node_data in "${new_nodes[@]}"; do
        IFS='|' read -r f1 f2 <<< "$node_data"
        make_call "$f2"
        if [ "$INCNODE" ]; then
            STRING="$STRING $(find_audio_file "$RPTSOUNDS/node")"
            make_call "$f1"
        fi
        write_call
    done
    # Update the previous database with the current data
    update_previous_db
}
# Parse command-line options
while getopts "hail:vn:d:fv" OPTION; do
    case $OPTION in
        h) usage; exit 0 ;;
        a) ;;
        i) INCNODE=1 ;;
        n) node=$OPTARG ;;
        d) DESTDIR=$OPTARG ;;
        f) FORCE_RUN=1 ;;  # Force execution without confirmation
        v) VERBOSE=1 ;;
        ?) usage; exit 1 ;;
    esac
done
# Ensure necessary directories exist
ensure_directory_exists "$DESTDIR"
# Verify that the source file exists
if [ ! -f "$SRCDIR/astdb.txt" ]; then
    echo "$SRCDIR/astdb.txt not found. Please verify the location."
    exit 1
fi
# Start processing nodes
process_nodes
# Calculate total script execution time
end_time=$(date +%s%3N)
total_duration=$((end_time - start_time))
formatted_duration=$(format_total_time $total_duration)
echo "Total script execution time: $formatted_duration"

Thank You Mason, I will give this a try. Many thanks and 73, Steve WB4IZC

I run it every midnight via cron. The first time you run it, it will take forever. After that, it only updates the callsigns for nodes with new/updated info. It uses astdb.txt, so you’ll need to read this: Other Software Products - AllStarLink Manual