Table of Contents
MB7AZE is now QRT and the NoV has expired.
The following is for historical interest only….
MB7AZE SvxLink Config
Main config file
- svxlink.conf
############################################################################### # # # Configuration file for the SvxLink server # # # ############################################################################### [GLOBAL] #MODULE_PATH=/usr/lib/arm-linux-gnueabihf/svxlink LOGICS=SimplexLogic,ReflectorLogic CFG_DIR=svxlink.d TIMESTAMP_FORMAT="%c" CARD_SAMPLE_RATE=48000 #CARD_CHANNELS=1 LOCATION_INFO=LocationInfo LINKS=LinkToReflector [SimplexLogic] TYPE=Simplex RX=Rx1 TX=Tx1 DEFAULT_LANG=en_US MODULES=ModuleHelp,ModuleEchoLink,ModuleParrot CALLSIGN=MB7AZE SHORT_IDENT_INTERVAL=15 SHORT_CW_ID_ENABLE=1 SHORT_VOICE_ID_ENABLE=0 LONG_VOICE_ID_ENABLE=1 CW_AMP=-20 CW_PITCH=1000 CW_WPM=20 LONG_IDENT_INTERVAL=60 #IDENT_ONLY_AFTER_TX=4 #EXEC_CMD_ON_SQL_CLOSE=500 EVENT_HANDLER=/usr/local/share/svxlink/events.tcl RGR_SOUND_DELAY=0 #REPORT_CTCSS=77.0 TX_CTCSS=ALWAYS MACROS=Macros FX_GAIN_NORMAL=0 FX_GAIN_LOW=-12 #ACTIVATE_MODULE_ON_LONG_CMD=4:EchoLink #QSO_RECORDER=8:QsoRecorder ONLINE_CMD=998877 #MUTE_RX_ON_TX=1 #MUTE_TX_ON_RX=1 #STATE_PTY=/var/run/svxlink/state #DTMF_CTRL_PTY=/dev/shm/simplex_dtmf_ctrl TIME_FORMAT=24 RGR_SOUND_ALWAYS=1 [RepeaterLogic] TYPE=Repeater RX=Rx1 TX=Tx1 MODULES=ModuleHelp,ModuleParrot,ModuleEchoLink,ModuleTclVoiceMail CALLSIGN=MYCALL SHORT_IDENT_INTERVAL=10 LONG_IDENT_INTERVAL=60 #IDENT_ONLY_AFTER_TX=4 #EXEC_CMD_ON_SQL_CLOSE=500 EVENT_HANDLER=/usr/local/share/svxlink/events.tcl DEFAULT_LANG=en_US RGR_SOUND_DELAY=0 REPORT_CTCSS=136.5 #TX_CTCSS=SQL_OPEN MACROS=Macros #SEL5_MACRO_RANGE=03400,03499 FX_GAIN_NORMAL=0 FX_GAIN_LOW=-12 #QSO_RECORDER=8:QsoRecorder #NO_REPEAT=1 IDLE_TIMEOUT=30 OPEN_ON_1750=1000 #OPEN_ON_CTCSS=136:2000 #OPEN_ON_DTMF=* #OPEN_ON_SQL=5000 #OPEN_ON_SEL5=01234 #OPEN_SQL_FLANK=OPEN #OPEN_ON_SQL_AFTER_RPT_CLOSE=10 IDLE_SOUND_INTERVAL=3000 #SQL_FLAP_SUP_MIN_TIME=1000 #SQL_FLAP_SUP_MAX_COUNT=10 #ACTIVATE_MODULE_ON_LONG_CMD=4:EchoLink #IDENT_NAG_TIMEOUT=15 #IDENT_NAG_MIN_TIME=2000 #ONLINE_CMD=998877 #STATE_PTY=/var/run/svxlink/state #DTMF_CTRL_PTY=/dev/shm/repeater_dtmf_ctrl [ReflectorLogic] TYPE=Reflector DEFAULT_LANG=en_US HOSTS="put the right server address in here" HOST_PORT=5300 CALLSIGN=MB7AZE AUTH_KEY="put the right password in here" JITTER_BUFFER_DELAY=0 DEFAULT_TG=0 MONITOR_TGS=8,91,235,2350,2351,23520,23590,23561 TG_SELECT_TIMEOUT=60 ANNOUNCE_REMOTE_MIN_INTERVAL=300 MUTE_FIRST_TX_LOC=0 MUTE_FIRST_TX_REM=0 TMP_MONITOR_TIMEOUT=3600 EVENT_HANDLER=/usr/local/share/svxlink/events.tcl NODE_INFO_FILE=/usr/local/etc/svxlink/node_info.json [LinkToReflector] CONNECT_LOGICS=SimplexLogic:9:,ReflectorLogic DEFAULT_ACTIVE=1 #TIMEOUT=300 #AUTOACTIVATE_ON_SQL=RepeaterLogic OPTIONS=DEFAULT_CONNECT,NO_DISCONNECT [QsoRecorder] REC_DIR=/var/spool/svxlink/qso_recorder #MIN_TIME=1000 MAX_TIME=3600 SOFT_TIME=300 MAX_DIRSIZE=1024 #DEFAULT_ACTIVE=1 #TIMEOUT=300 #QSO_TIMEOUT=300 #ENCODER_CMD=/usr/bin/oggenc -Q \"%f\" && rm \"%f\" [Voter] TYPE=Voter RECEIVERS=Rx1,Rx2,Rx3 VOTING_DELAY=200 BUFFER_LENGTH=0 #REVOTE_INTERVAL=1000 #HYSTERESIS=50 #SQL_CLOSE_REVOTE_DELAY=500 #RX_SWITCH_DELAY=500 #COMMAND_PTY=/dev/shm/voter_ctrl [MultiTx] TYPE=Multi TRANSMITTERS=Tx1,Tx2,Tx3 [NetRx] TYPE=Net HOST=remote.rx.host TCP_PORT=5210 #LOG_DISCONNECTS_ONCE=0 AUTH_KEY="Change this key now!" CODEC=S16 #SPEEX_ENC_FRAMES_PER_PACKET=4 #SPEEX_ENC_QUALITY=4 #SPEEX_ENC_BITRATE=15000 #SPEEX_ENC_COMPLEXITY=2 #SPEEX_ENC_VBR=0 #SPEEX_ENC_VBR_QUALITY=4 #SPEEX_ENC_ABR=15000 #SPEEX_DEC_ENHANCER=1 #OPUS_ENC_FRAME_SIZE=20 #OPUS_ENC_COMPLEXITY=10 #OPUS_ENC_BITRATE=20000 #OPUS_ENC_VBR=1 [NetTx] TYPE=Net HOST=remote.tx.host TCP_PORT=5210 #LOG_DISCONNECTS_ONCE=0 AUTH_KEY="Change this key now!" CODEC=S16 #SPEEX_ENC_FRAMES_PER_PACKET=4 #SPEEX_ENC_QUALITY=4 #SPEEX_ENC_BITRATE=15000 #SPEEX_ENC_COMPLEXITY=2 #SPEEX_ENC_VBR=0 #SPEEX_ENC_VBR_QUALITY=4 #SPEEX_ENC_ABR=15000 #SPEEX_DEC_ENHANCER=1 #OPUS_ENC_FRAME_SIZE=20 #OPUS_ENC_COMPLEXITY=10 #OPUS_ENC_BITRATE=20000 #OPUS_ENC_VBR=1 [Rx1] TYPE=Local AUDIO_DEV=alsa:plughw:1,0 AUDIO_CHANNEL=0 SQL_DET=CTCSS SQL_START_DELAY=0 SQL_DELAY=0 SQL_HANGTIME=200 #SQL_EXTENDED_HANGTIME=1000 #SQL_EXTENDED_HANGTIME_THRESH=15 #SQL_TIMEOUT=600 VOX_FILTER_DEPTH=20 VOX_THRESH=1000 #CTCSS_MODE=2 CTCSS_FQ=77.0 #CTCSS_SNR_OFFSET=0 #CTCSS_OPEN_THRESH=15 #CTCSS_CLOSE_THRESH=9 #CTCSS_BPF_LOW=60 #CTCSS_BPF_HIGH=270 #SERIAL_PORT=/dev/ttyUSB0 #SERIAL_PIN=CTS #SERIAL_SET_PINS= #EVDEV_DEVNAME=/dev/input/by-id/usb-SYNIC_SYNIC_Wireless_Audio-event-if03 #EVDEV_OPEN=1,163,1 #EVDEV_CLOSE=1,163,0 #GPIO_PATH=/sys/class/gpio #GPIO_SQL_PIN=gpio25 #PTY_PATH=/tmp/rx1_sql HID_DEVICE=/dev/hidraw0 HID_SQL_PIN=!VOL_DN #SIGLEV_DET=NOISE SIGLEV_SLOPE=1 SIGLEV_OFFSET=0 #SIGLEV_BOGUS_THRESH=120 #TONE_SIGLEV_MAP=100,84,60,50,37,32,28,23,19,8 SIGLEV_OPEN_THRESH=30 SIGLEV_CLOSE_THRESH=10 DEEMPHASIS=1 #SQL_TAIL_ELIM=300 PREAMP=-0.5 #PEAK_METER=1 DTMF_DEC_TYPE=INTERNAL DTMF_MUTING=1 DTMF_HANGTIME=40 DTMF_SERIAL=/dev/ttyS0 #DTMF_PTY=/tmp/rx1_dtmf #DTMF_MAX_FWD_TWIST=8 #DTMF_MAX_REV_TWIST=4 #1750_MUTING=1 #SEL5_DEC_TYPE=INTERNAL #SEL5_TYPE=ZVEI1 #FQ=433475000 #MODULATION=FM #WBRX=WbRx1 [WbRx1] #TYPE=RtlUsb #DEV_MATCH=0 #HOST=localhost #PORT=1234 #CENTER_FQ=435075000 #FQ_CORR=0 #GAIN=0 #PEAK_METER=1 #SAMPLE_RATE=960000 [Tx1] TYPE=Local AUDIO_DEV=alsa:plughw:1,0 AUDIO_CHANNEL=0 PTT_TYPE=Hidraw #PTT_PORT=/dev/ttyS0 #PTT_PIN=HIDRAW HID_DEVICE=/dev/hidraw0 HID_PTT_PIN=GPIO3 #SERIAL_SET_PINS=DTR!RTS #GPIO_PATH=/sys/class/gpio #PTT_HANGTIME=1000 TIMEOUT=300 TX_DELAY=500 CTCSS_FQ=77.0 CTCSS_LEVEL=20 PREEMPHASIS=1 DTMF_TONE_LENGTH=100 DTMF_TONE_SPACING=50 DTMF_DIGIT_PWR=-15 MASTER_GAIN=-3.5 [LocationInfo] APRS_SERVER_LIST=euro.aprs2.net:14580 STATUS_SERVER_LIST=aprs.echolink.org:5199 LON_POSITION=1.25.30W LAT_POSITION=60.17.18N CALLSIGN=EL-MB7AZE FREQUENCY=430.050 TX_POWER=1 ANTENNA_GAIN=0 ANTENNA_HEIGHT=1m ANTENNA_DIR=-1 #PATH=WIDE1-1 BEACON_INTERVAL=60 TONE=77 COMMENT=Connected to UK Svx Reflector [Macros] 1=EchoLink:9999# 2=EchoLink:973498# 3=EchoLink:662666# 4=:91235# 5=:9123561# 9=:910#
Audio levels
Devcal
Use the devcal
program to set RX and TX audio levels.
This allows adjustment of alsamixer
settings and then for fine tuning adjustment of PRE-AMP
(for RX
) and MASTER GAIN
(for TX
) config options to set the correct audio level INPUT on RX
and deviation on TX
.
Once the correct PRE-AMP
or MASTER GAIN
values have been determined in DEVCAL
they are added to the relevant sections in svxlink.conf
To use DEVCAL
first sudo killall svxlink
For Receive audio input:
sudo devcal -r -m=3000 -d=3000 /usr/local/etc/svxlink/svxlink.conf Rx1
Inject a signal to the radio @ ±3kHz deviation Adjust audio input level via
- radio volume control
- alsamixer Mic Capture level
- devcal +/- to adjust PRE-AMP setting
until the displayed deviation agrees with the input signal's deviation.
Quit devcal
Transfer the displayed PRE-AMP
value to the conf file.
For Transmit audio output
Monitor TX deviation
sudo devcal -t -m=3000 -d=3000 /usr/local/etc/svxlink/svxlink.conf Tx1
Toggle TX with T
Adjust
- Alsamixer output
- devcal +/-
to achieve ±3kHz TX deviation.
Turn off TX with T
Quit devcal
Copy the displayed MASTER GAIN
value to the conf file.
sudo alsactl store
to save current mixer levels.
To prevent Echolink incoming connections while a Talkgroup is active
Uncomment :
proc tg_selected {new_tg old_tg} { puts "### tg_selected #$new_tg (old #$old_tg)" # Reject incoming Echolink connections while a talkgroup is active if {$new_tg != 0} { setConfigValue "ModuleEchoLink" "REJECT_INCOMING" "^.*$" } else { setConfigValue "ModuleEchoLink" "REJECT_INCOMING" "^$" } }
in /usr/local/share/svxlink/events.d/local/ReflectorLogic.tcl
<svxinstall>/events.d/local/
Here are the 3 files containing tweaks to the standard behaviour.
Editing the original files (in events.d/
) would work, but would get overwritten if the software is upgraded/re-installed. Putting local copies in events.d/local
means the changes survive upgrades etc.
The actual path is:
/usr/local/share/svxlink/events.d/local/
I'll try to document what is different once I refresh my memory.
############################################################################### # # ReflectorLogic event handlers # ############################################################################### # # This is the namespace in which all functions below will exist. The name # must match the corresponding section "[ReflectorLogic]" in the configuration # file. The name may be changed but it must be changed in both places. # namespace eval ReflectorLogic { # The currently selected TG. Variable set from application. variable selected_tg 0 # The previously selected TG. Variable set from application. variable previous_tg 0 # Timestamp for previous TG announcement variable prev_announce_time 0 # The previously announced TG variable prev_announce_tg 0 # The minimum time between announcements of the same TG. # Change through ANNOUNCE_REMOTE_MIN_INTERVAL config variable. variable announce_remote_min_interval 0 # This variable will be set to 1 if the QSY pending feature ("QSY on squelch # activity") is active. See configuration variable QSY_PENDING_TIMEOUT. variable qsy_pending_active 0 # # Checking to see if this is the correct logic core # if {$logic_name != [namespace tail [namespace current]]} { return; } # # Executed when an unknown command is received # cmd - The command string # proc unknown_command {cmd} { Logic::unknown_command $cmd; } # # Executed when a received command fails # proc command_failed {cmd} { Logic::command_failed $cmd; } # # Executed when manual TG announcement is triggered # proc report_tg_status {} { variable selected_tg variable previous_tg variable prev_announce_time variable prev_announce_tg playSilence 100 if {$selected_tg > 0} { set prev_announce_time [clock seconds] set prev_announce_tg $selected_tg playMsg "Core" "talk_group" spellNumber $selected_tg } else { playMsg "Core" "previous" playMsg "Core" "talk_group" spellNumber $previous_tg } } # # Executed when a TG has been selected # This function is called immediately when a change in talkgroup selection # occurs. In constrast, the other more specific talkgroup selection event # functions below is called with a delay in order to make announcement ordering # more logical. # # new_tg -- The talk group that has been activated # old_tg -- The talk group that was active # proc tg_selected {new_tg old_tg} { puts "### tg_selected #$new_tg (old #$old_tg)" # Reject incoming Echolink connections while a talkgroup is active if {$new_tg != 0} { setConfigValue "ModuleEchoLink" "REJECT_INCOMING" "^.*$" } else { setConfigValue "ModuleEchoLink" "REJECT_INCOMING" "^$" } } # # Executed when a TG has been selected due to local activity # # new_tg -- The talk group that has been activated # old_tg -- The talk group that was active # proc tg_local_activation {new_tg old_tg} { variable prev_announce_time variable prev_announce_tg variable selected_tg puts "### tg_local_activation" if {$new_tg != $old_tg} { set prev_announce_time [clock seconds] set prev_announce_tg $new_tg playSilence 100 playMsg "Core" "talk_group" spellNumber $new_tg } } # # Executed when a TG has been selected due to remote activity # # new_tg -- The talk group that has been activated # old_tg -- The talk group that was active # proc tg_remote_activation {new_tg old_tg} { variable prev_announce_time variable prev_announce_tg variable announce_remote_min_interval puts "### tg_remote_activation" set now [clock seconds]; if {($new_tg == $prev_announce_tg) && \ ($now - $prev_announce_time < $announce_remote_min_interval)} { return; } if {$new_tg != $old_tg} { set prev_announce_time $now set prev_announce_tg $new_tg playSilence 100 playMsg "Core" "talk_group" spellNumber $new_tg } } # # Executed when a TG has been selected due to remote activity on a prioritized # monitored talk group while a lower prio talk group is selected # # new_tg -- The talk group that has been activated # old_tg -- The talk group that was active # proc tg_remote_prio_activation {new_tg old_tg} { tg_remote_activation $new_tg $old_tg } # # Executed when a TG has been selected by DTMF command # # new_tg -- The talk group that has been activated # old_tg -- The talk group that was active # proc tg_command_activation {new_tg old_tg} { variable prev_announce_time variable prev_announce_tg puts "### tg_command_activation" set prev_announce_time [clock seconds] set prev_announce_tg $new_tg playSilence 100 playMsg "Core" "talk_group" spellNumber $new_tg } # # Executed when a TG has been selected due to DEFAULT_TG configuration # # new_tg -- The talk group that has been activated # old_tg -- The talk group that was active # proc tg_default_activation {new_tg old_tg} { #variable prev_announce_time #variable prev_announce_tg #variable selected_tg puts "### tg_default_activation" #if {$new_tg != $old_tg} { # set prev_announce_time [clock seconds] # set prev_announce_tg $new_tg # playSilence 100 # playMsg "Core" "talk_group" # spellNumber $new_tg #} } # # Executed when a TG QSY request have been acted upon # # new_tg -- The talk group that has been activated # old_tg -- The talk group that was active # proc tg_qsy {new_tg old_tg} { variable prev_announce_time variable prev_announce_tg puts "### tg_qsy" set prev_announce_time [clock seconds] set prev_announce_tg $new_tg playSilence 100 playMsg "Core" "qsy" #playMsg "Core" "talk_group" spellNumber $new_tg } # # Executed when a QSY is followed due to squelch open (see QSY_PENDING_TIMEOUT) # # tg -- The talk group that has been activated # proc tg_qsy_on_sql {tg} { playSilence 100 playMsg "Core" "qsy" } # # Executed when a TG QSY request fails # # A TG QSY may fail for primarily two reasons, either no talk group is # currently active or there is no connection to the reflector server. # proc tg_qsy_failed {} { puts "### tg_qsy_failed" playSilence 100 playMsg "Core" "qsy" playSilence 200 playMsg "Core" "operation_failed" } # # Executed when a TG QSY request is pending # # tg -- The talk group requested in the QSY # proc tg_qsy_pending {tg} { playSilence 100 playMsg "Core" "qsy" spellNumber $tg playMsg "Core" "pending" } # # Executed when a TG QSY request is ignored # # tg -- The talk group requested in the QSY # proc tg_qsy_ignored {tg} { variable qsy_pending_active playSilence 100 if {!$qsy_pending_active} { playMsg "Core" "qsy" spellNumber $tg } playMsg "Core" "ignored" playSilence 500 playTone 880 200 50 playTone 659 200 50 playTone 440 200 50 playSilence 100 } # # Executed when a TG selection has timed out # # new_tg -- Always 0 # old_tg -- The talk group that was active # proc tg_selection_timeout {new_tg old_tg} { puts "### tg_selection_timeout" if {$old_tg != 0} { playSilence 50 playTone 880 200 50 playTone 659 200 50 playTone 440 200 50 playSilence 50 # } else { # playSilence 50 # playTone 440 200 150 # playSilence 50 } } # # Executed on talker start # # tg -- The talk group # callsign -- The callsign of the talker node # proc talker_start {tg callsign} { #puts "### Talker DID start on TG #$tg: $callsign" } # # Executed on talker stop # # tg -- The talk group # callsign -- The callsign of the talker node # proc talker_stop {tg callsign} { variable selected_tg variable ::Logic::CFG_CALLSIGN #puts "### Talker DID stop on TG #$tg: $callsign" if {($tg == $selected_tg) && ($callsign != $::Logic::CFG_CALLSIGN)} { playSilence 100 playTone 440 200 50 playTone 659 200 50 playTone 880 200 50 playSilence 100 } } # # A talk group was added for temporary monitoring # # tg -- The added talk group # proc tmp_monitor_add {tg} { puts "### tmp_monitor_add: $tg" playSilence 50 playMsg "Default" "activating" playMsg "Core" "monitor" spellNumber $tg } # # A talk group was removed from temporary monitoring # # tg -- The removed talk group # proc tmp_monitor_remove {tg} { puts "### tmp_monitor_remove: $tg" playSilence 50 playMsg "Default" "deactivating" playMsg "Core" "monitor" spellNumber $tg } if [info exists ::Logic::CFG_ANNOUNCE_REMOTE_MIN_INTERVAL] { set announce_remote_min_interval $::Logic::CFG_ANNOUNCE_REMOTE_MIN_INTERVAL } if [info exists ::Logic::CFG_QSY_PENDING_TIMEOUT] { if {$::Logic::CFG_QSY_PENDING_TIMEOUT > 0} { set qsy_pending_active 1 } } # end of namespace } # # This file has not been truncated #
############################################################################### # # Generic Logic event handlers # ############################################################################### # # This is the namespace in which all functions and variables below will exist. # namespace eval Logic { # # A variable used to store a timestamp for the last identification. # variable prev_ident 0; # # A constant that indicates the minimum time in seconds to wait between two # identifications. Manual and long identifications is not affected. # variable min_time_between_ident 120; # # Short and long identification intervals. They are setup from config # variables below. # variable short_ident_interval 0; variable long_ident_interval 0; variable short_voice_id_enable 1 variable short_cw_id_enable 0 variable short_announce_enable 0 variable short_announce_file "" variable long_voice_id_enable 1 variable long_cw_id_enable 0 variable long_announce_enable 0 variable long_announce_file "" # # The ident_only_after_tx variable indicates if identification is only to # occur after the node has transmitted. The variable is setup below from the # configuration variable with the same name. # The need_ident variable indicates if identification is needed. # variable ident_only_after_tx 0; variable need_ident 0; # # List of functions that should be called periodically. Use the # addMinuteTickSubscriber and addSecondTickSubscriber functions to # add subscribers. # variable minute_tick_subscribers [list]; variable second_tick_subscribers [list]; # # Contains the ID of the last receiver that indicated squelch activity # variable sql_rx_id "?"; # # Executed when the SvxLink software is started # proc startup {} { #playMsg "Core" "online" #send_short_ident } # # Executed when a specified module could not be found # module_id - The numeric ID of the module # proc no_such_module {module_id} { playMsg "Core" "no_such_module"; playNumber $module_id; } # # Executed when a manual identification is initiated with the * DTMF code # proc manual_identification {} { global mycall; global report_ctcss; global active_module; global loaded_modules; variable CFG_TYPE; variable prev_ident; set epoch [clock seconds]; set hour [clock format $epoch -format "%k"]; regexp {([1-5]?\d)$} [clock format $epoch -format "%M"] -> minute; set prev_ident $epoch; playMsg "Core" "online"; spellWord $mycall; if {$CFG_TYPE == "Repeater"} { playMsg "Core" "repeater"; } playSilence 250; playMsg "Core" "the_time_is"; playTime $hour $minute; playSilence 250; if {$report_ctcss > 0} { playMsg "Core" "pl_is"; playFrequency $report_ctcss playSilence 300; } if {$active_module != ""} { playMsg "Core" "active_module"; playMsg $active_module "name"; playSilence 250; set func "::"; append func $active_module "::status_report"; if {"[info procs $func]" ne ""} { $func; } } else { foreach module [split $loaded_modules " "] { set func "::"; append func $module "::status_report"; if {"[info procs $func]" ne ""} { $func; } } } #playMsg "Default" "press_0_for_help" #playSilence 250; } # # Executed when a short identification should be sent # hour - The hour on which this identification occur # minute - The minute on which this identification occur # proc send_short_ident {{hour -1} {minute -1}} { global mycall; variable CFG_TYPE; variable short_announce_file variable short_announce_enable variable short_voice_id_enable variable short_cw_id_enable # Play voice id if enabled if {$short_voice_id_enable} { puts "Playing short voice ID" spellWord $mycall; if {$CFG_TYPE == "Repeater"} { playMsg "Core" "repeater"; } playSilence 500; } # Play announcement file if enabled if {$short_announce_enable} { puts "Playing short announce" if [file exist "$short_announce_file"] { playFile "$short_announce_file" playSilence 500 } } # Play CW id if enabled if {$short_cw_id_enable} { puts "Playing short CW ID" if {$CFG_TYPE == "Repeater"} { set call "$mycall/R" CW::play $call } else { CW::play $mycall } playSilence 500; } } # # Executed when a long identification (e.g. hourly) should be sent # hour - The hour on which this identification occur # minute - The minute on which this identification occur # proc send_long_ident {hour minute} { global mycall; global loaded_modules; global active_module; variable CFG_TYPE; variable long_announce_file variable long_announce_enable variable long_voice_id_enable variable long_cw_id_enable # Play the voice ID if enabled if {$long_voice_id_enable} { puts "Playing Long voice ID" spellWord $mycall; if {$CFG_TYPE == "Repeater"} { playMsg "Core" "repeater"; } playSilence 500; playMsg "Core" "the_time_is"; playSilence 100; playTime $hour $minute; playSilence 500; # Call the "status_report" function in all modules if no module is active if {$active_module == ""} { foreach module [split $loaded_modules " "] { set func "::"; append func $module "::status_report"; if {"[info procs $func]" ne ""} { $func; } } } playSilence 500; } # Play announcement file if enabled if {$long_announce_enable} { puts "Playing long announce" if [file exist "$long_announce_file"] { playFile "$long_announce_file" playSilence 500 } } # Play CW id if enabled if {$long_cw_id_enable} { puts "Playing long CW ID" if {$CFG_TYPE == "Repeater"} { set call "$mycall/R" CW::play $call } else { CW::play $mycall } playSilence 100 } } # # Executed when the squelch have just closed and the RGR_SOUND_DELAY timer has # expired. # proc send_rgr_sound {} { variable sql_rx_id if {$sql_rx_id != "?"} { # 150 CPM, 1000 Hz, -4 dBFS CW::play $sql_rx_id 150 1000 -4 set sql_rx_id "?" } else { playTone 440 500 100 } playSilence 100 } # # Executed when an empty macro command (i.e. D#) has been entered. # proc macro_empty {} { playMsg "Core" "operation_failed"; } # # Executed when an entered macro command could not be found # proc macro_not_found {} { playMsg "Core" "operation_failed"; } # # Executed when a macro syntax error occurs (configuration error). # proc macro_syntax_error {} { playMsg "Core" "operation_failed"; } # # Executed when the specified module in a macro command is not found # (configuration error). # proc macro_module_not_found {} { playMsg "Core" "operation_failed"; } # # Executed when the activation of the module specified in the macro command # failed. # proc macro_module_activation_failed {} { playMsg "Core" "operation_failed"; } # # Executed when a macro command is executed that requires a module to # be activated but another module is already active. # proc macro_another_active_module {} { global active_module; playMsg "Core" "operation_failed"; playMsg "Core" "active_module"; playMsg $active_module "name"; } # # Executed when an unknown DTMF command is entered # cmd - The command string # proc unknown_command {cmd} { spellWord $cmd; playMsg "Core" "unknown_command"; } # # Executed when an entered DTMF command failed # cmd - The command string # proc command_failed {cmd} { spellWord $cmd; playMsg "Core" "operation_failed"; } # # Executed when a link to another logic core is activated. # name - The name of the link # proc activating_link {name} { if {[string length $name] > 0} { playMsg "Core" "activating_link_to"; spellWord $name; } } # # Executed when a link to another logic core is deactivated. # name - The name of the link # proc deactivating_link {name} { if {[string length $name] > 0} { playMsg "Core" "deactivating_link_to"; spellWord $name; } } # # Executed when trying to deactivate a link to another logic core but the # link is not currently active. # name - The name of the link # proc link_not_active {name} { if {[string length $name] > 0} { playMsg "Core" "link_not_active_to"; spellWord $name; } } # # Executed when trying to activate a link to another logic core but the # link is already active. # name - The name of the link # proc link_already_active {name} { if {[string length $name] > 0} { playMsg "Core" "link_already_active_to"; spellWord $name; } } # # Executed each time the transmitter is turned on or off # is_on - Set to 1 if the transmitter is on or 0 if it's off # proc transmit {is_on} { #puts "Turning the transmitter $is_on"; variable prev_ident; variable need_ident; if {$is_on && ([clock seconds] - $prev_ident > 5)} { set need_ident 1; } } # # Executed each time the squelch is opened or closed # rx_id - The ID of the RX that the squelch opened/closed on # is_open - Set to 1 if the squelch is open or 0 if it's closed # proc squelch_open {rx_id is_open} { variable sql_rx_id; #puts "The squelch is $is_open on RX $rx_id"; set sql_rx_id $rx_id; } # # Executed when a DTMF digit has been received # digit - The detected DTMF digit # duration - The duration, in milliseconds, of the digit # # Return 1 to hide the digit from further processing in SvxLink or # return 0 to make SvxLink continue processing as normal. # proc dtmf_digit_received {digit duration} { #puts "DTMF digit \"$digit\" detected with duration $duration ms"; return 0; } # # Executed when a DTMF command has been received # cmd - The command # # Return 1 to hide the command from further processing is SvxLink or # return 0 to make SvxLink continue processing as normal. # # This function can be used to implement your own custom commands or to disable # DTMF commands that you do not want users to execute. proc dtmf_cmd_received {cmd} { #global active_module # Example: Ignore all commands starting with 3 in the EchoLink module. # Allow commands that have four or more digits. #if {$active_module == "EchoLink"} { # if {[string length $cmd] < 4 && [string index $cmd 0] == "3"} { # puts "Ignoring random connect command for module EchoLink: $cmd" # return 1 # } #} # Handle the "force core command" mode where a command is forced to be # executed by the core command processor instead of by an active module. # The "force core command" mode is entered by prefixing a command by a star. #if {$active_module != "" && [string index $cmd 0] != "*"} { # return 0 #} #if {[string index $cmd 0] == "*"} { # set cmd [string range $cmd 1 end] #} # Example: Custom command executed when DTMF 99 is received #if {$cmd == "99"} { # puts "Executing external command" # playMsg "Core" "online" # exec ls & # return 1 #} return 0 } # # Executed once every whole minute. Don't put any code here directly # Create a new function and add it to the timer tick subscriber list # by using the function addMinuteTickSubscriber. # proc every_minute {} { variable minute_tick_subscribers; #puts [clock format [clock seconds] -format "%Y-%m-%d %H:%M:%S"]; foreach subscriber $minute_tick_subscribers { $subscriber; } } # # Executed once every whole minute. Don't put any code here directly # Create a new function and add it to the timer tick subscriber list # by using the function addSecondTickSubscriber. # proc every_second {} { variable second_tick_subscribers; #puts [clock format [clock seconds] -format "%Y-%m-%d %H:%M:%S"]; foreach subscriber $second_tick_subscribers { $subscriber; } } # # Deprecated: Use the addMinuteTickSubscriber function instead # proc addTimerTickSubscriber {func} { puts "*** WARNING: Calling deprecated TCL event handler addTimerTickSubcriber." puts " Use addMinuteTickSubscriber instead" addMinuteTickSubscriber $func; } # # Use this function to add a function to the list of functions that # should be executed once every whole minute. This is not an event # function but rather a management function. # proc addMinuteTickSubscriber {func} { variable minute_tick_subscribers; lappend minute_tick_subscribers $func; } # # Use this function to add a function to the list of functions that # should be executed once every second. This is not an event # function but rather a management function. # proc addSecondTickSubscriber {func} { variable second_tick_subscribers; lappend second_tick_subscribers $func; } # # Should be executed once every whole minute to check if it is time to # identify. Not exactly an event function. This function handle the # identification logic and call the send_short_ident or send_long_ident # functions when it is time to identify. # proc checkPeriodicIdentify {} { variable prev_ident; variable short_ident_interval; variable long_ident_interval; variable min_time_between_ident; variable ident_only_after_tx; variable need_ident; global logic_name; if {$short_ident_interval == 0} { return; } set now [clock seconds]; set hour [clock format $now -format "%k"]; regexp {([1-5]?\d)$} [clock format $now -format "%M"] -> minute; set short_ident_now \ [expr {($hour * 60 + $minute) % $short_ident_interval == 0}]; set long_ident_now 0; if {$long_ident_interval != 0} { set long_ident_now \ [expr {($hour * 60 + $minute) % $long_ident_interval == 0}]; } if {$long_ident_now} { puts "$logic_name: Sending long identification..."; send_long_ident $hour $minute; set prev_ident $now; set need_ident 0; } else { if {$now - $prev_ident < $min_time_between_ident} { return; } if {$ident_only_after_tx && !$need_ident} { return; } if {$short_ident_now} { puts "$logic_name: Sending short identification..."; send_short_ident $hour $minute; set prev_ident $now; set need_ident 0; } } } # # Executed when the QSO recorder is being activated # proc activating_qso_recorder {} { playMsg "Core" "activating"; playMsg "Core" "qso_recorder"; } # # Executed when the QSO recorder is being deactivated # proc deactivating_qso_recorder {} { playMsg "Core" "deactivating"; playMsg "Core" "qso_recorder"; } # # Executed when trying to deactivate the QSO recorder even though it's # not active # proc qso_recorder_not_active {} { playMsg "Core" "qso_recorder"; playMsg "Core" "not_active"; } # # Executed when trying to activate the QSO recorder even though it's # already active # proc qso_recorder_already_active {} { playMsg "Core" "qso_recorder"; playMsg "Core" "already_active"; } # # Executed when the timeout kicks in to activate the QSO recorder # proc qso_recorder_timeout_activate {} { playMsg "Core" "timeout" playMsg "Core" "activating"; playMsg "Core" "qso_recorder"; } # # Executed when the timeout kicks in to deactivate the QSO recorder # proc qso_recorder_timeout_deactivate {} { playMsg "Core" "timeout" playMsg "Core" "deactivating"; playMsg "Core" "qso_recorder"; } # # Executed when the user is requesting a language change # proc set_language {lang_code} { global logic_name; puts "$logic_name: Setting language $lang_code (NOT IMPLEMENTED)"; } # # Executed when the user requests a list of available languages # proc list_languages {} { global logic_name; puts "$logic_name: Available languages: (NOT IMPLEMENTED)"; } # # Executed when the node is being brought online or offline # proc logic_online {online} { global mycall variable CFG_TYPE if {$online} { playMsg "Core" "online"; spellWord $mycall; if {$CFG_TYPE == "Repeater"} { playMsg "Core" "repeater"; } } } # # Executed when a configuration variable is updated at runtime in the logic # core # proc config_updated {tag value} { #puts "Configuration variable updated: $tag=$value" } # # Executed when a DTMF command is received from another linked logic core # # logic -- The name of the logic core # cmd -- The received command # proc remote_cmd_received {logic cmd} { #puts "Remote command received from logic $logic: $cmd" #playDtmf "$cmd" "500" "50" } # # Executed when a talkgroup is received from another linked logic core # # logic -- The name of the logic core # tg -- The received talkgroup # proc remote_received_tg_updated {logic tg} { #puts "Remote TG received from logic $logic: $tg" #if {$tg > 0} { # playDtmf "1$tg" "500" "50" #} } ############################################################################## # # Main program # ############################################################################## if [info exists CFG_SHORT_IDENT_INTERVAL] { if {$CFG_SHORT_IDENT_INTERVAL > 0} { set short_ident_interval $CFG_SHORT_IDENT_INTERVAL; } } if [info exists CFG_LONG_IDENT_INTERVAL] { if {$CFG_LONG_IDENT_INTERVAL > 0} { set long_ident_interval $CFG_LONG_IDENT_INTERVAL; if {$short_ident_interval == 0} { set short_ident_interval $long_ident_interval; } } } if [info exists CFG_IDENT_ONLY_AFTER_TX] { if {$CFG_IDENT_ONLY_AFTER_TX > 0} { set ident_only_after_tx $CFG_IDENT_ONLY_AFTER_TX; } } if [info exists CFG_SHORT_ANNOUNCE_ENABLE] { set short_announce_enable $CFG_SHORT_ANNOUNCE_ENABLE } if [info exists CFG_SHORT_ANNOUNCE_FILE] { set short_announce_file $CFG_SHORT_ANNOUNCE_FILE } if [info exists CFG_SHORT_VOICE_ID_ENABLE] { set short_voice_id_enable $CFG_SHORT_VOICE_ID_ENABLE } if [info exists CFG_SHORT_CW_ID_ENABLE] { set short_cw_id_enable $CFG_SHORT_CW_ID_ENABLE } if [info exists CFG_LONG_ANNOUNCE_ENABLE] { set long_announce_enable $CFG_LONG_ANNOUNCE_ENABLE } if [info exists CFG_LONG_ANNOUNCE_FILE] { set long_announce_file $CFG_LONG_ANNOUNCE_FILE } if [info exists CFG_LONG_VOICE_ID_ENABLE] { set long_voice_id_enable $CFG_LONG_VOICE_ID_ENABLE } if [info exists CFG_LONG_CW_ID_ENABLE] { set long_cw_id_enable $CFG_LONG_CW_ID_ENABLE } # end of namespace } # # This file has not been truncated #
############################################################################### # # EchoLink module event handlers # ############################################################################### # # This is the namespace in which all functions and variables below will exist. # The name must match the configuration variable "NAME" in the # [ModuleEchoLink] section in the configuration file. The name may be changed # but it must be changed in both places. # namespace eval EchoLink { # # Check if this module is loaded in the current logic core # if {![info exists CFG_ID]} { return; } # # Extract the module name from the current namespace # set module_name [namespace tail [namespace current]]; # # An "overloaded" playMsg that eliminates the need to write the module name # as the first argument. # proc playMsg {msg} { variable module_name; ::playMsg $module_name $msg; } # # A convenience function for printing out information prefixed by the # module name # proc printInfo {msg} { variable module_name; puts "$module_name: $msg"; } # # This variable is updated by the EchoLink module when a station connects or # disconnects. It contains the number of currently connected stations. # variable num_connected_stations 0; # # Executed when this module is being activated # proc activating_module {} { variable module_name; Module::activating_module $module_name; } # # Executed when this module is being deactivated. # proc deactivating_module {} { variable module_name; Module::deactivating_module $module_name; } # # Executed when the inactivity timeout for this module has expired. # proc timeout {} { variable module_name; Module::timeout $module_name; } # # Executed when playing of the help message for this module has been requested. # proc play_help {} { variable module_name; Module::play_help $module_name; } # # Spell an EchoLink callsign # proc spellEchoLinkCallsign {call} { global langdir if [regexp {^(\w+)-L$} $call ignored callsign] { spellWord $callsign playSilence 50 playMsg "link" } elseif [regexp {^(\w+)-R$} $call ignored callsign] { spellWord $callsign playSilence 50 playMsg "repeater" } elseif [regexp {^\*(.+)\*$} $call ignored name] { playMsg "conference" playSilence 50 set lc_name [string tolower $name] if [file exists "$langdir/EchoLink/conf-$lc_name.wav"] { playFile "$langdir/EchoLink/conf-$lc_name.wav" } else { spellEchoLinkCallsign $name } } else { spellWord $call } } # # Executed when a request to list all connected stations is received. # That is, someone press DTMF "1#" when the EchoLink module is active. # proc list_connected_stations {connected_stations} { playNumber [llength $connected_stations]; playSilence 50; playMsg "connected_stations"; foreach {call} "$connected_stations" { spellEchoLinkCallsign $call; playSilence 250; } } # # Executed when someone tries to setup an outgoing EchoLink connection but # the directory server is offline due to communications failure. # proc directory_server_offline {} { playMsg "directory_server_offline"; } # # Executed when the limit for maximum number of QSOs has been reached and # an outgoing connection request is received. # proc no_more_connections_allowed {} { # FIXME: Change the message to something that makes more sense... playMsg "link_busy"; } # # Executed when a status report is requested. This usually happens at # manual identification when the user press DTMF "*". # proc status_report {} { variable num_connected_stations; variable module_name; global active_module; if {$active_module == $module_name} { playNumber $num_connected_stations; playMsg "connected_stations"; } } # # Executed when an EchoLink id cannot be found in an outgoing connect request. # proc station_id_not_found {station_id} { playNumber $station_id; playMsg "not_found"; } # # Executed when the lookup of an EchoLink callsign fail in an outgoing connect # request. # proc lookup_failed {station_id} { playMsg "operation_failed"; } # # Executed when a local user tries to connect to the local node. # proc self_connect {} { playMsg "operation_failed"; } # # Executed when a local user tries to connect to a node that is already # connected. # proc already_connected_to {call} { playMsg "already_connected_to"; playSilence 50; spellEchoLinkCallsign $call; } # # Executed when an internal error occurs. # proc internal_error {} { playMsg "operation_failed"; } # # Executed when an outgoing connection has been requested. # proc connecting_to {call} { playMsg "connecting_to"; spellEchoLinkCallsign $call; playSilence 500; } # # Executed when an EchoLink connection has been terminated # proc disconnected {call} { spellEchoLinkCallsign $call; playMsg "disconnected"; playSilence 500; } # # Executed when an incoming EchoLink connection has been accepted. # proc remote_connected {call} { playMsg "connected"; spellEchoLinkCallsign $call; playSilence 500; } # # Executed when an outgoing connection has been established. # call - The callsign of the remote station # proc connected {call} { #puts "Outgoing Echolink connection to $call established" playMsg "connected"; playSilence 500; } # # Executed when the list of connected remote EchoLink clients changes # client_list - List of connected clients # proc client_list_changed {client_list} { #foreach {call} $client_list { # puts $call #} } # # Executed when the EchoLink connection has been idle for too long. The # connection will be terminated. # proc link_inactivity_timeout {} { playMsg "timeout"; } # # Executed when a too short connect by callsign command is received # proc cbc_too_short_cmd {cmd} { spellWord $cmd; playSilence 50; playMsg "operation_failed"; } # # Executed when the connect by callsign function cannot find a match # proc cbc_no_match {code} { playNumber $code; playSilence 50; playMsg "no_match"; } # # Executed when the connect by callsign list has been retrieved # proc cbc_list {call_list} { playMsg "choose_station"; set idx 0; foreach {call} $call_list { incr idx; playSilence 500; playNumber $idx; playSilence 200; spellEchoLinkCallsign $call; } } # # Executed when the connect by callsign function is manually aborted # proc cbc_aborted {} { playMsg "aborted"; } # # Executed when an out of range index is entered in the connect by callsign # list # proc cbc_index_out_of_range {idx} { playNumber $idx; playSilence 50; playMsg "idx_out_of_range"; } # # Executed when there are more than nine matches in the connect by # callsign function # proc cbc_too_many_matches {} { playMsg "too_many_matches"; } # # Executed when no station have been chosen in 60 seconds in the connect # by callsign function # proc cbc_timeout {} { playMsg "aborted"; } # # Executed when the disconnect by callsign list has been retrieved # proc dbc_list {call_list} { playMsg "disconnect_by_callsign"; playSilence 200 playMsg "choose_station"; set idx 0; foreach {call} $call_list { incr idx; playSilence 500; playNumber $idx; playSilence 200; spellEchoLinkCallsign $call; } } # # Executed when the disconnect by callsign function is manually aborted # proc dbc_aborted {} { playMsg "disconnect_by_callsign"; playMsg "aborted"; } # # Executed when an out of range index is entered in the disconnect by callsign # list # proc dbc_index_out_of_range {idx} { playNumber $idx; playSilence 50; playMsg "idx_out_of_range"; } # # Executed when no station have been chosen in 60 seconds in the disconnect # by callsign function # proc dbc_timeout {} { playMsg "disconnect_by_callsign"; playMsg "timeout"; } # # Executed when a local user enter the DTMF code for playing back the # local node ID. # proc play_node_id {my_node_id} { playMsg "node_id_is"; playSilence 200; if { $my_node_id != 0} { playNumber $my_node_id; } else { playMsg "unknown"; } } # # Executed when an entered command failed or have bad syntax. # proc command_failed {cmd} { spellWord $cmd; playMsg "operation_failed"; } # # Executed when an unrecognized command has been received. # proc unknown_command {cmd} { spellWord $cmd; playMsg "unknown_command"; } # # Executed when the listen only feature is activated or deactivated # status - The current status of the feature (0=deactivated, 1=activated) # activate - The requested new status of the feature # (0=deactivate, 1=activate) # proc listen_only {status activate} { variable module_name; if {$status == $activate} { playMsg "listen_only"; playMsg [expr {$status ? "already_active" : "not_active"}]; } else { puts "$module_name: [expr {$activate ? "Activating" : "Deactivating"}]\ listen only mode."; playMsg [expr {$activate ? "activating" : "deactivating"}]; playMsg "listen_only"; } } # # Executed when an outgoing connection is rejected. This can happen if # REJECT_OUTGOING and/or ACCEPT_OUTGOING has been setup. # proc reject_outgoing_connection {call} { spellEchoLinkCallsign $call; playSilence 50; playMsg "reject_connection"; } # # Executed when a transmission from an EchoLink station is starting # or stopping # rx - 1 if receiving or 0 if not # call - The callsign of the remote station # # proc is_receiving {rx call} { if {$rx == 0} { playTone 1000 100 100; } } # # Executed when a chat message is received from a remote station # # msg -- The message text # # WARNING: This is a slightly dangerous function since unexepected input # may open up a security flaw. Make sure that the message string is handled # as unknown data that can contain anything. Check it thoroughly before # using it. Do not run SvxLink as user root. proc chat_received {msg} { #puts $msg } # # Executed when an info message is received from a remote station # # call -- The callsign of the sending station # msg -- The message text # # WARNING: This is a slightly dangerous function since unexepected input # may open up a security flaw. Make sure that the message string is handled # as unknown data that can contain anything. Check it thoroughly before # using it. Do not run SvxLink as user root. proc info_received {call msg} { #puts "$call: $msg" } # # Executed when a configuration variable is updated at runtime # proc config_updated {tag value} { #puts "Configuration variable updated: $tag=$value" } #----------------------------------------------------------------------------- # The events below are for remote EchoLink announcements. Sounds are not # played over the local transmitter but are sent to the remote station. #----------------------------------------------------------------------------- # # Executed when an incoming connection is accepted # proc remote_greeting {call} { # playSilence 1000; # playMsg "greeting"; } # # Executed when an incoming connection is rejected # proc reject_remote_connection {perm} { playSilence 1000; if {$perm} { playMsg "reject_connection"; } else { playMsg "reject_connection"; playMsg "please_try_again_later" } playSilence 1000; } # # Executed when the inactivity timer times out # proc remote_timeout {} { playMsg "timeout"; playSilence 1000; } # # Executed when the squelch state changes # proc squelch_open {is_open} { # The listen_only_active and CFG_REMOTE_RGR_SOUND global variables are set by # the C++ code variable listen_only_active variable CFG_REMOTE_RGR_SOUND if {$CFG_REMOTE_RGR_SOUND && !$is_open && !$listen_only_active} { playSilence 200 playTone 1000 100 100 } } # end of namespace } # # This file has not been truncated #
John Pumford-Green Tue May 3 20:58:10 2022
Page Updated : 06/03/25 06:49 GMT