Keelan Lightfoot преди 7 години
родител
ревизия
fe686e55f9
променени са 9 файла, в които са добавени 809 реда и са изтрити 23 реда
  1. 89
    0
      chopper.go
  2. 2
    2
      demodulator.go
  3. 9
    1
      dsp.go
  4. 2
    3
      encoder.go
  5. 6
    6
      examples/audioin/main.go
  6. 176
    0
      examples/foo/main.go
  7. 41
    11
      examples/main.go
  8. 480
    0
      examples/rttymodem/main.go
  9. 4
    0
      modulator.go

+ 89
- 0
chopper.go Целия файл

@@ -0,0 +1,89 @@
1
+// Copyright (C) 2017 Keelan Lightfoot
2
+// Copyright (C) 2007-2008 Board of Regents of the University of Wisconsin
3
+//             System (Univ. of Wisconsin-Madison, Trace R&D Center)
4
+// Copyright (C) 2007-2008 Omnitor AB
5
+// Copyright (C) 2007-2008 Voiceriver Inc
6
+//
7
+// This library is free software; you can redistribute it and/or modify it
8
+// under the terms of the GNU Lesser General Public License as published by
9
+// the Free Software Foundation; either version 2.1 of the License, or (at
10
+// your option) any later version.
11
+
12
+package gobaudot
13
+
14
+import (
15
+	"math"
16
+)
17
+
18
+
19
+
20
+type TimeSlice struct {
21
+	Marking		bool
22
+	Carrier		bool
23
+	LpfVal		float64
24
+	ThreshVal		float64
25
+}
26
+
27
+// Chopper is fun
28
+type Chopper struct {
29
+	input         chan int16
30
+	output        chan TimeSlice  
31
+	dsp           *dsp
32
+	settings      *ModemSettings
33
+	sigOut        bool
34
+	samplerate    int
35
+	div           int
36
+}
37
+
38
+// NewChopper is fun
39
+func NewChopper(s *ModemSettings, input chan int16, output chan TimeSlice) *Chopper {
40
+
41
+	d := new(Chopper)
42
+	d.input = input
43
+	d.output = output
44
+	
45
+	d.settings = s
46
+
47
+	// the bandpass filter is picky about the sample rate, and the output is perfectly
48
+	// balanced when the sample rate is 3x the center frequency, so lets divide the sample
49
+	// rate down to as close to that as we can get.
50
+
51
+	centerFrequency := math.Abs(float64(d.settings.OneFreq)+float64(d.settings.ZeroFreq)) / 2 * 3
52
+
53
+	d.div = int(math.Floor(float64(d.settings.SampleRate) / float64(centerFrequency)))
54
+	if d.div == 0 {
55
+		d.div = 1
56
+	}
57
+
58
+	d.samplerate = d.settings.SampleRate / d.div
59
+	
60
+	d.dsp = newDsp(d.settings.OneFreq, d.settings.ZeroFreq, d.samplerate)
61
+
62
+	go d.run()
63
+	return d
64
+}
65
+
66
+func (d *Chopper) run() {
67
+
68
+	count := 0
69
+	for sam := range d.input {
70
+	
71
+		count++
72
+		// can't remember what this is for
73
+		if count%d.div > 0 {
74
+			continue
75
+		}
76
+
77
+		d.dsp.demod(float64(sam))
78
+
79
+		d.output <- TimeSlice{
80
+			Marking: d.dsp.mark(),
81
+			Carrier: d.dsp.carrier(),
82
+			LpfVal: d.dsp.lpfVal(),
83
+			ThreshVal: d.dsp.threshVal(),
84
+		}
85
+		
86
+		
87
+	}
88
+
89
+}

+ 2
- 2
demodulator.go Целия файл

@@ -12,7 +12,7 @@
12 12
 package gobaudot
13 13
 
14 14
 import (
15
-	"fmt"
15
+	//"fmt"
16 16
 	"math"
17 17
 )
18 18
 
