No Description

main.go 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. package main
  2. import (
  3. "fmt"
  4. "bufio"
  5. "bytes"
  6. "github.com/gordonklaus/portaudio"
  7. "github.com/naleek/gortty"
  8. "io"
  9. "net"
  10. "regexp"
  11. "strings"
  12. "unicode/utf8"
  13. "time"
  14. "os"
  15. "strconv"
  16. )
  17. func main() {
  18. m := &modem{}
  19. rttySettings := &gortty.ModemSettings{
  20. Baud: 45,
  21. StopBits: 2,
  22. DataBits: 5,
  23. SampleRate: 8000,
  24. OneFreq: 2125,
  25. ZeroFreq: 2295,
  26. }
  27. charset := gortty.LoadCharset(&gortty.USTTY)
  28. in := make([]int16, 64)
  29. out := make([]int16, 64)
  30. var stream *portaudio.Stream
  31. useStdio := false
  32. if !useStdio {
  33. // Set up audio with system default input and output
  34. portaudio.Initialize()
  35. defer portaudio.Terminate()
  36. var err error
  37. stream, err = portaudio.OpenDefaultStream(1, 1, float64(rttySettings.SampleRate), len(in), in, out)
  38. if err != nil {
  39. panic(err)
  40. }
  41. defer stream.Close()
  42. }
  43. modulatorToAudio := make(chan int16)
  44. audioToDemodulator := make(chan int16)
  45. if !useStdio {
  46. // Set up modulator pipeline
  47. ttyToEncoder := make(chan rune, 100)
  48. m.ttyWriter = NewModemWriter(ttyToEncoder)
  49. encoderToModulator := make(chan byte)
  50. m.encoder = gortty.NewEncoder(ttyToEncoder, encoderToModulator, charset)
  51. m.modulator = gortty.NewModulator(rttySettings, encoderToModulator, modulatorToAudio)
  52. m.modulator.SetIdleDelay(1000)
  53. // Set up demodulator pipeline
  54. demodulatorToDecoder := make(chan byte)
  55. m.demodulator = gortty.NewDemodulator(rttySettings, audioToDemodulator, demodulatorToDecoder)
  56. decoderToTTY := make(chan rune)
  57. m.decoder = gortty.NewDecoder(demodulatorToDecoder, decoderToTTY, charset)
  58. m.ttyReader = NewModemReader(decoderToTTY)
  59. } else {
  60. m.ttyReader = os.Stdin
  61. m.ttyWriter = os.Stdout
  62. }
  63. go m.cli()
  64. if !useStdio {
  65. // start audio
  66. stream.Start()
  67. defer stream.Stop()
  68. // Run audio output in goroutine
  69. go func() {
  70. l := 0
  71. for v := range modulatorToAudio {
  72. out[l] = v
  73. l++
  74. if l == 64 {
  75. l = 0
  76. stream.Write()
  77. }
  78. }
  79. }()
  80. // Run audio input
  81. for {
  82. stream.Read()
  83. for i := range in {
  84. audioToDemodulator <- in[i]
  85. }
  86. }
  87. }
  88. }
  89. type ModemReader struct {
  90. input chan rune
  91. }
  92. func NewModemReader(c chan rune) *ModemReader {
  93. m := &ModemReader{}
  94. m.input = c
  95. return m
  96. }
  97. func (m *ModemReader) Read(p []byte) (n int, err error) {
  98. r := <-m.input
  99. fmt.Print(string(r))
  100. l := utf8.EncodeRune(p, r)
  101. return l, nil
  102. }
  103. func (m *ModemReader) Add(b rune) {
  104. m.input <- b
  105. }
  106. type ModemWriter struct {
  107. output chan rune
  108. }
  109. func NewModemWriter(c chan rune) *ModemWriter {
  110. m := &ModemWriter{}
  111. m.output = c
  112. return m
  113. }
  114. func (m *ModemWriter) Write(p []byte) (n int, err error) {
  115. runes := bytes.Runes(p)
  116. fmt.Print(string(runes))
  117. for _, r := range runes {
  118. m.output <- r
  119. }
  120. return len(p), nil
  121. }
  122. func (m *ModemWriter) Get() rune {
  123. return <-m.output
  124. }
  125. type modem struct {
  126. ttyReader io.Reader
  127. ttyWriter io.Writer
  128. modulator *gortty.Modulator
  129. demodulator *gortty.Demodulator
  130. encoder *gortty.Encoder
  131. decoder *gortty.Decoder
  132. }
  133. func (m *modem) modulatorCallback(event int, data interface{}) {
  134. switch event {
  135. case gortty.EventActive:
  136. fmt.Println("EventActive")
  137. m.demodulator.Inhibit(true)
  138. case gortty.EventIdle:
  139. fmt.Println("EventIdle")
  140. m.demodulator.Inhibit(false)
  141. }
  142. }
  143. type configFunction func(action int, value string) string
  144. type configRegister struct {
  145. help string
  146. f configFunction
  147. }
  148. const (
  149. registerGet = iota
  150. registerSet
  151. )
  152. func (m *modem) cli() {
  153. fmt.Println("RTTY TCP Modem 1.0")
  154. autoAnswer := false
  155. escapeRune := '?'
  156. modulatorOn := true
  157. turnaroundDelay := 0
  158. usos := false
  159. settings := map[string]configRegister{
  160. "0": {
  161. help: "auto-answer enable",
  162. f: func(action int, value string) string {
  163. switch action {
  164. case registerGet:
  165. if autoAnswer {
  166. return "1"
  167. } else {
  168. return "0"
  169. }
  170. case registerSet:
  171. autoAnswer = (value == "1")
  172. return "OK"
  173. }
  174. return ""
  175. },
  176. },
  177. "2": {
  178. help: "escape character",
  179. f: func(action int, value string) string {
  180. switch action {
  181. case registerGet:
  182. return string(escapeRune)
  183. case registerSet:
  184. runes := []rune(value)
  185. escapeRune = runes[0]
  186. return "OK"
  187. }
  188. return ""
  189. },
  190. },
  191. "3": {
  192. help: "modulator enable",
  193. f: func(action int, value string) string {
  194. switch action {
  195. case registerGet:
  196. if modulatorOn {
  197. return "1"
  198. } else {
  199. return "0"
  200. }
  201. case registerSet:
  202. modulatorOn = (value == "1")
  203. if m.modulator != nil {
  204. if modulatorOn {
  205. m.modulator.SetAmplitude(16384)
  206. } else {
  207. m.modulator.SetAmplitude(0)
  208. }
  209. }
  210. return "OK"
  211. }
  212. return ""
  213. },
  214. },
  215. "4": {
  216. help: "turnaround delay",
  217. f: func(action int, value string) string {
  218. switch action {
  219. case registerGet:
  220. return fmt.Sprintln(turnaroundDelay)
  221. case registerSet:
  222. if d, err := strconv.Atoi(value); err == nil {
  223. turnaroundDelay = d
  224. return "OK"
  225. } else {
  226. return "ERROR"
  227. }
  228. }
  229. return ""
  230. },
  231. },
  232. "5": {
  233. help: "unshift on space",
  234. f: func(action int, value string) string {
  235. switch action {
  236. case registerGet:
  237. if usos {
  238. return "1"
  239. } else {
  240. return "0"
  241. }
  242. case registerSet:
  243. usos = (value == "1")
  244. m.decoder.UnshiftOnSpace(usos)
  245. return "OK"
  246. }
  247. return ""
  248. },
  249. },
  250. }
  251. localEcho := false
  252. ttyBufferedReader := bufio.NewReader(m.ttyReader)
  253. var netConn *NetConn
  254. if m.modulator != nil {
  255. m.modulator.SetCallback(m.modulatorCallback)
  256. }
  257. for {
  258. s, _ := ttyBufferedReader.ReadString('\n')
  259. //fmt.Println(s)
  260. s = strings.ToUpper(s)
  261. regex, _ := regexp.Compile(`^AT([SHOED?])([0-9])?(\?)?(?:[=|\:]([\S]*))?(?: (.*))?`)
  262. params := regex.FindStringSubmatch(s)
  263. if params == nil {
  264. continue
  265. }
  266. command := params[1]
  267. var register string
  268. if len(params) > 2 {
  269. register = params[2]
  270. }
  271. var registerQuery string
  272. if len(params) > 3 {
  273. registerQuery = params[3]
  274. }
  275. var value string
  276. if len(params) > 4 {
  277. value = params[4]
  278. }
  279. var parameter string
  280. if len(params) > 5 {
  281. parameter = params[5]
  282. }
  283. //fmt.Fprintf(m.ttyWriter, "command: '%s' register: '%s' query: '%s' value: '%s' parameter: '%s'\n", command, register, registerQuery, value, parameter)
  284. switch command {
  285. case "?":
  286. fmt.Fprintf(m.ttyWriter, "Setting Registers\n")
  287. for s, conf := range settings {
  288. fmt.Fprintf(m.ttyWriter, "%s: %s (current value: %s)\n", s, conf.help, conf.f(registerGet, ""))
  289. }
  290. case "O": // online
  291. if !netConn.Connected() {
  292. fmt.Fprintln(m.ttyWriter, "ERROR No active session")
  293. break
  294. }
  295. fmt.Fprintln(m.ttyWriter, "OK")
  296. dataMode(netConn, ttyBufferedReader, m.ttyWriter, escapeRune)
  297. case "D": // dial
  298. if netConn.Connected() {
  299. fmt.Fprintln(m.ttyWriter, "ERROR Session in progress")
  300. break
  301. }
  302. addr := strings.TrimSpace(parameter)
  303. var err error
  304. netConn, err = NewNetConn(addr)
  305. if err != nil {
  306. fmt.Fprintln(m.ttyWriter, "ERROR", err)
  307. break
  308. } else {
  309. fmt.Fprintln(m.ttyWriter, "CONNECTED")
  310. dataMode(netConn, ttyBufferedReader, m.ttyWriter, escapeRune)
  311. }
  312. case "H": // hangup
  313. if !netConn.Connected() {
  314. fmt.Fprintln(m.ttyWriter, "ERROR No active session")
  315. break
  316. }
  317. netConn.Hangup()
  318. fmt.Fprintln(m.ttyWriter, "OK")
  319. case "E": // local echo
  320. localEcho = (register == "1")
  321. fmt.Fprintln(m.ttyWriter, "local echo", localEcho)
  322. case "S": // local echo
  323. if conf, ok := settings[register]; ok {
  324. if registerQuery == "?" {
  325. fmt.Fprintln(m.ttyWriter, conf.f(registerGet, ""))
  326. } else if value != "" {
  327. fmt.Fprintln(m.ttyWriter, conf.f(registerSet, value))
  328. } else {
  329. fmt.Fprintln(m.ttyWriter, "ERROR")
  330. }
  331. }
  332. }
  333. }
  334. }
  335. func dataMode(netConn *NetConn, ttyBufferedReader *bufio.Reader, ttyWriter io.Writer, escapeRune rune) {
  336. ttyCancelReader := make(chan struct{})
  337. ttyReadChan := make(chan rune)
  338. ttyReadPending := make(chan struct{})
  339. var t *time.Timer
  340. escapeRunes := 0
  341. go func() {
  342. for {
  343. r, l, err := ttyBufferedReader.ReadRune()
  344. if err != nil && err != io.EOF {
  345. return
  346. }
  347. if l > 0 {
  348. select {
  349. case <-ttyCancelReader:
  350. ttyBufferedReader.UnreadRune() // oops this wasn't ours. let's put it back.
  351. close(ttyReadPending)
  352. return
  353. default:
  354. ttyReadChan <- r
  355. }
  356. }
  357. }
  358. }()
  359. netReadChan := netConn.OutputChannel()
  360. netConn.Resume()
  361. pipeLoop:
  362. for {
  363. var r rune
  364. if t == nil {
  365. select {
  366. case r = <-ttyReadChan:
  367. case q := <-netReadChan:
  368. ttyWriter.Write([]byte(string(q)))
  369. continue pipeLoop
  370. }
  371. } else {
  372. select {
  373. case r = <-ttyReadChan:
  374. case q := <-netReadChan:
  375. ttyWriter.Write([]byte(string(q)))
  376. continue pipeLoop
  377. case <-t.C:
  378. fmt.Fprintln(ttyWriter, "\nOK")
  379. close(ttyCancelReader)
  380. netConn.Pause()
  381. break pipeLoop
  382. }
  383. }
  384. if r == escapeRune && escapeRunes < 3 {
  385. escapeRunes++
  386. } else {
  387. if t != nil {
  388. t.Stop()
  389. }
  390. escapeRunes = 0
  391. }
  392. if escapeRunes == 3 {
  393. t = time.NewTimer(1*time.Second)
  394. }
  395. err := netConn.Write(r)
  396. if err != nil {
  397. fmt.Fprintln(ttyWriter, "NO CARRIER")
  398. close(ttyCancelReader)
  399. break pipeLoop
  400. }
  401. }
  402. <- ttyReadPending
  403. }
  404. type NetConn struct {
  405. netReader *bufio.Reader
  406. conn net.Conn
  407. netReadChan chan rune
  408. hangup chan struct{}
  409. paused bool
  410. connected bool
  411. }
  412. func NewNetConn(addr string) (*NetConn, error) {
  413. c := &NetConn{}
  414. var err error
  415. c.conn, err = net.Dial("tcp", addr)
  416. if err != nil {
  417. return nil, err
  418. }
  419. c.connected = true
  420. c.netReader = bufio.NewReader(c.conn)
  421. c.netReadChan = make(chan rune)
  422. c.hangup = make(chan struct{})
  423. c.paused = false
  424. go func() {
  425. for {
  426. r, l, err := c.netReader.ReadRune()
  427. if err != nil && err != io.EOF {
  428. c.cleanup()
  429. return
  430. }
  431. if c.paused || l == 0 {
  432. select {
  433. case <- c.hangup:
  434. c.cleanup()
  435. return
  436. default: // discard
  437. }
  438. } else {
  439. select {
  440. case c.netReadChan <- r:
  441. case <- c.hangup:
  442. c.cleanup()
  443. return
  444. }
  445. }
  446. }
  447. }()
  448. return c, nil
  449. }
  450. func (c *NetConn) Write(r rune) error {
  451. if c == nil {
  452. return fmt.Errorf("NetConn not initialized")
  453. }
  454. _, err := c.conn.Write([]byte(string(r)))
  455. if err != nil {
  456. close(c.hangup)
  457. }
  458. return err
  459. }
  460. func (c *NetConn) Hangup() {
  461. if c == nil {
  462. return
  463. }
  464. c.conn.Close()
  465. close(c.hangup)
  466. }
  467. func (c *NetConn) cleanup() {
  468. close(c.netReadChan)
  469. c.connected = false
  470. }
  471. func (c *NetConn) Pause() {
  472. if c == nil {
  473. return
  474. }
  475. c.paused = true
  476. }
  477. func (c *NetConn) Resume() {
  478. if c == nil {
  479. return
  480. }
  481. c.paused = false
  482. }
  483. func (c *NetConn) OutputChannel() chan rune {
  484. if c == nil {
  485. return nil
  486. }
  487. return c.netReadChan
  488. }
  489. func (c *NetConn) Connected() bool {
  490. if c == nil {
  491. return false
  492. }
  493. return c.connected
  494. }