connect.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. // Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved.
  2. // Use of this source code is governed by the MIT-license that can be
  3. // found in the LICENSE file.
  4. package main
  5. import (
  6. "fmt"
  7. "log"
  8. "strconv"
  9. "strings"
  10. "time"
  11. "github.com/la5nta/wl2k-go/transport"
  12. "github.com/la5nta/wl2k-go/transport/ardop"
  13. "github.com/la5nta/wl2k-go/transport/winmor"
  14. "github.com/harenber/ptc-go/ptc"
  15. // Register other dialers
  16. _ "github.com/la5nta/wl2k-go/transport/ax25"
  17. _ "github.com/la5nta/wl2k-go/transport/telnet"
  18. )
  19. var (
  20. wmTNC *winmor.TNC // Pointer to the WINMOR TNC used by Listen and Connect
  21. adTNC *ardop.TNC // Pointer to the ARDOP TNC used by Listen and Connect
  22. pModem *pactor.Modem
  23. )
  24. func hasSSID(str string) bool { return strings.Contains(str, "-") }
  25. func connectAny(connectStr ...string) bool {
  26. for _, str := range connectStr {
  27. if Connect(str) {
  28. return true
  29. }
  30. }
  31. return false
  32. }
  33. func Connect(connectStr string) (success bool) {
  34. if connectStr == "" {
  35. return false
  36. } else if aliased, ok := config.ConnectAliases[connectStr]; ok {
  37. return Connect(aliased)
  38. }
  39. url, err := transport.ParseURL(connectStr)
  40. if err != nil {
  41. log.Println(err)
  42. return false
  43. }
  44. // Init TNCs
  45. switch url.Scheme {
  46. case "ardop":
  47. if err := initArdopTNC(); err != nil {
  48. log.Println(err)
  49. return
  50. }
  51. case "winmor":
  52. if err := initWinmorTNC(); err != nil {
  53. log.Println(err)
  54. return
  55. }
  56. case "pactor":
  57. if err := initPactorModem(); err != nil {
  58. log.Println(err)
  59. return
  60. }
  61. }
  62. // Set default userinfo (mycall)
  63. if url.User == nil {
  64. url.SetUser(fOptions.MyCall)
  65. }
  66. // Set default host interface address
  67. if url.Host == "" {
  68. switch url.Scheme {
  69. case "ax25":
  70. url.Host = config.AX25.Port
  71. case "serial-tnc":
  72. url.Host = config.SerialTNC.Path
  73. if config.SerialTNC.Baudrate > 0 {
  74. url.Params.Set("hbaud", fmt.Sprint(config.SerialTNC.Baudrate))
  75. }
  76. }
  77. }
  78. // Radio Only?
  79. radioOnly := fOptions.RadioOnly
  80. if v := url.Params.Get("radio_only"); v != "" {
  81. radioOnly, _ = strconv.ParseBool(v)
  82. }
  83. if radioOnly {
  84. if hasSSID(fOptions.MyCall) {
  85. log.Println("Radio Only does not support callsign with SSID")
  86. return
  87. }
  88. switch url.Scheme {
  89. case "ax25", "serial-tnc":
  90. log.Printf("Radio-Only is not available for %s", url.Scheme)
  91. return
  92. default:
  93. url.SetUser(url.User.Username() + "-T")
  94. }
  95. }
  96. // QSY
  97. var revertFreq func()
  98. if freq := url.Params.Get("freq"); freq != "" {
  99. revertFreq, err = qsy(url.Scheme, freq)
  100. if err != nil {
  101. log.Printf("Unable to QSY: %s", err)
  102. return
  103. }
  104. defer revertFreq()
  105. }
  106. var currFreq Frequency
  107. if vfo, ok := VFOForTransport(url.Scheme); ok {
  108. f, _ := vfo.GetFreq()
  109. currFreq = Frequency(f)
  110. }
  111. // Wait for a clear channel
  112. switch url.Scheme {
  113. case "ardop":
  114. waitBusy(adTNC)
  115. case "winmor":
  116. waitBusy(wmTNC)
  117. }
  118. // Catch interrupts (signals) while dialing, so users can abort ardop/winmor connects.
  119. doneHandleInterrupt := handleInterrupt()
  120. log.Printf("Connecting to %s (%s)...", url.Target, url.Scheme)
  121. conn, err := transport.DialURL(url)
  122. close(doneHandleInterrupt)
  123. eventLog.LogConn("connect "+connectStr, currFreq, conn, err)
  124. if err != nil {
  125. log.Printf("Unable to establish connection to remote: %s", err)
  126. return
  127. }
  128. err = exchange(conn, url.Target, false)
  129. if err != nil {
  130. log.Printf("Exchange failed: %s", err)
  131. } else {
  132. log.Println("Disconnected.")
  133. success = true
  134. }
  135. return
  136. }
  137. func qsy(method, addr string) (revert func(), err error) {
  138. noop := func() {}
  139. var rigName string
  140. switch method {
  141. case MethodWinmor:
  142. rigName = config.Winmor.Rig
  143. case MethodArdop:
  144. rigName = config.Ardop.Rig
  145. case MethodAX25:
  146. rigName = config.AX25.Rig
  147. case MethodPactor:
  148. rigName = config.Pactor.Rig
  149. default:
  150. return noop, fmt.Errorf("Not supported with transport '%s'", method)
  151. }
  152. if rigName == "" {
  153. return noop, fmt.Errorf("Missing rig reference in config section for %s, don't know which rig to qsy", method)
  154. }
  155. var ok bool
  156. rig, ok := rigs[rigName]
  157. if !ok {
  158. return noop, fmt.Errorf("Hamlib rig '%s' not loaded.", rigName)
  159. }
  160. log.Printf("QSY %s: %s", method, addr)
  161. _, oldFreq, err := setFreq(rig, addr)
  162. if err != nil {
  163. return noop, err
  164. }
  165. time.Sleep(3 * time.Second)
  166. return func() {
  167. time.Sleep(time.Second)
  168. log.Printf("QSX %s: %.3f", method, float64(oldFreq)/1e3)
  169. rig.SetFreq(oldFreq)
  170. }, nil
  171. }
  172. func waitBusy(b transport.BusyChannelChecker) {
  173. printed := false
  174. for b.Busy() {
  175. if !printed && fOptions.IgnoreBusy {
  176. log.Println("Ignoring busy channel!")
  177. break
  178. } else if !printed {
  179. log.Println("Waiting for clear channel...")
  180. printed = true
  181. }
  182. time.Sleep(300 * time.Millisecond)
  183. }
  184. }
  185. func initWinmorTNC() error {
  186. if wmTNC != nil && wmTNC.Ping() == nil {
  187. return nil
  188. }
  189. if wmTNC != nil {
  190. wmTNC.Close()
  191. }
  192. var err error
  193. wmTNC, err = winmor.Open(config.Winmor.Addr, fOptions.MyCall, config.Locator)
  194. if err != nil {
  195. return fmt.Errorf("WINMOR TNC initialization failed: %s", err)
  196. }
  197. if config.Winmor.DriveLevel != 0 {
  198. if err := wmTNC.SetDriveLevel(config.Winmor.DriveLevel); err != nil {
  199. log.Println("Failed to set WINMOR drive level:", err)
  200. }
  201. }
  202. if v, err := wmTNC.Version(); err != nil {
  203. return fmt.Errorf("WINMOR TNC initialization failed: %s", err)
  204. } else {
  205. log.Printf("WINMOR TNC v%s initialized", v)
  206. }
  207. transport.RegisterDialer("winmor", wmTNC)
  208. if !config.Winmor.PTTControl {
  209. return nil
  210. }
  211. rig, ok := rigs[config.Winmor.Rig]
  212. if !ok {
  213. return fmt.Errorf("Unable to set PTT rig '%s': Not defined or not loaded.", config.Winmor.Rig)
  214. }
  215. wmTNC.SetPTT(rig)
  216. return nil
  217. }
  218. func initArdopTNC() error {
  219. if adTNC != nil && adTNC.Ping() == nil {
  220. return nil
  221. }
  222. if adTNC != nil {
  223. adTNC.Close()
  224. }
  225. var err error
  226. adTNC, err = ardop.OpenTCP(config.Ardop.Addr, fOptions.MyCall, config.Locator)
  227. if err != nil {
  228. return fmt.Errorf("ARDOP TNC initialization failed: %s", err)
  229. }
  230. if !config.Ardop.ARQBandwidth.IsZero() {
  231. if err := adTNC.SetARQBandwidth(config.Ardop.ARQBandwidth); err != nil {
  232. return fmt.Errorf("Unable to set ARQ bandwidth for ardop TNC: %s", err)
  233. }
  234. }
  235. if err := adTNC.SetCWID(config.Ardop.CWID); err != nil {
  236. return fmt.Errorf("Unable to configure CWID for ardop TNC: %s", err)
  237. }
  238. if v, err := adTNC.Version(); err != nil {
  239. return fmt.Errorf("ARDOP TNC initialization failed: %s", err)
  240. } else {
  241. log.Printf("ARDOP TNC (%s) initialized", v)
  242. }
  243. transport.RegisterDialer("ardop", adTNC)
  244. if !config.Ardop.PTTControl {
  245. return nil
  246. }
  247. rig, ok := rigs[config.Ardop.Rig]
  248. if !ok {
  249. return fmt.Errorf("Unable to set PTT rig '%s': Not defined or not loaded.", config.Ardop.Rig)
  250. }
  251. adTNC.SetPTT(rig)
  252. return nil
  253. }
  254. func initPactorModem() error {
  255. if pModem != nil {
  256. pModem.Close()
  257. }
  258. var err error
  259. pModem, err = pactor.OpenModem(config.Pactor.Path, config.Pactor.Baudrate, fOptions.MyCall, config.Pactor.InitScript)
  260. if err != nil || pModem == nil {
  261. return fmt.Errorf("Pactor initialization failed: %s", err)
  262. }
  263. transport.RegisterDialer("pactor", pModem)
  264. return nil
  265. }