(ModuleEchoLink.conf)
 
Line 96: Line 96:
 
LINK_IDLE_TIMEOUT=300
 
LINK_IDLE_TIMEOUT=300
 
DESCRIPTION="Link to BrandMeister TGxxx0\n"
 
DESCRIPTION="Link to BrandMeister TGxxx0\n"
 +
</pre>
 +
 +
=== optional registry plugin for SQL_DET=PTY: 991-SvxLink.lua ===
 +
<pre>
 +
require('Global')
 +
 +
local bit = require('bit')
 +
local fio = require('fio')
 +
local log = require('log')
 +
 +
--[[
 +
 +
  SvxLink PTY SQL_DET Plug-In
 +
 +
]]--
 +
 +
local cfg = {
 +
  [2840]    = { 20, '/tmp/sql.2840',  nil },
 +
  [28430]  = { 10, '/tmp/sql.28430',  nil },
 +
  [284359]  = { 11, '/tmp/sql.284359', nil }
 +
}
 +
 +
local function setSquelch(destination, state)
 +
  local file = fio.readlink(cfg[destination][2])
 +
  if file ~= nil then
 +
    local handle = fio.open(file, { 'O_WRONLY' })
 +
    handle:write(state)
 +
    handle:close()
 +
  end
 +
end
 +
 +
local function resetSquelch(state)
 +
  for k, v in pairs(cfg) do
 +
    -- log.info("SvxLink: reset %s %s %s", k, v[1], v[2], v[3])
 +
    setSquelch(k, state)
 +
  end
 +
end
 +
 +
local function checkSquelch(destination, state, newSessionID)
 +
  if state == 'O' and cfg[destination][3] == nil then
 +
    setSquelch(destination, state)
 +
    cfg[destination][3] = newSessionID
 +
    -- log.info("SvxLink: open %d %s", destination, newSessionID)
 +
  end
 +
 +
  if state == 'Z' and cfg[destination][3] == newSessionID then
 +
    setSquelch(destination, state)
 +
    cfg[destination][3] = nil
 +
    -- log.info("SvxLink: close %d %s", destination, newSessionID)
 +
  end
 +
end
 +
 +
local SvxLink = { }
 +
 +
function SvxLink.start()
 +
  resetSquelch('Z')
 +
end
 +
 +
function SvxLink.handleSystemEvent(network, data)
 +
  -- work around to detect brandmeister core restart
 +
  if data['Event'] == 'Initialize' and
 +
    data['LinkName'] == 'FastForward'
 +
  then
 +
    resetSquelch('Z')
 +
  end
 +
end
 +
 +
function SvxLink.handleSessionEvent(network, data)
 +
  if data['Event'] == 'Session-Start-Extended' or
 +
    data['Event'] == 'Session-Stop-Extended'
 +
  then
 +
    local kind        = tonumber(data['LinkType'    ])
 +
    local flavor      = tonumber(data['SessionType'  ])
 +
    local number      = tonumber(data['Number'      ])
 +
    local destination = tonumber(data['DestinationID'])
 +
    local sessionid  =          data['SessionID'    ]
 +
 +
    if cfg[destination] ~= nil then
 +
      if number ~= cfg[destination][1] and
 +
        bit.band(flavor, SESSION_TYPE_FLAG_VOICE) ~= 0 and
 +
        bit.band(flavor, SESSION_TYPE_FLAG_GROUP) ~= 0
 +
      then
 +
        if data['Event'] == 'Session-Start-Extended' then
 +
          checkSquelch(destination, 'O', sessionid)
 +
        else
 +
          checkSquelch(destination, 'Z', sessionid)
 +
        end
 +
      end
 +
    end
 +
  end
 +
end
 +
 +
return SvxLink
 
</pre>
 
</pre>

Revision as of 20:20, 18 November 2021

EchoLink® software allows licensed Amateur Radio stations to communicate with one another over the Internet, using streaming-audio technology.

http://echolink.org

Cross-linking with BrandMeister

To create cross-link you need several things:

  • An valid EchoLink node account
  • Patched version of SVXLink software that should be deployed on the same server with BrandMeister
  • One of supported AMBE Dongles

Patch is available here - https://code.brandmeister.network/r3abm/AutoPatch.git
Patched version of SVXLink is available here = https://github.com/stefansaraev/svxlink/tree/autopatch

