This is an old revision of the document!
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