#!/bin/bash
#
#	cwtimer v3.0 - an identification timer script for IRLP
#	Written by Robert Pectol - http://rob.pectol.com/irlp
#
#	* NOTE *
#	This script requires that your /etc/sudoers file contains the
#	following line if you opt not to use an AUX line for PTT:
#
#		repeater ALL= NOPASSWD: /sbin/iptables
#
#	This MUST be accomplished by editing /etc/sudoers with the,
#	'visudo' command as user, root.
#
#   v3.1  05/02/09 VK8SJ - added line to make log entry after ID transmitted
#   v3.2  14/05/11 VK8SJ - Fixup to disable id node disabled
#   v3.3  28/01/12 VK8SJ - Shifted kill flag to $LOCAL from ~/ and un-hide it
#
####################################################################


###########################    USER CONFIGURABLE SETTINGS    ###########################
#
id_delay="900"		# default 8 min (480 secs); system identification interval
courtesy_delay="600"	# default 2 min (120 secs); delay before forcing an ID during active PTT
beacon_mode="yes"	# causes the ID to be sent out at intervals, regardless of whether or not there has been recent activity
forced_id="no"		# force an ID at the expiry of courtesy_delay, even during active COS
id_disconnect="yes"	# force an ID upon disconnecting (technically not necessary since it will ID within the id_delay timeframe)
id_file=""		# sends optional wav id file instead of CW string for normal id
welcome_id=""		# optional welcome id wav file played at start of new activity (after sufficient inactivity)
volume=".90"		# volume of optional id_file and welcome_id wav files (.01 to .99 - only affects playback of these wav files)
aux_line_ptt="no"	# change to, "yes" for AUX line PTT if configured (v3 boards only)
aux_line="1"		# specify which AUX line if using AUX line PTT stated above
debugging="yes"		# whether or not to output debugging info to the terminal while running
#
###########################     END USER CONFIG SETTINGS     ###########################


# debugging message
if [ "$debugging" = "yes" ]; then
	cat <<-EOT


	   cwtimer is currently running in debug mode.  Debug mode can be useful for helping figure
	   out if/why cwtimer is not running as expected.  Although cwtimer will function normally
	   in this mode, it is not intended to be left in debug mode indefinitely.  Once all issues
	   are worked out, it is recommended that debugging be disabled in the USER CONFIGURABLE
	   SETTINGS near the top of the script.


	EOT
fi

# ensure script is run as repeater user
if [ `/usr/bin/whoami` != "repeater" ] ; then
  if [ "$debugging" = "yes" ]; then
          echo "cwtimer must be run as user, repeater!  Exiting..."
  fi
  exit 1
fi

# ensure the environment file has been sourced
if [ "$RUN_ENV" != "TRUE" ] ; then
	. /home/irlp/custom/environment
fi

# set some script vars
idstate="idle"
iddelay_cnt=$id_delay
idcourt_cnt=$courtesy_delay

# for AUX line PTT support, and/or incoming audio data detection
if [ "$aux_line_ptt" = "yes" ]; then
	tx_key=`echo "$BIN/aux$aux_line""on"`
	tx_unkey=`echo "$BIN/aux$aux_line""off"`
else
	# check for the sudo binary (required for incoming audio data detection if not using an AUX line for PTT)
	check_sudo=`which sudo`
	if [ "$check_sudo" = "" ]; then
		cat <<-EOT

		The incoming audio data stream detection code relies on sudo.
		Unfortunately, sudo could not be found on your system!  Either
		install it or enable AUX line PTT to remedy this problem.

		EOT
		exit 1
        fi
	# check for the iptables tool (required for incoming audio data detection if not using an AUX line for PTT)
	if [ ! -x /sbin/iptables ]; then
		cat <<-EOT

		The incoming audio data stream detection code relies on iptables.
		Unfortunately, iptables could not be found on your system!  Either
		install it or enable AUX line PTT to remedy this problem.

		EOT
		exit 1
	fi
	# zero the audio data flag
	audio_data_in=0
fi