Compiling

  1. # create user and group
  2. groupadd svxlink
  3. useradd -r -g nogroup -s /sbin/nologin -d /opt/SVXLink -c "SvxLink Daemon" svxlink
  4. usermod -G audio,nogroup,svxlink svxlink
  5.  
  6. # fetch patched sources, compile and install into /opt/SVXLink
  7. cd /usr/src
  8. git clone https://github.com/stefansaraev/svxlink.git -b autopatch
  9. cd svxlink
  10. ./build.sh
  11.  
  12. # install the systemd unit if you want to run svxlink as systemd service
  13. cp bm/svxlink.service /etc/systemd/system/
  14. systemctl enable svxlink
  15.  

System configuration

The supplied patch used to translate callsigns that come from EchoLink into DMR IDs.

svxlink.conf

  1. [GLOBAL]
  2. LOGICS=SimplexLogic
  3. CFG_DIR=svxlink.d
  4. TIMESTAMP_FORMAT="%c"
  5. # CARD_SAMPLE_RATE should be 8000
  6. CARD_SAMPLE_RATE=8000
  7. CARD_CHANNELS=1
  8.  
  9. [SimplexLogic]
  10. TYPE=Simplex
  11. RX=Rx1
  12. TX=Tx1
  13. MODULES=ModuleEchoLink
  14. CALLSIGN=NOCALL
  15. EVENT_HANDLER=/opt/SVXLink/share/svxlink/events.tcl
  16.  
  17. [Rx1]
  18. TYPE=Local
  19. AUDIO_DEV=alsa:hw:Loopback,1,2
  20. AUDIO_CHANNEL=0
  21. SQL_DET=VOX
  22. SQL_START_DELAY=0
  23. SQL_HANGTIME=1500
  24. SQL_TIMEOUT=180
  25. VOX_FILTER_DEPTH=20
  26. VOX_THRESH=50
  27.  
  28. [Tx1]
  29. TYPE=Local
  30. AUDIO_DEV=alsa:hw:Loopback,0,0
  31. AUDIO_CHANNEL=0
  32. PTT_TYPE=NONE
  33. TIMEOUT=180
  34.  

ModuleEchoLink.conf

  1. [ModuleEchoLink]
  2. # DMR ID to use as a default when SVXLink cannot provide a source callsign
  3. BRIDGE_DEFAULT=nnn
  4. # MASTER ID : AUTOPATCH ID
  5. BRIDGE_PROXY=xxx1:10
  6. # Encoding of text used by EchoLink in your region
  7. BRIDGE_ENCODING=cp1251
  8.  
  9. NAME=EchoLink
  10. ID=2
  11. TIMEOUT=0
  12. SERVERS=servers.echolink.org
  13. CALLSIGN=N0CALL-L
  14. PASSWORD=trololol
  15. SYSOPNAME=N0CALL
  16. LOCATION=BrandMeister TGxxx0
  17. MAX_QSOS=50
  18. MAX_CONNECTIONS=51
  19. LINK_IDLE_TIMEOUT=300
  20. DESCRIPTION="Link to BrandMeister TGxxx0\n"

