Преглед на файлове

code cleanup and i forget what other changes

Keelan Lightfoot преди 6 години
родител
ревизия
55fb023e8c
променени са 9 файла, в които са добавени 401 реда и са изтрити 357 реда
  1. 66
    55
      code.go
  2. 2
    3
      common.go
  3. 32
    21
      decoder.go
  4. 121
    116
      demodulator.go
  5. 1
    1
      dsp.go
  6. 104
    95
      encoder.go
  7. 12
    7
      modulator.go
  8. 45
    45
      rawdemodulator.go
  9. 18
    14
      rawmodulator.go

+ 66
- 55
code.go Целия файл

@@ -11,7 +11,10 @@
11 11
 
12 12
 package gortty
13 13
 
14
-import "unicode"
14
+import (
15
+	"fmt"
16
+	"unicode"
17
+)
15 18
 
16 19
 const (
17 20
 	shiftUnknown = iota
@@ -27,60 +30,63 @@ var (
27 30
 		'[': '(',
28 31
 		']': ')',
29 32
 	}
30
-	// TDD Telecommunications Device for the Deaf standard
31
-	TDD = CodeSet{
32
-		Codes: [2][]rune{
33
-			{'\b', 'E', '\n', 'A', ' ', 'S', 'I', 'U', '\r', 'D', 'R', 'J', 'N', 'F', 'C', 'K', 'T', 'Z', 'L', 'W', 'H', 'Y', 'P', 'Q', 'O', 'B', 'G', '\x0e', 'M', 'X', 'V', '\x0f'},
34
-			{'\b', '3', '\n', '-', ' ', ',', '8', '7', '\r', '$', '4', '\'', ',', '!', ':', '(', '5', '"', ')', '2', '=', '6', '0', '1', '9', '?', '+', '\x0e', '.', '/', ';', '\x0f'},
33
+
34
+	codeSets = map[string]CodeSet{
35
+		// TDD Telecommunications Device for the Deaf standard
36
+		"TDD": CodeSet{
37
+			Codes: [2][]rune{
38
+				{'\b', 'E', '\n', 'A', ' ', 'S', 'I', 'U', '\r', 'D', 'R', 'J', 'N', 'F', 'C', 'K', 'T', 'Z', 'L', 'W', 'H', 'Y', 'P', 'Q', 'O', 'B', 'G', '\x0e', 'M', 'X', 'V', '\x0f'},
39
+				{'\b', '3', '\n', '-', ' ', ',', '8', '7', '\r', '$', '4', '\'', ',', '!', ':', '(', '5', '"', ')', '2', '=', '6', '0', '1', '9', '?', '+', '\x0e', '.', '/', ';', '\x0f'},
40
+			},
41
+			Substitutes: defaultSubstitutes,
42
+			Fallback:    '\'',
43
+			ShiftCode:   0x1b,
44
+			UnshiftCode: 0x1f,
35 45
 		},
36
-		Substitutes: defaultSubstitutes,
37
-		Fallback:    '\'',
38
-		ShiftCode:   0x1b,
39
-		UnshiftCode: 0x1f,
40
-	}
41
-	// USTTY US Teletype Corporation
42
-	USTTY = CodeSet{
43
-		Codes: [2][]rune{
44
-			{'\x00', 'E', '\n', 'A', ' ', 'S', 'I', 'U', '\r', 'D', 'R', 'J', 'N', 'F', 'C', 'K', 'T', 'Z', 'L', 'W', 'H', 'Y', 'P', 'Q', 'O', 'B', 'G', '\x0e', 'M', 'X', 'V', '\x0f'},
45
-			{'\x00', '3', '\n', '-', ' ', '\a', '8', '7', '\r', '$', '4', '\'', ',', '!', ':', '(', '5', '"', ')', '2', '#', '6', '0', '1', '9', '?', '&', '\x0e', '.', '/', ';', '\x0f'},
46
+		// USTTY US Teletype Corporation
47
+		"USTTY": CodeSet{
48
+			Codes: [2][]rune{
49
+				{'\x00', 'E', '\n', 'A', ' ', 'S', 'I', 'U', '\r', 'D', 'R', 'J', 'N', 'F', 'C', 'K', 'T', 'Z', 'L', 'W', 'H', 'Y', 'P', 'Q', 'O', 'B', 'G', '\x0e', 'M', 'X', 'V', '\x0f'},
50
+				{'\x00', '3', '\n', '-', ' ', '\a', '8', '7', '\r', '$', '4', '\'', ',', '!', ':', '(', '5', '"', ')', '2', '#', '6', '0', '1', '9', '?', '&', '\x0e', '.', '/', ';', '\x0f'},
51
+			},
52
+			Substitutes: defaultSubstitutes,
53
+			Fallback:    '-',
54
+			ShiftCode:   0x1b,
55
+			UnshiftCode: 0x1f,
46 56
 		},
47
-		Substitutes: defaultSubstitutes,
48
-		Fallback:    '-',
49
-		ShiftCode:   0x1b,
50
-		UnshiftCode: 0x1f,
51
-	}
52
-	// USITA2 USITA2
53
-	USITA2 = CodeSet{
54
-		Codes: [2][]rune{
55
-			{'\x00', 'E', '\n', 'A', ' ', 'S', 'I', 'U', '\r', 'D', 'R', 'J', 'N', 'F', 'C', 'K', 'T', 'Z', 'L', 'W', 'H', 'Y', 'P', 'Q', 'O', 'B', 'G', '\x0e', 'M', 'X', 'V', '\x0f'},
56
-			{'\x00', '3', '\n', '-', ' ', '\'', '8', '7', '\r', '$', '4', '\a', ',', '!', ':', '(', '5', '"', ')', '2', '=', '6', '0', '1', '9', '?', '&', '\x0e', '.', '/', '=', '\x0f'},
57
+		// USITA2 USITA2
58
+		"USITA2": CodeSet{
59
+			Codes: [2][]rune{
60
+				{'\x00', 'E', '\n', 'A', ' ', 'S', 'I', 'U', '\r', 'D', 'R', 'J', 'N', 'F', 'C', 'K', 'T', 'Z', 'L', 'W', 'H', 'Y', 'P', 'Q', 'O', 'B', 'G', '\x0e', 'M', 'X', 'V', '\x0f'},
61
+				{'\x00', '3', '\n', '-', ' ', '\'', '8', '7', '\r', '$', '4', '\a', ',', '!', ':', '(', '5', '"', ')', '2', '=', '6', '0', '1', '9', '?', '&', '\x0e', '.', '/', '=', '\x0f'},
62
+			},
63
+			Substitutes: defaultSubstitutes,
64
+			Fallback:    '-',
65
+			ShiftCode:   0x1b,
66
+			UnshiftCode: 0x1f,
57 67
 		},
58
-		Substitutes: defaultSubstitutes,
59
-		Fallback:    '-',
60
-		ShiftCode:   0x1b,
61
-		UnshiftCode: 0x1f,
62
-	}
63
-	// Weather US Weather
64
-	Weather = CodeSet{
65
-		Codes: [2][]rune{
66
-			{'\x00', 'E', '\n', 'A', ' ', 'S', 'I', 'U', '\r', 'D', 'R', 'J', 'N', 'F', 'C', 'K', 'T', 'Z', 'L', 'W', 'H', 'Y', 'P', 'Q', 'O', 'B', 'G', '\x0e', 'M', 'X', 'V', '\x0f'},
67
-			{'\x00', '3', '\n', '↑', ' ', '\a', '8', '7', '\r', '↗', '4', '↙', '⦷', '→', '○', '←', '5', '+', '↖', '2', '↓', '6', '0', '1', '9', '⊕', '↘', '\x0e', '.', '/', '⦶', '\x0f'},
68
+		// Weather US Weather
69
+		"Weather": CodeSet{
70
+			Codes: [2][]rune{
71
+				{'\x00', 'E', '\n', 'A', ' ', 'S', 'I', 'U', '\r', 'D', 'R', 'J', 'N', 'F', 'C', 'K', 'T', 'Z', 'L', 'W', 'H', 'Y', 'P', 'Q', 'O', 'B', 'G', '\x0e', 'M', 'X', 'V', '\x0f'},
72
+				{'\x00', '3', '\n', '↑', ' ', '\a', '8', '7', '\r', '↗', '4', '↙', '⦷', '→', '○', '←', '5', '+', '↖', '2', '↓', '6', '0', '1', '9', '⊕', '↘', '\x0e', '.', '/', '⦶', '\x0f'},
73
+			},
74
+			Substitutes: defaultSubstitutes,
75
+			Fallback:    '-',
76
+			ShiftCode:   0x1b,
77
+			UnshiftCode: 0x1f,
68 78
 		},
69
-		Substitutes: defaultSubstitutes,
70
-		Fallback:    '-',
71
-		ShiftCode:   0x1b,
72
-		UnshiftCode: 0x1f,
73
-	}
74
-	// Fractions US Fractions
75
-	Fractions = CodeSet{
76
-		Codes: [2][]rune{
77
-			{'\x00', 'E', '\n', 'A', ' ', 'S', 'I', 'U', '\r', 'D', 'R', 'J', 'N', 'F', 'C', 'K', 'T', 'Z', 'L', 'W', 'H', 'Y', 'P', 'Q', 'O', 'B', 'G', '\x0e', 'M', 'X', 'V', '\x0f'},
78
-			{'\x00', '3', '\n', '↑', ' ', '\a', '8', '7', '\r', '$', '4', '\'', '⅞', '¼', '⅛', '½', '5', '"', '¾', '2', ' ', '6', '0', '1', '9', '⅝', '&', '\x0e', '.', '/', '⅜', '\x0f'},
79
+		// Fractions US Fractions
80
+		"Fractions": CodeSet{
81
+			Codes: [2][]rune{
82
+				{'\x00', 'E', '\n', 'A', ' ', 'S', 'I', 'U', '\r', 'D', 'R', 'J', 'N', 'F', 'C', 'K', 'T', 'Z', 'L', 'W', 'H', 'Y', 'P', 'Q', 'O', 'B', 'G', '\x0e', 'M', 'X', 'V', '\x0f'},
83
+				{'\x00', '3', '\n', '↑', ' ', '\a', '8', '7', '\r', '$', '4', '\'', '⅞', '¼', '⅛', '½', '5', '"', '¾', '2', ' ', '6', '0', '1', '9', '⅝', '&', '\x0e', '.', '/', '⅜', '\x0f'},
84
+			},
85
+			Substitutes: defaultSubstitutes,
86
+			Fallback:    '-',
87
+			ShiftCode:   0x1b,
88
+			UnshiftCode: 0x1f,
79 89
 		},
80
-		Substitutes: defaultSubstitutes,
81
-		Fallback:    '-',
82
-		ShiftCode:   0x1b,
83
-		UnshiftCode: 0x1f,
84 90
 	}
85 91
 )
86 92
 
@@ -106,12 +112,17 @@ type code struct {
106 112
 	v     byte
107 113
 }
108 114
 
109
-//LoadCharset is fun
110
-func LoadCharset(cs *CodeSet) *CodeSet {
111
-	if !cs.initialized {
112
-		cs.initialize()
115
+//LoadCodeSet is fun
116
+func LoadCodeSet(n string) (*CodeSet, error) {
117
+	cs, ok := codeSets[n]
118
+
119
+	if !ok {
120
+		return nil, fmt.Errorf("Attempt to load nonexistent codeset: %s", n)
113 121
 	}
114
-	return cs
122
+
123
+	cs.initialize()
124
+
125
+	return &cs, nil
115 126
 }
116 127
 
117 128
 func (cs *CodeSet) initialize() *CodeSet {

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

@@ -27,13 +27,12 @@ const (
27 27
 	EventActive
28 28
 	// EventIdle is triggered after idle
29 29
 	EventIdle
30
-	
31
-	
30
+
32 31
 	// EventStartBit is triggered with the falling edge of the start bit when transmitting a character
33 32
 	EventStartBit
34 33
 	// EventStopBit is triggered with rising edge of the stop bit when transmitting a character
35 34
 	EventStopBit
36
-	
35
+
37 36
 	// EventEncodeStringComplete is fun
38 37
 	EventEncodeStringComplete
39 38
 )

+ 32
- 21
decoder.go Целия файл

@@ -11,7 +11,8 @@
11 11
 
12 12
 package gortty
13 13
 
14
-import(
14
+import (
15
+	"context"
15 16
 	"unicode/utf8"
16 17
 )
17 18
 
@@ -21,7 +22,7 @@ type Decoder struct {
21 22
 	input   chan byte
22 23
 	output  chan rune
23 24
 	charset *CodeSet
24
-	usos bool
25
+	usos    bool
25 26
 }
26 27
 
27 28
 // UnshiftOnSpace sets how the decoder behaves when a space is received while in figures
@@ -31,34 +32,44 @@ func (d *Decoder) UnshiftOnSpace(usos bool) {
31 32
 }
32 33
 
33 34
 // NewDecoder creates a new decoder.
34
-func NewDecoder(input chan byte, output chan rune, charset *CodeSet) *Decoder {
35
+func NewDecoder(ctx context.Context, input chan byte, output chan rune, charset *CodeSet) *Decoder {
35 36
 	d := new(Decoder)
36 37
 	d.input = input
37
-	d.output = output
38
+	if output == nil {
39
+		d.output = make(chan rune)
40
+	} else {
41
+		d.output = output
42
+	}
38 43
 	d.charset = charset
39 44
 
40
-	go d.run()
45
+	go d.run(ctx)
41 46
 	return d
42 47
 }
43 48
 
44
-func (d *Decoder) run() {
49
+func (d *Decoder) run(ctx context.Context) {
45 50
 	shift := shiftLetter
46
-	for c := range d.input {
47
-		switch c {
48
-		case d.charset.UnshiftCode:
49
-			shift = shiftLetter
50
-			//TODO: Trigger EventDemodCase
51
-		case d.charset.ShiftCode:
52
-			shift = shiftFigure
53
-			//TODO: Trigger EventDemodCase
54
-		default:
55
-			r := d.charset.toRune(shift, c)
56
-			if d.usos && shift == shiftFigure && r == ' ' {
51
+
52
+	for {
53
+		select {
54
+		case <-ctx.Done():
55
+			return
56
+		case c := <-d.input:
57
+			switch c {
58
+			case d.charset.UnshiftCode:
57 59
 				shift = shiftLetter
58
-				//TODO: Trigger EventUnshiftOnSpace
60
+				//TODO: Trigger EventDemodCase
61
+			case d.charset.ShiftCode:
62
+				shift = shiftFigure
63
+				//TODO: Trigger EventDemodCase
64
+			default:
65
+				r := d.charset.toRune(shift, c)
66
+				if d.usos && shift == shiftFigure && r == ' ' {
67
+					shift = shiftLetter
68
+					//TODO: Trigger EventUnshiftOnSpace
69
+				}
70
+				d.output <- d.charset.toRune(shift, c)
71
+				//TODO: Trigger EventDemodChar
59 72
 			}
60
-			d.output <- d.charset.toRune(shift, c)
61
-			//TODO: Trigger EventDemodChar
62 73
 		}
63 74
 	}
64 75
 }
@@ -67,4 +78,4 @@ func (d *Decoder) Read(p []byte) (n int, err error) {
67 78
 	r := <-d.output
68 79
 	l := utf8.EncodeRune(p, r)
69 80
 	return l, nil
70
-}
81
+}

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

@@ -13,6 +13,7 @@ package gortty
13 13
 
14 14
 import (
15 15
 	//"fmt"
16
+	"context"
16 17
 	"math"
17 18
 )
18 19
 
@@ -46,9 +47,8 @@ type Demodulator struct {
46 47
 	}
47 48
 }
48 49
 
49
-
50 50
 // NewDemodulator is fun
51
-func NewDemodulator(s *ModemSettings, input chan int16, output chan byte) *Demodulator {
51
+func NewDemodulator(ctx context.Context, s *ModemSettings, input chan int16, output chan byte) *Demodulator {
52 52
 
53 53
 	d := new(Demodulator)
54 54
 	d.input = input
@@ -71,7 +71,7 @@ func NewDemodulator(s *ModemSettings, input chan int16, output chan byte) *Demod
71 71
 	d.dsp = newDsp(d.settings.OneFreq, d.settings.ZeroFreq, d.samplerate)
72 72
 
73 73
 	d.autobaud.estimate = d.settings.Baud
74
-	go d.run()
74
+	go d.run(ctx)
75 75
 	return d
76 76
 
77 77
 }
@@ -80,7 +80,7 @@ func (d *Demodulator) Inhibit(i bool) {
80 80
 	d.inhibitOutput = i
81 81
 }
82 82
 
83
-func (d *Demodulator) run() {
83
+func (d *Demodulator) run(ctx context.Context) {
84 84
 
85 85
 	var nextState int
86 86
 	var sample int  // down counter for sampling instant
@@ -97,151 +97,156 @@ func (d *Demodulator) run() {
97 97
 		autobaudState = autobaudStateDisabled
98 98
 	}
99 99
 	count := 0
100
-	for sam := range d.input {
101
-		count++
102
-		if count%d.div > 0 {
100
+	for {
101
+		select {
102
+		case <-ctx.Done():
103
+			return
104
+		case sam := <-d.input:
103 105
 
104
-			continue
105
-		}
106
-		//	d.dsp.demod(d.l.update(float64(sam)))
107
-		d.dsp.demod(float64(sam))
108
-
109
-		if d.sigOut {
110
-			m0, m1, m2 := d.dsp.level()
111
-			d.sigOutput <- [3]float64{m0, m1, m2}
112
-
113
-		} else {
114
-			//fmt.Println(d.dsp.level())
115
-			nextState = state
116
-
117
-			switch state {
118
-			case demodStateWaitStart:
119
-				// The demodulator sits in this state when it
120
-				// is waiting for a start bit.  When a
121
-				// "0" start-bit is detected that is
122
-				// above a minimum threshold it transitions
123
-				// out of this state.
124
-
125
-				if d.dsp.space() {
126
-					sample = int(float64(d.samplerate) / d.settings.Baud * 1.5)
127
-					baudot = 0
128
-					bit = 0
129
-					nextState = demodStateSample
130
-					//	fmt.Println("demodStateSample")
106
+			count++
107
+			if count%d.div > 0 {
131 108
 
132
-				}
109
+				continue
110
+			}
111
+			//	d.dsp.demod(d.l.update(float64(sam)))
112
+			d.dsp.demod(float64(sam))
113
+
114
+			if d.sigOut {
115
+				m0, m1, m2 := d.dsp.level()
116
+				d.sigOutput <- [3]float64{m0, m1, m2}
117
+
118
+			} else {
119
+				//fmt.Println(d.dsp.level())
120
+				nextState = state
121
+
122
+				switch state {
123
+				case demodStateWaitStart:
124
+					// The demodulator sits in this state when it
125
+					// is waiting for a start bit.  When a
126
+					// "0" start-bit is detected that is
127
+					// above a minimum threshold it transitions
128
+					// out of this state.
129
+
130
+					if d.dsp.space() {
131
+						sample = int(float64(d.samplerate) / d.settings.Baud * 1.5)
132
+						baudot = 0
133
+						bit = 0
134
+						nextState = demodStateSample
135
+						//	fmt.Println("demodStateSample")
133 136
 
134
-			case demodStateSample:
137
+					}
138
+
139
+				case demodStateSample:
135 140
 
136
-				sample--
137
-				if sample == 0 {
138
-					if d.dsp.mark() {
139
-						baudot |= (1 << uint(bit))
141
+					sample--
142
+					if sample == 0 {
143
+						if d.dsp.mark() {
144
+							baudot |= (1 << uint(bit))
145
+						}
146
+						bit++
147
+						if bit == d.settings.DataBits {
148
+							// complete baudot char has been received
149
+							sample = int(float64(d.samplerate) / d.settings.Baud)
150
+							nextState = demodStateWaitStop
151
+							if !d.inhibitOutput {
152
+								d.output <- baudot
153
+							}
154
+						} else {
155
+							/* sample next bit 1 bit-period later */
156
+							sample = int(float64(d.samplerate) / d.settings.Baud)
157
+						}
140 158
 					}
141
-					bit++
142
-					if bit == d.settings.DataBits {
143
-						// complete baudot char has been received
144
-						sample = int(float64(d.samplerate) / d.settings.Baud)
145
-						nextState = demodStateWaitStop
146
-						if !d.inhibitOutput {
147
-							d.output <- baudot
159
+
160
+					/* check for d.sampleRateK signal drop out */
161
+
162
+					if !d.dsp.carrier() {
163
+						d.minSigTimeout++
164
+
165
+						/* If signal drops out for approx one bit period
166
+						   assume we have lost d.sampleRateK signal */
167
+						if d.minSigTimeout > d.samplesPerBit {
168
+							nextState = demodStateWaitStart
169
+							//fmt.Println("demodStateWaitStart")
170
+
148 171
 						}
149 172
 					} else {
150
-						/* sample next bit 1 bit-period later */
151
-						sample = int(float64(d.samplerate) / d.settings.Baud)
173
+						d.minSigTimeout = 0
152 174
 					}
153
-				}
154 175
 
155
-				/* check for d.sampleRateK signal drop out */
176
+				case demodStateWaitStop:
156 177
 
157
-				if !d.dsp.carrier() {
158
-					d.minSigTimeout++
178
+					/* wait until we are in the middle of stop bit */
159 179
 
160
-					/* If signal drops out for approx one bit period
161
-					   assume we have lost d.sampleRateK signal */
162
-					if d.minSigTimeout > d.samplesPerBit {
180
+					sample--
181
+					if sample == 0 {
163 182
 						nextState = demodStateWaitStart
164 183
 						//fmt.Println("demodStateWaitStart")
165
-
166 184
 					}
167
-				} else {
168
-					d.minSigTimeout = 0
169
-				}
170 185
 
171
-			case demodStateWaitStop:
172
-
173
-				/* wait until we are in the middle of stop bit */
174
-
175
-				sample--
176
-				if sample == 0 {
177
-					nextState = demodStateWaitStart
178
-					//fmt.Println("demodStateWaitStart")
179 186
 				}
180 187
 
181
-			}
182
-
183
-			state = nextState
188
+				state = nextState
184 189
 
185
-			// auto baud detection state machine
186
-			// This deviates quite a bit from the original C implementation.
187
-			// Starts a counter at falling edge that marks the
188
-			// beginning of the start bit.  Measures time to
189
-			// next rising edge.  This time is presumably from
190
-			// 1 to 6 bit periods long, so the remainder from divinging
191
-			// the time span by a numbe of bit periods for different baud
192
-			// rates is accumulated. The baud rate that divides the most
193
-			// evenly into all measured bit periods is the estimated baud rate.
190
+				// auto baud detection state machine
191
+				// This deviates quite a bit from the original C implementation.
192
+				// Starts a counter at falling edge that marks the
193
+				// beginning of the start bit.  Measures time to
194
+				// next rising edge.  This time is presumably from
195
+				// 1 to 6 bit periods long, so the remainder from divinging
196
+				// the time span by a numbe of bit periods for different baud
197
+				// rates is accumulated. The baud rate that divides the most
198
+				// evenly into all measured bit periods is the estimated baud rate.
194 199
 
195
-			nextState = autobaudState
200
+				nextState = autobaudState
196 201
 
197
-			switch autobaudState {
198
-			case autobaudStateWaitStart:
199
-				/* start bit kicks off this state machine */
200
-				if state == demodStateSample {
201
-					nextState = autobaudStateWaitZeroX
202
-					zeroX = 0
203
-					remainders = map[int]int{45: 0, 47: 0, 50: 0, 75: 0}
202
+				switch autobaudState {
203
+				case autobaudStateWaitStart:
204
+					/* start bit kicks off this state machine */
205
+					if state == demodStateSample {
206
+						nextState = autobaudStateWaitZeroX
207
+						zeroX = 0
208
+						remainders = map[int]int{45: 0, 47: 0, 50: 0, 75: 0}
204 209
 
205
-				}
206
-			case autobaudStateWaitZeroX:
210
+					}
211
+				case autobaudStateWaitZeroX:
207 212
 
208
-				zeroX++
213
+					zeroX++
209 214
 
210
-				if d.dsp.edgeDetected() {
215
+					if d.dsp.edgeDetected() {
211 216
 
212
-					estimate := 45 // pick one as a starting point
213
-					minRemainder := remainders[estimate]
217
+						estimate := 45 // pick one as a starting point
218
+						minRemainder := remainders[estimate]
214 219
 
215
-					for b, t := range remainders {
216
-						q := t + (zeroX % (d.samplerate / b))
217
-						remainders[b] = q
218
-						if q < minRemainder {
219
-							minRemainder = q
220
-							estimate = b
220
+						for b, t := range remainders {
221
+							q := t + (zeroX % (d.samplerate / b))
222
+							remainders[b] = q
223
+							if q < minRemainder {
224
+								minRemainder = q
225
+								estimate = b
226
+							}
221 227
 						}
222
-					}
223 228
 
224
-					d.autobaud.estimate = float64(estimate)
225
-					nextState = autobaudStateWaitStop
226
-				}
229
+						d.autobaud.estimate = float64(estimate)
230
+						nextState = autobaudStateWaitStop
231
+					}
227 232
 
228
-				// reset autobaud state machine, e.g. if signal drops out before
229
-				// we get a zero crossing
233
+					// reset autobaud state machine, e.g. if signal drops out before
234
+					// we get a zero crossing
230 235
 
231
-				if state != demodStateSample {
232
-					nextState = autobaudStateWaitStop
233
-				}
236
+					if state != demodStateSample {
237
+						nextState = autobaudStateWaitStop
238
+					}
234 239
 
235
-			case autobaudStateWaitStop:
236
-				if state == demodStateWaitStop {
237
-					nextState = autobaudStateWaitStart
238
-				}
240
+				case autobaudStateWaitStop:
241
+					if state == demodStateWaitStop {
242
+						nextState = autobaudStateWaitStart
243
+					}
239 244
 
240
-			case autobaudStateDisabled:
245
+				case autobaudStateDisabled:
241 246
 
247
+				}
248
+				autobaudState = nextState
242 249
 			}
243
-			autobaudState = nextState
244 250
 		}
245 251
 	}
246
-
247 252
 }

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

@@ -77,7 +77,7 @@ func (d *dsp) lpfVal() float64 {
77 77
 }
78 78
 
79 79
 func (d *dsp) threshVal() float64 {
80
-	return d.demTotal*d.minThresh
80
+	return d.demTotal * d.minThresh
81 81
 }
82 82
 
83 83
 func (d *dsp) level() (float64, float64, float64) {

+ 104
- 95
encoder.go Целия файл

@@ -12,8 +12,9 @@
12 12
 package gortty
13 13
 
14 14
 import (
15
-	"fmt"
16 15
 	"bytes"
16
+	"context"
17
+	"fmt"
17 18
 )
18 19
 
19 20
 // Encoder is fun
@@ -30,15 +31,19 @@ type Encoder struct {
30 31
 }
31 32
 
32 33
 // NewEncoder is fun
33
-func NewEncoder(input chan rune, output chan byte, charset *CodeSet) *Encoder {
34
+func NewEncoder(ctx context.Context, input chan rune, output chan byte, charset *CodeSet) *Encoder {
34 35
 	e := new(Encoder)
35
-	e.input = input
36
+	if input == nil {
37
+		e.input = make(chan rune)
38
+	} else {
39
+		e.input = input
40
+	}
36 41
 	e.output = output
37 42
 	e.charset = charset
38 43
 	e.crlfThresh = 60
39 44
 	e.crlfForceThresh = 72
40 45
 	e.shiftThresh = 70
41
-	go e.run()
46
+	go e.run(ctx)
42 47
 	return e
43 48
 }
44 49
 
@@ -60,113 +65,118 @@ func (e *Encoder) SetCallback(callback Callback) {
60 65
 	e.callback = callback
61 66
 }
62 67
 
63
-func (e *Encoder) run() {
68
+func (e *Encoder) run(ctx context.Context) {
64 69
 
65 70
 	var modCase int           // current Letters/Figures state
66 71
 	var charsWithoutCRLF int  // number of chars we have handled without inserting a CR-LF
67 72
 	var charsWithoutShift int // number of chars we have handled without seeing a shift
68
-	for r := range e.input {
69
-		c := e.charset.toCode(r)
70
-
71
-		/*this rune can either be rendered as a Figures character,
72
-		  a Letters character or WHITESPACE which can be rendered by
73
-		  either character set*/
74
-		if r == '\r' || r == '\n' {
75
-			charsWithoutCRLF = 0
76
-		}
77
-
78
-		/*see if we need to first insert a case character*/
79
-		if c.shift == shiftWhitespace {
80
-			/*we have white space, we don't need to insert
81
-			a Letters/Figures case character.  We will insert
82
-			a case character when we transition from white
83
-			space to some other case.  The only exception to this
84
-			is when we are just starting to transmit, in which case
85
-			we must send something, so we'll choose the more common
86
-			Letters case*/
87
-			if modCase == shiftUnknown {
88
-				e.output <- e.charset.unshift()
73
+	for {
74
+		select {
75
+		case <-ctx.Done():
76
+			return
77
+		case r := <-e.input:
78
+			c := e.charset.toCode(r)
79
+
80
+			/*this rune can either be rendered as a Figures character,
81
+			  a Letters character or WHITESPACE which can be rendered by
82
+			  either character set*/
83
+			if r == '\r' || r == '\n' {
84
+				charsWithoutCRLF = 0
89 85
 			}
90 86
 
91
-			modCase = shiftWhitespace
92
-		} else if c.shift != modCase {
93
-			/*case is either Letters or Figures and doesn't match
94
-			  our modulator's current state, so add a new case char
95
-			  here*/
96
-			if c.shift == shiftLetter {
97
-				e.output <- e.charset.unshift()
98
-			} else {
99
-				e.output <- e.charset.shift()
100
-			}
87
+			/*see if we need to first insert a case character*/
88
+			if c.shift == shiftWhitespace {
89
+				/*we have white space, we don't need to insert
90
+				a Letters/Figures case character.  We will insert
91
+				a case character when we transition from white
92
+				space to some other case.  The only exception to this
93
+				is when we are just starting to transmit, in which case
94
+				we must send something, so we'll choose the more common
95
+				Letters case*/
96
+				if modCase == shiftUnknown {
97
+					e.output <- e.charset.unshift()
98
+				}
101 99
 
102
-			/*and save our current state*/
103
-			modCase = c.shift
100
+				modCase = shiftWhitespace
101
+			} else if c.shift != modCase {
102
+				/*case is either Letters or Figures and doesn't match
103
+				  our modulator's current state, so add a new case char
104
+				  here*/
105
+				if c.shift == shiftLetter {
106
+					e.output <- e.charset.unshift()
107
+				} else {
108
+					e.output <- e.charset.shift()
109
+				}
104 110
 
105
-			/*and update our counter*/
106
-			//fmt.Println("charsWithoutShift", charsWithoutShift)
107
-			charsWithoutShift = 0
108
-		}
111
+				/*and save our current state*/
112
+				modCase = c.shift
109 113
 
110
-		/*we need to perform a little logic here to check for some
111
-		 * boundry conditions in the protocol*/
112
-		/* We need to ensure that we re-send the current case character
113
-		   at least every 72 characters. */
114
-		if charsWithoutShift > e.shiftThresh {
115
-			if modCase == shiftWhitespace {
116
-				e.output <- e.charset.unshift() /*doesn't matter which, so how 'bout LETR*/
117
-			} else if modCase == shiftLetter {
118
-				e.output <- e.charset.unshift()
119
-			} else {
120
-				e.output <- e.charset.shift()
114
+				/*and update our counter*/
115
+				//fmt.Println("charsWithoutShift", charsWithoutShift)
116
+				charsWithoutShift = 0
121 117
 			}
122
-			//fmt.Println("charsWithoutShift", charsWithoutShift)
123 118
 
124
-			charsWithoutShift = 0
125
-		}
126
-
127
-		/* we need to ensure that we send a carriage return/linefeed
128
-		 * at least every 72 characters, but we start looking for a
129
-		 * space character starting at 60 characters */
130
-		if !e.autoWrap {
131
-			/*we just insert our regular character*/
132
-			e.output <- c.toByte()
133
-
134
-			if e.autoCRLF {
135
-				if r == '\n' {
136
-					e.output <- e.charset.toByte('\r')
137
-				} else if r == '\r' {
138
-					e.output <- e.charset.toByte('\n')
119
+			/*we need to perform a little logic here to check for some
120
+			 * boundry conditions in the protocol*/
121
+			/* We need to ensure that we re-send the current case character
122
+			   at least every 72 characters. */
123
+			if charsWithoutShift > e.shiftThresh {
124
+				if modCase == shiftWhitespace {
125
+					e.output <- e.charset.unshift() /*doesn't matter which, so how 'bout LETR*/
126
+				} else if modCase == shiftLetter {
127
+					e.output <- e.charset.unshift()
128
+				} else {
129
+					e.output <- e.charset.shift()
139 130
 				}
131
+				//fmt.Println("charsWithoutShift", charsWithoutShift)
132
+
133
+				charsWithoutShift = 0
140 134
 			}
141 135
 
142
-		} else if (charsWithoutCRLF > e.crlfThresh) && (modCase == shiftWhitespace) {
143
-			/*insert the cr-lf instead of the space character*/
144
-			e.output <- e.charset.toByte('\r')
145
-			e.output <- e.charset.toByte('\n')
146
-			charsWithoutCRLF = 0
147
-		} else if charsWithoutCRLF > e.crlfForceThresh {
148
-			/*insert the cr-lf followed by the character*/
149
-			e.output <- e.charset.toByte('\r')
150
-			e.output <- e.charset.toByte('\n')
151
-			e.output <- c.toByte()
152
-			charsWithoutCRLF = 0
153
-		} else {
154
-			/*we just insert our regular character*/
155
-			e.output <- c.toByte()
156
-
157
-			if e.autoCRLF {
158
-				if r == '\n' {
159
-					e.output <- e.charset.toByte('\r')
160
-				} else if r == '\r' {
161
-					e.output <- e.charset.toByte('\n')
136
+			/* we need to ensure that we send a carriage return/linefeed
137
+			 * at least every 72 characters, but we start looking for a
138
+			 * space character starting at 60 characters */
139
+			if !e.autoWrap {
140
+				/*we just insert our regular character*/
141
+				e.output <- c.toByte()
142
+
143
+				if e.autoCRLF {
144
+					if r == '\n' {
145
+						e.output <- e.charset.toByte('\r')
146
+					} else if r == '\r' {
147
+						e.output <- e.charset.toByte('\n')
148
+					}
162 149
 				}
163
-			}
164 150
 
165
-		}
151
+			} else if (charsWithoutCRLF > e.crlfThresh) && (modCase == shiftWhitespace) {
152
+				/*insert the cr-lf instead of the space character*/
153
+				e.output <- e.charset.toByte('\r')
154
+				e.output <- e.charset.toByte('\n')
155
+				charsWithoutCRLF = 0
156
+			} else if charsWithoutCRLF > e.crlfForceThresh {
157
+				/*insert the cr-lf followed by the character*/
158
+				e.output <- e.charset.toByte('\r')
159
+				e.output <- e.charset.toByte('\n')
160
+				e.output <- c.toByte()
161
+				charsWithoutCRLF = 0
162
+			} else {
163
+				/*we just insert our regular character*/
164
+				e.output <- c.toByte()
165
+
166
+				if e.autoCRLF {
167
+					if r == '\n' {
168
+						e.output <- e.charset.toByte('\r')
169
+					} else if r == '\r' {
170
+						e.output <- e.charset.toByte('\n')
171
+					}
172
+				}
166 173
 
167
-		charsWithoutShift++
168
-		charsWithoutCRLF++
174
+			}
175
+
176
+			charsWithoutShift++
177
+			charsWithoutCRLF++
169 178
 
179
+		}
170 180
 	}
171 181
 
172 182
 }
@@ -176,10 +186,9 @@ func (e *Encoder) Write(p []byte) (n int, err error) {
176 186
 	runes := bytes.Runes(p)
177 187
 
178 188
 	fmt.Print(string(runes))
179
-	
189
+
180 190
 	for _, r := range runes {
181 191
 		e.input <- r
182 192
 	}
183 193
 	return len(p), nil
184 194
 }
185
-

+ 12
- 7
modulator.go Целия файл

@@ -11,7 +11,10 @@
11 11
 
12 12
 package gortty
13 13
 
14
-import "math"
14
+import (
15
+	"context"
16
+	"math"
17
+)
15 18
 
16 19
 const (
17 20
 	modStateIdle = iota
@@ -38,12 +41,12 @@ type Modulator struct {
38 41
 	wZeroQ16        uint16  // scaled zero freq 0..2math.Pi -> 0..65536
39 42
 	sinLutQ15       []int16 // sine lookup table
40 43
 	callback        Callback
41
-	
44
+
42 45
 	idleDelay int
43 46
 }
44 47
 
45 48
 // NewModulator is fun
46
-func NewModulator(s *ModemSettings, input chan byte, output chan int16) *Modulator {
49
+func NewModulator(ctx context.Context, s *ModemSettings, input chan byte, output chan int16) *Modulator {
47 50
 
48 51
 	m := new(Modulator)
49 52
 
@@ -66,7 +69,7 @@ func NewModulator(s *ModemSettings, input chan byte, output chan int16) *Modulat
66 69
 	for i := 0; i < 16384; i++ {
67 70
 		m.sinLutQ15[i] = int16(32767 * math.Sin(2.0*math.Pi*float64(i)/float64(16384)))
68 71
 	}
69
-	go m.run()
72
+	go m.run(ctx)
70 73
 	return m
71 74
 }
72 75
 
@@ -75,7 +78,7 @@ func (m *Modulator) SetCallback(callback Callback) {
75 78
 	m.callback = callback
76 79
 }
77 80
 
78
-// SetIdleDelay sets how long (in samples) that the modulator must be in the ready state 
81
+// SetIdleDelay sets how long (in samples) that the modulator must be in the ready state
79 82
 // before switching to the idle state
80 83
 func (m *Modulator) SetIdleDelay(d int) {
81 84
 	m.idleDelay = d
@@ -105,7 +108,7 @@ func (m *Modulator) SetAmplitude(amplitude int16) {
105 108
 	m.amplitude = amplitude
106 109
 }
107 110
 
108
-func (m *Modulator) run() {
111
+func (m *Modulator) run(ctx context.Context) {
109 112
 
110 113
 	var modState int  // modulator state machine
111 114
 	var nextState int // next state of state machine
@@ -122,13 +125,15 @@ func (m *Modulator) run() {
122 125
 			fallthrough
123 126
 		case modStateIdle:
124 127
 			select {
128
+			case <-ctx.Done():
129
+				return
125 130
 			case c = <-m.input:
126 131
 				modSample = 0
127 132
 				nextState = modStateStart
128 133
 				idleSamples = 0
129 134
 				if m.callback != nil {
130 135
 					m.callback(EventStartBit, 0)
131
-					if (modState == modStateIdle) {
136
+					if modState == modStateIdle {
132 137
 						m.callback(EventActive, 0)
133 138
 					}
134 139
 				}

+ 45
- 45
rawdemodulator.go Целия файл

@@ -12,29 +12,28 @@
12 12
 package gortty
13 13
 
14 14
 import (
15
+	"context"
15 16
 	"math"
16 17
 )
17 18
 
18
-
19
-
20 19
 // RawDemodulator is fun
21 20
 type RawDemodulator struct {
22
-	input         chan int16
23
-	output        chan DCSignal  
24
-	dsp           *dsp
25
-	settings      *ModemSettings
26
-	sigOut        bool
27
-	samplerate    int
28
-	div           int
21
+	input      chan int16
22
+	output     chan DCSignal
23
+	dsp        *dsp
24
+	settings   *ModemSettings
25
+	sigOut     bool
26
+	samplerate int
27
+	div        int
29 28
 }
30 29
 
31 30
 // NewRawDemodulator is fun
32
-func NewRawDemodulator(s *ModemSettings, input chan int16, output chan DCSignal) *RawDemodulator {
31
+func NewRawDemodulator(ctx context.Context, s *ModemSettings, input chan int16, output chan DCSignal) *RawDemodulator {
33 32
 
34 33
 	d := new(RawDemodulator)
35 34
 	d.input = input
36 35
 	d.output = output
37
-	
36
+
38 37
 	d.settings = s
39 38
 
40 39
 	// the bandpass filter is picky about the sample rate, and the output is perfectly
@@ -49,47 +48,48 @@ func NewRawDemodulator(s *ModemSettings, input chan int16, output chan DCSignal)
49 48
 	}
50 49
 
51 50
 	d.samplerate = d.settings.SampleRate / d.div
52
-	
51
+
53 52
 	d.dsp = newDsp(d.settings.OneFreq, d.settings.ZeroFreq, d.samplerate)
54 53
 
55
-	go d.run()
54
+	go d.run(ctx)
56 55
 	return d
57 56
 }
58 57
 
59
-func (d *RawDemodulator) run() {
58
+func (d *RawDemodulator) run(ctx context.Context) {
60 59
 
61 60
 	count := 0
62
-	for sam := range d.input {
63
-	
64
-		// sample rate reduction
65
-		count++
66
-		if count%d.div > 0 {
67
-			continue
61
+	for {
62
+		select {
63
+		case <-ctx.Done():
64
+			return
65
+		case sam := <-d.input:
66
+			// sample rate reduction
67
+			count++
68
+			if count%d.div > 0 {
69
+				continue
70
+			}
71
+
72
+			d.dsp.demod(float64(sam))
73
+
74
+			var signal int8
75
+			var quality int8
76
+
77
+			if math.Abs(d.dsp.demLpf) > d.dsp.demTotal*d.dsp.minThresh && d.dsp.demTotal*d.dsp.minThresh != 0 {
78
+				quality = 1
79
+			} else {
80
+				quality = 0
81
+			}
82
+
83
+			if d.dsp.demLpf-d.dsp.minThresh*d.dsp.demTotal > 0 {
84
+				signal = 1 * quality
85
+			} else {
86
+				signal = -1 * quality
87
+			}
88
+
89
+			d.output <- DCSignal{
90
+				Signal:  signal,
91
+				Quality: quality,
92
+			}
68 93
 		}
69
-
70
-		d.dsp.demod(float64(sam))
71
-
72
-		var signal int8
73
-		var quality int8
74
-		
75
-
76
-		if math.Abs(d.dsp.demLpf) > d.dsp.demTotal*d.dsp.minThresh && d.dsp.demTotal*d.dsp.minThresh != 0 {
77
-			quality = 1
78
-		} else {
79
-			quality = 0
80
-		}
81
-
82
-		if d.dsp.demLpf - d.dsp.minThresh*d.dsp.demTotal > 0 {
83
-			signal = 1*quality
84
-		} else {
85
-			signal = -1*quality
86
-		}	
87
-				
88
-		d.output <-  DCSignal {
89
-			Signal: signal,
90
-			Quality: quality,
91
-		}
92
-		
93 94
 	}
94
-
95 95
 }

+ 18
- 14
rawmodulator.go Целия файл

@@ -11,11 +11,13 @@
11 11
 
12 12
 package gortty
13 13
 
14
-import "math"
15
-//import "fmt"
14
+import (
15
+	"context"
16
+	"math"
17
+)
16 18
 
17 19
 type DCSignal struct {
18
-	Signal int8
20
+	Signal  int8
19 21
 	Quality int8
20 22
 }
21 23
 
@@ -26,16 +28,16 @@ type RawModulator struct {
26 28
 
27 29
 	settings *ModemSettings
28 30
 
29
-	phaseQ16        uint16  // phase acc 0..2math.Pi -> 0..65536
30
-	amplitude       int16   // peak RawModulator amplitude for one tone
31
-	wOneQ16         uint16  // scaled one freq 0..2math.Pi -> 0..65536
32
-	wZeroQ16        uint16  // scaled zero freq 0..2math.Pi -> 0..65536
33
-	sinLutQ15       []int16 // sine lookup table
34
-	callback        Callback
31
+	phaseQ16  uint16  // phase acc 0..2math.Pi -> 0..65536
32
+	amplitude int16   // peak RawModulator amplitude for one tone
33
+	wOneQ16   uint16  // scaled one freq 0..2math.Pi -> 0..65536
34
+	wZeroQ16  uint16  // scaled zero freq 0..2math.Pi -> 0..65536
35
+	sinLutQ15 []int16 // sine lookup table
36
+	callback  Callback
35 37
 }
36 38
 
37 39
 // NewRawModulator is fun
38
-func NewRawModulator(s *ModemSettings, input chan DCSignal, output chan int16) *RawModulator {
40
+func NewRawModulator(ctx context.Context, s *ModemSettings, input chan DCSignal, output chan int16) *RawModulator {
39 41
 
40 42
 	m := new(RawModulator)
41 43
 
@@ -55,7 +57,7 @@ func NewRawModulator(s *ModemSettings, input chan DCSignal, output chan int16) *
55 57
 	for i := 0; i < 16384; i++ {
56 58
 		m.sinLutQ15[i] = int16(32767 * math.Sin(2.0*math.Pi*float64(i)/float64(16384)))
57 59
 	}
58
-	go m.run()
60
+	go m.run(ctx)
59 61
 	return m
60 62
 }
61 63
 
@@ -68,11 +70,13 @@ func (m *RawModulator) SetAmplitude(amplitude int16) {
68 70
 	m.amplitude = amplitude
69 71
 }
70 72
 
71
-func (m *RawModulator) run() {
73
+func (m *RawModulator) run(ctx context.Context) {
72 74
 
73 75
 	var lut uint16
74 76
 	for {
75 77
 		select {
78
+		case <-ctx.Done():
79
+			return
76 80
 		case s := <-m.input:
77 81
 			if s.Signal > 0 {
78 82
 				lut = m.wOneQ16
@@ -86,7 +90,7 @@ func (m *RawModulator) run() {
86 90
 				m.output <- 0
87 91
 			}
88 92
 		}
89
-		
90
-	}			
93
+
94
+	}
91 95
 
92 96
 }