# 'net activity check function (for nodes NOT utilizing an AUX line for PTT)
net_activity ()
{
	if [ "$debugging" = "yes" ]; then
		echo "detecting incoming audio data stream..."
	fi
	# establish packet marking for incoming audio data only
	if ! ( sudo /sbin/iptables -t mangle --list -nv | grep '0x2' > /dev/null 2>&1 ); then
           sudo /sbin/iptables -t mangle -A INPUT -p udp --dport 2074:2093 -m length --length 128:640 -j MARK --set-mark 2 > /dev/null 2>&1
	fi

	# reset the packet counters and inspect the number of marked packets between intervals and see if they are different
	sudo /sbin/iptables -t mangle --zero
	pass=0
	firstvalue=0
	secondvalue=0
	while [ "$pass" -lt "3" ] && [ "$secondvalue" = "$firstvalue" ]; do
		let firstvalue=`sudo /sbin/iptables -t mangle --list -nv | grep '0x2' | awk '{$1=$1;print}' | cut -d ' ' -f1`
		usleep 100000
		let secondvalue=`sudo /sbin/iptables -t mangle --list -nv | grep '0x2' | awk '{$1=$1;print}' | cut -d ' ' -f1`
		let pass+=1
          echo "Pass - $pass"
	done

	# set the activity flag based upon the inspected packet counts
	if [ "$secondvalue" -gt "$firstvalue" ]; then
		audio_data_in=1
		if [ "$debugging" = "yes" ]; then
			echo "incoming audio detected!"
		fi
	else
		audio_data_in=0
		if [ "$debugging" = "yes" ]; then
			echo "incoming audio NOT detected!"
		fi
	fi
}

# id function
cwid ()
{
	if [ "$debugging" = "yes" ]; then
		echo "cwid function"
	fi
	# remove the id_trigger file if it exists
	rm -f $LOCAL/id_trigger > /dev/null 2>&1
	# create id flag file
	touch $LOCAL/id
	# key up the transmitter
	if [ "$aux_line_ptt" = "yes" ]; then
		$tx_key
	else
		$BIN/forcekey
	fi
	usleep 600000
	# send the identification

# MODIFICATION 24/06/2009 VK8SJ -
# Instead of looking for id_file, look for flag file "id_voice" in $LOCAL
# This flag can be set by other scripts or console to send "voice.wav"
# at any time. Filename for voice.wav should be inside the flag file.
# ie 'cat id_voice' should return '/home/irlp/somefile.wav'
#     Make sure file is specified with complete path name!
#     The flag file is removed after playing.

        if [ -f $LOCAL/id_voice ]; then
           id_file=`cat $LOCAL/id_voice`
           echo "Voice ID specified - "$id_file
           rm -f $LOCAL/id_voice > /dev/null 2>&1
        fi


	if [ "$id_file" = "" ]; then
		# call the script that generates the CW identification
		$CUSTOM/callsign #>&/dev/null 2>&1
                #Update station activity log.
	        echo `date '+%b %d %Y %T'` "Station ID Transmitted." >> $LOGFILE
	else
		# play the optional identification wav file instead of CW
		play -v $volume $id_file # >&/dev/null 2>&1
                unset id_file      # make sure not played twice
                #Update station activity log.
                echo `date '+%b %d %Y %T'` "Voice ID File Transmitted."  >> $LOGFILE
	fi
	# create a timestamp for this id
	echo `date` > $LOCAL/last_id
## MOD - Add $LOCAL/cwid file for irlp_vmon to work!!
	echo `date` > $LOCAL/cwid
	# unkey the transmitter
	if [ "$aux_line_ptt" = "yes" ]; then
		$tx_unkey
	else
		# unkey the transmitter ONLY if there is no audio activity coming in from the 'Net
		net_activity
		if [ "$audio_data_in" = "0" ]; then
			$BIN/forceunkey
		else
			$BIN/forceunkey
			$BIN/key
		fi
	fi
	# remove id flag file
	rm -f $LOCAL/id > /dev/null 2>&1


	# remove .id_disconnect flag if it exists (no need to send ID twice in succession on a disconnect)
	if [ -f $LOCAL/.id_disconnect ]; then
		rm -f $LOCAL/.id_disconnect > /dev/null 2>&1
	fi
}