optional registry plugin for SQL_DET=PTY: 991-SvxLink.lua

  1. require('Global')
  2.  
  3. local bit = require('bit')
  4. local fio = require('fio')
  5. local log = require('log')
  6.  
  7. --[[
  8.  
  9. SvxLink PTY SQL_DET Plug-In
  10.  
  11. ]]--
  12.  
  13. local cfg = {
  14. [2840] = { 20, '/tmp/sql.2840', nil },
  15. [28430] = { 10, '/tmp/sql.28430', nil },
  16. [284359] = { 11, '/tmp/sql.284359', nil }
  17. }
  18.  
  19. local function setSquelch(destination, state)
  20. local file = fio.readlink(cfg[destination][2])
  21. if file ~= nil then
  22. local handle = fio.open(file, { 'O_WRONLY' })
  23. handle:write(state)
  24. handle:close()
  25. end
  26. end
  27.  
  28. local function resetSquelch(state)
  29. for k, v in pairs(cfg) do
  30. -- log.info("SvxLink: reset %s %s %s", k, v[1], v[2], v[3])
  31. setSquelch(k, state)
  32. end
  33. end
  34.  
  35. local function checkSquelch(destination, state, newSessionID)
  36. if state == 'O' and cfg[destination][3] == nil then
  37. setSquelch(destination, state)
  38. cfg[destination][3] = newSessionID
  39. -- log.info("SvxLink: open %d %s", destination, newSessionID)
  40. end
  41.  
  42. if state == 'Z' and cfg[destination][3] == newSessionID then
  43. setSquelch(destination, state)
  44. cfg[destination][3] = nil
  45. -- log.info("SvxLink: close %d %s", destination, newSessionID)
  46. end
  47. end
  48.  
  49. local SvxLink = { }
  50.  
  51. function SvxLink.start()
  52. resetSquelch('Z')
  53. end
  54.  
  55. function SvxLink.handleSystemEvent(network, data)
  56. -- work around to detect brandmeister core restart
  57. if data['Event'] == 'Initialize' and
  58. data['LinkName'] == 'FastForward'
  59. then
  60. resetSquelch('Z')
  61. end
  62. end
  63.  
  64. function SvxLink.handleSessionEvent(network, data)
  65. if data['Event'] == 'Session-Start-Extended' or
  66. data['Event'] == 'Session-Stop-Extended'
  67. then
  68. local kind = tonumber(data['LinkType' ])
  69. local flavor = tonumber(data['SessionType' ])
  70. local number = tonumber(data['Number' ])
  71. local destination = tonumber(data['DestinationID'])
  72. local sessionid = data['SessionID' ]
  73.  
  74. if cfg[destination] ~= nil then
  75. if number ~= cfg[destination][1] and
  76. bit.band(flavor, SESSION_TYPE_FLAG_VOICE) ~= 0 and
  77. bit.band(flavor, SESSION_TYPE_FLAG_GROUP) ~= 0
  78. then
  79. if data['Event'] == 'Session-Start-Extended' then
  80. checkSquelch(destination, 'O', sessionid)
  81. else
  82. checkSquelch(destination, 'Z', sessionid)
  83. end
  84. end
  85. end
  86. end
  87. end
  88.  
  89. return SvxLink

EchoLink® software allows licensed Amateur Radio stations to communicate with one another over the Internet, using streaming-audio technology.

http://echolink.org

Cross-linking with BrandMeister[edit]

To create cross-link you need several things:

Patch is available here - https://code.brandmeister.network/r3abm/AutoPatch.git
Patched version of SVXLink is available here = https://github.com/stefansaraev/svxlink/tree/autopatch

Compiling[edit]

  1. # create user and group
  2. groupadd svxlink
  3. useradd -r -g nogroup -s /sbin/nologin -d /opt/SVXLink -c "SvxLink Daemon" svxlink
  4. usermod -G audio,nogroup,svxlink svxlink
  5.  
  6. # fetch patched sources, compile and install into /opt/SVXLink
  7. cd /usr/src
  8. git clone https://github.com/stefansaraev/svxlink.git -b autopatch
  9. cd svxlink
  10. ./build.sh
  11.  
  12. # install the systemd unit if you want to run svxlink as systemd service
  13. cp bm/svxlink.service /etc/systemd/system/
  14. systemctl enable svxlink
  15.  

System configuration[edit]

The supplied patch used to translate callsigns that come from EchoLink into DMR IDs.

svxlink.conf[edit]

  1. [GLOBAL]
  2. LOGICS=SimplexLogic
  3. CFG_DIR=svxlink.d
  4. TIMESTAMP_FORMAT="%c"
  5. # CARD_SAMPLE_RATE should be 8000
  6. CARD_SAMPLE_RATE=8000
  7. CARD_CHANNELS=1
  8.  
  9. [SimplexLogic]
  10. TYPE=Simplex
  11. RX=Rx1
  12. TX=Tx1
  13. MODULES=ModuleEchoLink
  14. CALLSIGN=NOCALL
  15. EVENT_HANDLER=/opt/SVXLink/share/svxlink/events.tcl
  16.  
  17. [Rx1]
  18. TYPE=Local
  19. AUDIO_DEV=alsa:hw:Loopback,1,2
  20. AUDIO_CHANNEL=0
  21. SQL_DET=VOX
  22. SQL_START_DELAY=0
  23. SQL_HANGTIME=1500
  24. SQL_TIMEOUT=180
  25. VOX_FILTER_DEPTH=20
  26. VOX_THRESH=50
  27.  
  28. [Tx1]
  29. TYPE=Local
  30. AUDIO_DEV=alsa:hw:Loopback,0,0
  31. AUDIO_CHANNEL=0
  32. PTT_TYPE=NONE
  33. TIMEOUT=180
  34.  