@@ -190,7 +190,7 @@ func (d *Demodulator) run() {
190 190
 					   assume we have lost d.sampleRateK signal */
191 191
 					if d.minSigTimeout > d.samplesPerBit {
192 192
 						nextState = demodStateWaitStart
193
-						fmt.Println("demodStateWaitStart")
193
+						//fmt.Println("demodStateWaitStart")
194 194
 
195 195
 					}
196 196
 				} else {

+ 9
- 1
dsp.go Целия файл

@@ -69,7 +69,15 @@ func (d *dsp) edgeDetected() bool {
69 69
 }
70 70
 
71 71
 func (d *dsp) carrier() bool {
72
-	return math.Abs(d.demLpf) > d.demTotal*d.minThresh
72
+	return math.Abs(d.demLpf) > d.demTotal*d.minThresh && d.demTotal*d.minThresh != 0
73
+}
74
+
75
+func (d *dsp) lpfVal() float64 {
76
+	return math.Abs(d.demLpf)
77
+}
78
+
79
+func (d *dsp) threshVal() float64 {
80
+	return d.demTotal*d.minThresh
73 81
 }
74 82
 
75 83
 func (d *dsp) level() (float64, float64, float64) {

+ 2
- 3
encoder.go Целия файл

@@ -11,7 +11,6 @@
11 11
 
12 12
 package gobaudot
13 13
 
14
-import "fmt"
15 14
 
16 15
 // Encoder is fun
17 16
 type Encoder struct {
@@ -100,7 +99,7 @@ func (e *Encoder) run() {
100 99
 			modCase = c.shift
101 100
 
102 101
 			/*and update our counter*/
103
-			fmt.Println("charsWithoutShift", charsWithoutShift)
102
+			//fmt.Println("charsWithoutShift", charsWithoutShift)
104 103
 			charsWithoutShift = 0
105 104
 		}
106 105
 
@@ -116,7 +115,7 @@ func (e *Encoder) run() {
116 115
 			} else {
117 116
 				e.output <- e.charset.shift()
118 117
 			}
119
-			fmt.Println("charsWithoutShift", charsWithoutShift)
118
+			//fmt.Println("charsWithoutShift", charsWithoutShift)
120 119
 
121 120
 			charsWithoutShift = 0
122 121
 		}

+ 6
- 6
examples/audioin/main.go Целия файл

@@ -9,7 +9,7 @@ import (
9 9
 
10 10
 func main() {
11 11
 
12
-	s := &openbaudot.ModemSettings{
12
+	s := &gobaudot.ModemSettings{
13 13
 		Baud:       45,
14 14
 		StopBits:   2,
15 15
 		DataBits:   5,
@@ -27,7 +27,7 @@ func main() {
27 27
 		panic(err)
28 28
 	}
29 29
 	defer stream.Close()
30
-	charset := openbaudot.LoadCharset(&openbaudot.USTTY)
30
+	charset := gobaudot.LoadCharset(&gobaudot.USTTY)
31 31
 	//
32 32
 	// encoderIn := make(chan rune)
33 33
 	// modulatorIn := make(chan byte)
@@ -39,10 +39,10 @@ func main() {
39 39
 	//done := make(chan struct{})
40 40
 
41 41
 	//sig := make(chan [2]float64)
42
-	// encoder := openbaudot.NewEncoder(encoderIn, modulatorIn, charset)
43
-	// openbaudot.NewModulator(s, modulatorIn, demodulatorIn)
44
-	openbaudot.NewDemodulator(s, demodulatorIn, decoderIn)
45
-	openbaudot.NewDecoder(decoderIn, decoderOut, charset)
42
+	// encoder := gobaudot.NewEncoder(encoderIn, modulatorIn, charset)
43
+	// gobaudot.NewModulator(s, modulatorIn, demodulatorIn)
44
+	gobaudot.NewDemodulator(s, demodulatorIn, decoderIn)
45
+	gobaudot.NewDecoder(decoderIn, decoderOut, charset)
46 46
 
47 47
 	go func() {
48 48
 		for {

+ 176
- 0
examples/foo/main.go Целия файл

@@ -0,0 +1,176 @@
1
+package main
2
+
3
+import (
4
+	"fmt"
5
+
6
+	"github.com/gordonklaus/portaudio"
7
+	"github.com/naleek/openbaudot"
8
+)
9
+
10
+// CLoIP
11
+// Current Loop over IP
12
+// A Terrible Protocol
13
+// Based on TCP (because latency really doesn't matter)
14
+// Full duplex
15
+
16
+// client-server protocol
17
+// server handles pairing
18
+// client handles encoding/decoding
19
+
20
+/*
21
+
22
+SERVER
23
+
24
+<client connects>
25
+
26
+>> greeting message
27
+<< IAM <client ID>
28
+>> 200 ok
29
+
30
+<< RATE 1000
31
+>> 200 ok
32
+
33
+<< WAIT
34
+>> 200 ok <connection ID>
35
+
36
+
37
+-- or -- 
38
+
39
+<< JOIN <connection ID>
40
+>> 200 ok
41
+
42
+
43
+
44
+
45
+*/
46
+
47
+type Frame struct {
48
+	Seq		uint32
49
+	Len		uint32
50
+	Samples []byte
51
+}
52
+
53
+func main() {
54
+
55
+	s := &gobaudot.ModemSettings{
56
+		Baud:       45,
57
+		StopBits:   2,
58
+		DataBits:   5,
59
+		SampleRate: 8000,
60
+		OneFreq:    2125,
61
+		ZeroFreq:   2295,
62
+	}
63
+
64
+	portaudio.Initialize()
65
+	defer portaudio.Terminate()
66
+
67
+	in := make([]int16, 64)
68
+	stream, err := portaudio.OpenDefaultStream(1, 0, float64(s.SampleRate), len(in), in)
69
+	if err != nil {
70
+		panic(err)
71
+	}
72
+	defer stream.Close()
73
+	
74
+	chopperIn := make(chan int16)
75
+	chopperOut := make(chan gobaudot.TimeSlice)
76
+	
77
+	gobaudot.NewChopper(s, chopperIn, chopperOut)
78
+
79
+
80
+	var buff byte
81
+	var bits int
82
+	x := 0
83
+	
84
+	sampleRate := 500
85
+	
86
+	rollover := s.SampleRate/sampleRate
87
+	
88
+	fmt.Println("Data Rate: ", sampleRate/8, "bytes per second")
89
+	fmt.Println(rollover)
90
+	
91
+	go func() {
92
+		for {
93
+			r := <-chopperOut
94
+			x++
95
+			
96
+			if r.Carrier && x%rollover==0 {
97
+				buff = buff << 1
98
+				bits++
99
+				if r.Marking {
100
+					buff = buff | 0x01
101
+				}
102
+				if bits == 8 {
103
+					fmt.Printf("%x",buff)
104
+					bits = 0
105
+					buff = 0
106
+				}
107
+				//fmt.Println(r.LpfVal, r.ThreshVal)
108
+			}
109
+		}
110
+	}()
111
+
112
+	stream.Start()
113
+	for {
114
+		stream.Read()
115
+		for i := range in {
116
+			chopperIn <- in[i]
117
+		}
118
+
119
+	}
120
+	//stream.Stop()
121
+
122
+	// go func() {
123
+	// 	encoder.EncodeString("hello world")
124
+	// 	time.Sleep(2000 * time.Millisecond)
125
+	// 	done <- struct{}{}
126
+	// }()
127
+
128
+}
129
+
130
+// // NewOBL is fun
131
+// func NewOBL(baud float64, callback Callback) *OBL {
132
+//
133
+// 	obl := new(OBL)
134
+//
135
+// 	obl.charset = USTTY.initialize()
136
+//
137
+// 	obl.callback = callback
138
+//
139
+// 	obl.mod.sinLutQ15 = make([]int16, 16384)
140
+// 	for i := 0; i < 16384; i++ {
141
+// 		obl.mod.sinLutQ15[i] = int16(32767 * math.Sin(2.0*math.Pi*float64(i)/float64(16384)))
142
+// 	}
143
+//
144
+// 	/* regular reset */
145
+// 	obl.autobaud.enabled = true
146
+// 	obl.Reset(baud)
147
+//
148
+// 	go func() {
149
+// 		portaudio.Initialize()
150
+// 		defer portaudio.Terminate()
151
+//
152
+// 		x := make(chan int16, 300)
153
+//
154
+// 		stream, _ := portaudio.OpenDefaultStream(0, 1, float64(sampleRate), 0, func(out []int16) {
155
+// 			for i := range out {
156
+// 				out[i] = <-x
157
+// 			}
158
+// 		})
159
+//
160
+// 		stream.Start()
161
+//
162
+// 		for {
163
+// 			s := <-obl.mod.output
164
+// 			//go func() {
165
+// 			obl.demod.input <- s
166
+// 			x <- s
167
+// 			//}()
168
+// 			//	binary.Write(f, binary.LittleEndian, s)
169
+// 		}
170
+// 	}()
171
+//
172
+// 	go obl.Modulate()
173
+// 	go obl.Demodulate()
174
+//
175
+// 	return obl
176
+// }

+ 41
- 11
examples/main.go Целия файл

@@ -7,9 +7,15 @@ import (
7 7
 	"github.com/naleek/openbaudot"
8 8
 )
9 9
 
10
+type TTYFrame struct {
11
+	Seq		uint32
12
+	Len		uint32
13
+	Samples []byte
14
+}
15
+
10 16
 func main() {
11 17
 
12
-	s := &openbaudot.ModemSettings{
18
+	s := &gobaudot.ModemSettings{
13 19
 		Baud:       45,
14 20
 		StopBits:   2,
15 21
 		DataBits:   5,
@@ -18,22 +24,28 @@ func main() {
18 24
 		ZeroFreq:   2295,
19 25
 	}
20 26
 
21
-	charset := openbaudot.LoadCharset(&openbaudot.USTTY)
27
+	charset := gobaudot.LoadCharset(&gobaudot.USTTY)
22 28
 
23 29
 	encoderIn := make(chan rune)
24 30
 	modulatorIn := make(chan byte)
25 31
 
26
-	demodulatorIn := make(chan int16)
27
-	decoderIn := make(chan byte)
28
-	decoderOut := make(chan rune)
32
+	//demodulatorIn := make(chan int16)
33
+//	decoderIn := make(chan byte)
34
+//	decoderOut := make(chan rune)
29 35
 
36
+	chopperIn := make(chan int16)
37
+	chopperOut := make(chan gobaudot.TimeSlice)
38
+	
30 39
 	done := make(chan struct{})
31 40
 
32 41
 	//sig := make(chan [2]float64)
33
-	encoder := openbaudot.NewEncoder(encoderIn, modulatorIn, charset)
34
-	openbaudot.NewModulator(s, modulatorIn, demodulatorIn)
35
-	openbaudot.NewDemodulator(s, demodulatorIn, decoderIn)
36
-	openbaudot.NewDecoder(decoderIn, decoderOut, charset)
42
+	encoder := gobaudot.NewEncoder(encoderIn, modulatorIn, charset)
43
+	gobaudot.NewModulator(s, modulatorIn, chopperIn)
44
+//	gobaudot.NewModulator(s, modulatorIn, demodulatorIn)
45
+	//gobaudot.NewDemodulator(s, demodulatorIn, decoderIn)
46
+//	gobaudot.NewDecoder(decoderIn, decoderOut, charset)
47
+
48
+	gobaudot.NewChopper(s, chopperIn, chopperOut)
37 49
 
38 50
 	go func() {
39 51
 		encoder.EncodeString("hello world")
@@ -42,10 +54,28 @@ func main() {
42 54
 	}()
43 55
 
44 56
 	running := true
57
+	x := 0
58
+	
59
+	var buff byte
60
+	var bits int
61
+	
45 62
 	for running {
46 63
 		select {
47
-		case r := <-decoderOut:
48
-			fmt.Printf("%c", r)
64
+		case r := <-chopperOut:
65
+			x++
66
+			
67
+			if r.Carrier && x%10==0 {
68
+				buff = buff << 1
69
+				bits++
70
+				if r.Marking {
71
+					buff = buff | 0x01
72
+				}
73
+				if bits == 8 {
74
+					fmt.Printf("%x",buff)
75
+					bits = 0
76
+					buff = 0
77
+				}
78
+			}
49 79
 		case <-done:
50 80
 			running = false
51 81
 		}

+ 480
- 0
examples/rttymodem/main.go Целия файл

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

+ 4
- 0
modulator.go Целия файл

@@ -92,6 +92,10 @@ func (m *Modulator) modulate(bit int) { //short *buffer, int samples)
92 92
 	}
93 93
 }
94 94
 
95
+func (m *Modulator) SetAmplitude(amplitude int16) {
96
+	m.amplitude = amplitude
97
+}
98
+
95 99
 func (m *Modulator) run() {
96 100
 
97 101
 	var modState int  // modulator state machine