# voice id function
welcome_id ()
{
	if [ "$debugging" = "yes" ]; then
		echo "welcome_id function"
	fi
	# remove the id_trigger file if it exists
	rm -f $LOCAL/id_trigger > /dev/null 2>&1
	# create id flag file
	touch $LOCAL/id
	# key up the transmitter
	if [ "$aux_line_ptt" = "yes" ]; then
		$tx_key
	else
		$BIN/forcekey
	fi
	usleep 600000
	# play the welcome id message
	aplay $welcome_id > /dev/null 2>&1
	# create a timestamp for this id
	echo `date` > $LOCAL/last_id
	# unkey the transmitter
	if [ "$aux_line_ptt" = "yes" ]; then
		$tx_unkey
	else
		# unkey the transmitter ONLY if there is no audio activity coming in from the 'Net
		net_activity
		if [ "$audio_data_in" == "0" ]; then
			$BIN/forceunkey
		else
			$BIN/forceunkey
			$BIN/key
		fi
	fi
	# remove id flag file
	rm -f $LOCAL/id
}

# courtesy function
courtesy ()
{
	if [ "$debugging" = "yes" ]; then
		echo "courtesy function"
	fi
	# handles forced ID preference during active COS
	if [ "$forced_id" = "yes" ]; then
		# check courtesy timer status (return 0 if time HAS expired)
		if [ "$idcourt_cnt" -le "1" ]; then
			return 0
		fi
	else
		# check courtesy timer status (return 0 if time HAS expired, AND cos is NOT active)
		if [ "$idcourt_cnt" -le "1" ]; then
			if $BIN/cosstate; then
				return 0
			fi
		fi
	fi
	# check for activity on cos and ptt (return 0 if NEITHER are active)
	if $BIN/pttstate && $BIN/cosstate; then
		return 0
	fi
	return 1
}

# ptt trigger function
ptt_trigger ()
{
	if [ "$debugging" = "yes" ]; then
		echo "ptt_trigger function"
	fi
	if [ "$welcome_id" = "" ]; then
		do_cw="1"
	else
		do_cw="0"
	fi
	idstate="id"
	idcourt_cnt=$courtesy_delay
}

# kill_it function (kills off cwtimer if $LOCAL/cwtimer is found)
kill_it ()
{
	if [ -f $LOCAL/cwtimer ] ; then
		if [ "$debugging" = "yes" ]; then
			echo "cwtimer is disabled! Re-enable it by removing the $LOCAL/cwtimer file."
		fi
		exit 0
	fi
        if [ ! -f /home/irlp/local/enable ]; then
		if [ "$debugging" = "yes" ]; then
			echo "cwtimer is disabled 'coz the node is disabled."
		fi
		exit 0
        fi
}