ModuleEchoLink.conf[edit]

  1. [ModuleEchoLink]
  2. # DMR ID to use as a default when SVXLink cannot provide a source callsign
  3. BRIDGE_DEFAULT=nnn
  4. # MASTER ID : AUTOPATCH ID
  5. BRIDGE_PROXY=xxx1:10
  6. # Encoding of text used by EchoLink in your region
  7. BRIDGE_ENCODING=cp1251
  8.  
  9. NAME=EchoLink
  10. ID=2
  11. TIMEOUT=0
  12. SERVERS=servers.echolink.org
  13. CALLSIGN=N0CALL-L
  14. PASSWORD=trololol
  15. SYSOPNAME=N0CALL
  16. LOCATION=BrandMeister TGxxx0
  17. MAX_QSOS=50
  18. MAX_CONNECTIONS=51
  19. LINK_IDLE_TIMEOUT=300
  20. DESCRIPTION="Link to BrandMeister TGxxx0\n"

optional registry plugin for SQL_DET=PTY: 991-SvxLink.lua[edit]

  1. require('Global')
  2.  
  3. local bit = require('bit')
  4. local fio = require('fio')
  5. local log = require('log')
  6.  
  7. --[[
  8.  
  9. SvxLink PTY SQL_DET Plug-In
  10.  
  11. ]]--
  12.  
  13. local cfg = {
  14. [2840] = { 20, '/tmp/sql.2840', nil },
  15. [28430] = { 10, '/tmp/sql.28430', nil },
  16. [284359] = { 11, '/tmp/sql.284359', nil }
  17. }
  18.  
  19. local function setSquelch(destination, state)
  20. local file = fio.readlink(cfg[destination][2])
  21. if file ~= nil then
  22. local handle = fio.open(file, { 'O_WRONLY' })
  23. handle:write(state)
  24. handle:close()
  25. end
  26. end
  27.  
  28. local function resetSquelch(state)
  29. for k, v in pairs(cfg) do
  30. -- log.info("SvxLink: reset %s %s %s", k, v[1], v[2], v[3])
  31. setSquelch(k, state)
  32. end
  33. end
  34.  
  35. local function checkSquelch(destination, state, newSessionID)
  36. if state == 'O' and cfg[destination][3] == nil then
  37. setSquelch(destination, state)
  38. cfg[destination][3] = newSessionID
  39. -- log.info("SvxLink: open %d %s", destination, newSessionID)
  40. end
  41.  
  42. if state == 'Z' and cfg[destination][3] == newSessionID then
  43. setSquelch(destination, state)
  44. cfg[destination][3] = nil
  45. -- log.info("SvxLink: close %d %s", destination, newSessionID)
  46. end
  47. end
  48.  
  49. local SvxLink = { }
  50.  
  51. function SvxLink.start()
  52. resetSquelch('Z')
  53. end
  54.  
  55. function SvxLink.handleSystemEvent(network, data)
  56. -- work around to detect brandmeister core restart
  57. if data['Event'] == 'Initialize' and
  58. data['LinkName'] == 'FastForward'
  59. then
  60. resetSquelch('Z')
  61. end
  62. end
  63.  
  64. function SvxLink.handleSessionEvent(network, data)
  65. if data['Event'] == 'Session-Start-Extended' or
  66. data['Event'] == 'Session-Stop-Extended'
  67. then
  68. local kind = tonumber(data['LinkType' ])
  69. local flavor = tonumber(data['SessionType' ])
  70. local number = tonumber(data['Number' ])
  71. local destination = tonumber(data['DestinationID'])
  72. local sessionid = data['SessionID' ]
  73.  
  74. if cfg[destination] ~= nil then
  75. if number ~= cfg[destination][1] and
  76. bit.band(flavor, SESSION_TYPE_FLAG_VOICE) ~= 0 and
  77. bit.band(flavor, SESSION_TYPE_FLAG_GROUP) ~= 0
  78. then
  79. if data['Event'] == 'Session-Start-Extended' then
  80. checkSquelch(destination, 'O', sessionid)
  81. else
  82. checkSquelch(destination, 'Z', sessionid)
  83. end
  84. end
  85. end
  86. end
  87. end
  88.  
  89. return SvxLink