# Main loop
while true; do
	# unkey the transmitter if no incoming audio is detected (only when NOT using AUX for PTT)
	if [ "$aux_line_ptt" != "yes" ] && [ "$audio_data_in" = "1" ]; then
		net_activity
		if [ "$audio_data_in" = "0" ]; then
			$BIN/forceunkey
		fi
		# compensate for net_activity function delay
		if [ "$iddelay_cnt" -gt "0" ]; then
			let iddelay_cnt-=1
		else
			iddelay_cnt=0
		fi
	fi
	# determine state
	case $idstate in
		idle)
			if [ "$debugging" = "yes" ]; then
				echo "idle case in main loop"
			fi
			# initial ptt triggers the id
			if ! $BIN/pttstate; then
				ptt_trigger
			fi
			# the presense of a flag (id_trigger) in the $LOCAL filesystem can also trigger the id
			if [ -f $LOCAL/id_trigger ]; then
				rm -f $LOCAL/id_trigger > /dev/null 2>&1
				ptt_trigger
			fi
			# the presense of a flag (simulate_activity) in the $LOCAL filesystem can also trigger the id
			if [ -f $LOCAL/simulate_activity ]; then
				rm -f $LOCAL/simulate_activity > /dev/null 2>&1
				ptt_trigger
			fi
			# if configured for beacon mode, exit idle mode immediately
			if [ "$beacon_mode" = "yes" ]; then
				if [ "$debugging" = "yes" ]; then
					echo "configured for beacon mode"
				fi
				idstate="id"
			fi ;;

		id)
			if [ "$debugging" = "yes" ]; then
				echo "id case in main loop"
			fi
			# Try to be courteous
			if courtesy; then
				if [ "$do_cw" = "0" ]; then
					welcome_id
					do_cw="1"
				else
					cwid
				fi
				idstate="delay"
				iddelay_cnt=$id_delay
			fi
			# only decrement if greater than 0
			if [ "$idcourt_cnt" -gt "0" ]; then
				idcourt_cnt=$((idcourt_cnt - 1))
			else
				idcourt_cnt=0
			fi
			;;

		delay)
			if [ "$debugging" = "yes" ]; then
				echo "delay case in main loop"
			fi
			# reset to the, 'idle' case if, 'initial key-up followed by NO activity', delay has expired
			if [ "$iddelay_cnt" -le "1" ]; then
				idstate="idle"
			fi
			# only decrement if greater than 0
			if [ "$iddelay_cnt" -gt "0" ]; then
				iddelay_cnt=$((iddelay_cnt - 1))
				if [ "$debugging" = "yes" ]; then
					echo "iddelay_cnt: $iddelay_cnt"
				fi
			else
				iddelay_cnt=0
			fi
			# trigger on active ptt during initial key-up delay time and send to the, 'delayid' function
			if ! $BIN/pttstate; then
				idstate="delayid"
			fi
			# the presense of a flag (id_trigger) in the $LOCAL filesystem can also trigger the id (forces immediate ID)
			if [ -f $LOCAL/id_trigger ]; then
				rm -f $LOCAL/id_trigger > /dev/null 2>&1
				ptt_trigger
			fi
			# the presense of a flag (simulate_activity) in the $LOCAL filesystem can also trigger the id
			if [ -f $LOCAL/simulate_activity ]; then
				rm -f $LOCAL/simulate_activity > /dev/null 2>&1
				idstate="delayid"
                        fi
			# send ID upon node disconnect (configurable at top of script)
			if [ "$id_disconnect" = "yes" ]; then
				if [ -f $LOCAL/active ]; then
					touch $LOCAL/.id_disconnect
				fi
				if [ -f $LOCAL/.id_disconnect ]; then
					if [ ! -f $LOCAL/active ]; then
						if [ "$debugging" = "yes" ]; then
							echo "node disconnected!  sending ID..."
						fi
						rm -f $LOCAL/.id_disconnect > /dev/null 2>&1
						ptt_trigger
					fi
				fi
			fi ;;

		delayid)
			if [ "$debugging" = "yes" ]; then
				echo "delayid case in main loop"
			fi
			# the presense of a flag (id_trigger) in the $LOCAL filesystem can also trigger the id (forces immediate ID)
			if [ -f $LOCAL/id_trigger ]; then
				rm -f $LOCAL/id_trigger > /dev/null 2>&1
				ptt_trigger
			fi
			# clear simulate_activity flag in the $LOCAL filesystem if present
			if [ -f $LOCAL/simulate_activity ]; then
				rm -f $LOCAL/simulate_activity > /dev/null 2>&1
			fi
			# send ID upon node disconnect (configurable at top of script)
			if [ "$id_disconnect" = "yes" ]; then
				if [ -f $LOCAL/active ]; then
					touch $LOCAL/.id_disconnect
				fi
				if [ -f $LOCAL/.id_disconnect ]; then
					if [ ! -f $LOCAL/active ]; then
						if [ "$debugging" = "yes" ]; then
							echo "node disconnected!  sending ID..."
						fi
						rm -f $LOCAL/.id_disconnect > /dev/null 2>&1
						ptt_trigger
					fi
				fi
			fi

			# Wait for delay timeout before sending the id
			if [ "$iddelay_cnt" -le "1" ]; then
				idstate="id"
				idcourt_cnt=$courtesy_delay
			fi
			# only decrement if greater than 0
			if [ "$iddelay_cnt" -gt "0" ]; then
				iddelay_cnt=$((iddelay_cnt - 1))
				if [ "$debugging" = "yes" ]; then
					echo "iddelay_cnt: $iddelay_cnt"
				fi
			else
				iddelay_cnt=0
			fi
			;;
	esac
	# fine grained control over main timing loop to compensate for elapsed time during test loops, etc.
	usleep 999975
	kill_it
done
echo "cwtimer should NEVER get here!"
exit 1

