Browse Source

more ugly dev committing

Keelan Lightfoot 7 years ago
parent
commit
2c95547ada
7 changed files with 598 additions and 336 deletions
  1. 2
    0
      common.go
  2. 183
    216
      demodulator.go
  3. 110
    49
      dsp.go
  4. 118
    0
      examples/audioin/main.go
  5. 145
    0
      examples/demtest.go
  6. 23
    55
      examples/main.go
  7. 17
    16
      modulator.go

+ 2
- 0
common.go View File

@@ -37,6 +37,8 @@ type ModemSettings struct {
37 37
 	StopBits   float64
38 38
 	DataBits   int
39 39
 	SampleRate int
40
+	OneFreq    int
41
+	ZeroFreq   int
40 42
 }
41 43
 
42 44
 func multq15(x, y int16) int16 {

+ 183
- 216
demodulator.go View File

@@ -11,7 +11,10 @@
11 11
 
12 12
 package openbaudot
13 13
 
14
-import "math"
14
+import (
15
+	"fmt"
16
+	"math"
17
+)
15 18
 
16 19
 const (
17 20
 	demodStateWaitStart = iota
@@ -28,282 +31,246 @@ const (
28 31
 type Demodulator struct {
29 32
 	input         chan int16
30 33
 	output        chan byte
31
-	state         int     // current demod state machine state
32
-	minSigTimeout int     // minimum signal time out counter
33
-	baud          float64 // demod baud rate
34
-	bit           int     // current bit being received
35
-	sample        int     // down counter for sampling instant
36
-	//	lettersFigures int     // current Letters/Figures mode
37
-	baudot        byte // demodulator baud rate
34
+	sigOutput     chan [3]float64
35
+	minSigTimeout int // minimum signal time out counter
38 36
 	samplesPerBit int
39 37
 	dsp           *dsp
40
-	dataBits      int
41
-	sampleRate    int
38
+	settings      *ModemSettings
39
+	sigOut        bool
40
+	samplerate    int
41
+	div           int
42 42
 	autobaud      struct {
43 43
 		enabled  bool    // audio baud detection enable/disable flag
44
-		state    int     // auto buad detection state mach state
45
-		zeroX    int     // samples to zero crossing
46 44
 		estimate float64 // autobaud estimate
47 45
 	}
48 46
 }
49 47
 
50
-// NewDemodulator is fun
51
-func NewDemodulator(s ModemSettings, input chan int16, output chan byte) *Demodulator {
48
+// NewDumbDemodulator is fun
49
+func NewDumbDemodulator(s *ModemSettings, input chan int16, sigOutput chan [3]float64) *Demodulator {
52 50
 
53 51
 	d := new(Demodulator)
54 52
 	d.input = input
55
-	d.output = output
56
-	d.state = demodStateWaitStart
57
-	//d.lettersFigures = Letters
58
-	d.baud = s.Baud
59
-	d.dataBits = s.DataBits
60
-	d.sampleRate = s.SampleRate
61
-	d.samplesPerBit = int(float64(d.sampleRate) / d.baud)
62
-
63
-	wOne, wZero := calculateW(2125, 2295, d.sampleRate)
64
-
65
-	lfpOrder := 20
66
-	beta := 0.95 //.95
67
-
68
-	d.dsp = &dsp{
69
-		c1:         int16((2.0 * math.Cos(wOne) * beta) * 32768),
70
-		c2:         int16(beta * beta * 32768),
71
-		c3:         int16((2.0 * math.Cos(wZero) * beta) * 32768),
72
-		demLpfIn:   make([]int16, lfpOrder),
73
-		demTotalIn: make([]int16, lfpOrder),
74
-		lpfOrder:   lfpOrder,
75
-	}
53
+	d.sigOutput = sigOutput
76 54
 
77
-	d.autobaud.estimate = d.baud
78
-	if d.autobaud.enabled {
79
-		d.autobaud.state = autobaudStateWaitStart
80
-	} else {
81
-		d.autobaud.state = autobaudStateDisabled
82
-	}
83
-	go d.run()
84
-	return d
55
+	d.settings = s
85 56
 
86
-}
57
+	// the bandpass filter is picky about the sample rate, and the output is perfectly
58
+	// balanced when the sample rate is 3x the center frequency, so lets divide the sample
59
+	// rate down to as close to that as we can get.
87 60
 
88
-func (d *Demodulator) run() {
61
+	centerFrequency := math.Abs(float64(d.settings.OneFreq)+float64(d.settings.ZeroFreq)) / 2 * 3
89 62
 
90
-	var nextState int
91
-	var edge, dist int
63
+	d.div = int(math.Floor(float64(d.settings.SampleRate) / float64(centerFrequency)))
64
+	if d.div == 0 {
65
+		d.div = 1
66
+	}
92 67
 
93
-	var demDist75 [2]int // previous 75 baud distance measures
94
-	var demDist50 [2]int // previous 50 baud distance measures
95
-	var demDist47 [2]int // previous 47 baud distance measures
96
-	var demDist45 [2]int // previous 45 baud distance measures
68
+	d.samplerate = d.settings.SampleRate / d.div
69
+	//d.div = div
70
+	d.samplesPerBit = int(float64(d.samplerate) / d.settings.Baud)
71
+	d.autobaud.enabled = false
97 72
 
98
-	/* main loop, process sample by sample */
73
+	d.dsp = newDsp(d.settings.OneFreq, d.settings.ZeroFreq, d.samplerate)
99 74
 
100
-	for sam := range d.input {
101
-		d.dsp.demod(sam)
102
-
103
-		nextState = d.state
104
-
105
-		switch d.state {
106
-		case demodStateWaitStart:
107
-			/*
108
-			   The demodulator sits in this state when it
109
-			   is waiting for a start bit.  When a d.sampleRateK
110
-			   "0" (1800 Hz) start-bit is detected that is
111
-			   above a minimum threshold it transitions
112
-			   out of this state.
113
-			*/
114
-			if d.dsp.signalPresent() {
115
-				d.sample = int(float64(d.sampleRate)/d.baud + float64(d.sampleRate)/(2*d.baud))
116
-				d.baudot = 0
117
-				d.bit = 0
118
-				nextState = demodStateSample
119
-			}
120
-		case demodStateSample:
75
+	d.autobaud.estimate = d.settings.Baud
76
+	d.sigOut = true
121 77
 
122
-			d.sample--
123
-			if d.sample == 0 {
124
-				if d.dsp.onePresent() {
125
-					d.baudot |= (1 << uint(d.bit))
126
-				}
127
-				d.bit++
78
+	go d.run()
79
+	return d
80
+}
128 81
 
129
-				if d.bit == d.dataBits {
82
+// NewDemodulator is fun
83
+func NewDemodulator(s *ModemSettings, input chan int16, output chan byte) *Demodulator {
130 84
 
131
-					/* complete baudot char has been received */
132
-					d.output <- d.baudot
133
-					//fmt.Printf("Demodulator done %d\n", d.baudot)
85
+	d := new(Demodulator)
86
+	d.input = input
87
+	d.output = output
134 88
 
135
-					d.sample = int(float64(d.sampleRate) / d.baud)
136
-					nextState = demodStateWaitStop
137
-				} else {
138
-					/* sample next bit 1 bit-period later */
139
-					d.sample = int(float64(d.sampleRate) / d.baud)
140
-				}
141
-			}
89
+	d.settings = s
142 90
 
143
-			/* check for d.sampleRateK signal drop out */
91
+	centerFrequency := math.Abs(float64(d.settings.OneFreq)+float64(d.settings.ZeroFreq)) / 2 * 3
144 92
 
145
-			//return d.demTotal != 0 && d.demLpf < -1*MinThresh*d.demTotal
93
+	d.div = int(math.Floor(float64(d.settings.SampleRate) / float64(centerFrequency)))
94
+	if d.div == 0 {
95
+		d.div = 1
96
+	}
146 97
 
147
-			if d.dsp.dropout() {
148
-				d.minSigTimeout++
98
+	d.samplerate = d.settings.SampleRate / d.div
149 99
 
150
-				/* If signal drops out for approx one bit period
151
-				   assume we have lost d.sampleRateK signal */
152
-				if d.minSigTimeout > d.samplesPerBit {
153
-					nextState = demodStateWaitStart
154
-				}
155
-			} else {
156
-				d.minSigTimeout = 0
157
-			}
100
+	d.samplesPerBit = int(float64(d.samplerate) / d.settings.Baud)
101
+	d.autobaud.enabled = false
158 102
 
159
-		case demodStateWaitStop:
103
+	d.dsp = newDsp(d.settings.OneFreq, d.settings.ZeroFreq, d.samplerate)
160 104
 
161
-			/* wait until we are in the middle of stop bit */
105
+	d.autobaud.estimate = d.settings.Baud
106
+	go d.run()
107
+	return d
162 108
 
163
-			d.sample--
164
-			if d.sample == 0 {
165
-				nextState = demodStateWaitStart
166
-			}
109
+}
167 110
 
168
-		}
111
+func (d *Demodulator) run() {
169 112
 
170
-		d.state = nextState
171
-
172
-		/* auto baud detection state machine ----------------------------*/
173
-
174
-		/*
175
-		   Starts a counter at falling edge that marks the
176
-		   beginning of the start bit.  Measures distance to
177
-		   next rising edge.  Calculates the distance from
178
-		   that rising edge to ideal position of the nearest
179
-		   edge for (i) the 50 baud case and (ii) the 45 baud
180
-		   case.  This gives us a distance metric for each
181
-		   possible baud rate.  The distance measure is
182
-		   smoothed over three samples.  The baud rate with
183
-		   the smallest distance is chosen as the current baud
184
-		   rate estimate.
185
-		*/
186
-
187
-		nextState = d.autobaud.state
188
-
189
-		switch d.autobaud.state {
190
-		case autobaudStateWaitStart:
191
-			/* start bit kicks off this state machine */
192
-			if d.state == demodStateSample {
193
-				nextState = autobaudStateWaitZeroX
194
-				d.autobaud.zeroX = 0
195
-			}
196
-		case autobaudStateWaitZeroX:
113
+	var nextState int
114
+	var sample int  // down counter for sampling instant
115
+	var bit int     // current bit being received
116
+	var baudot byte // received code
117
+	var zeroX int
118
+	var remainders map[int]int
119
+
120
+	state := demodStateWaitStart // current demod state machine state
121
+	var autobaudState int
122
+	if d.autobaud.enabled {
123
+		autobaudState = autobaudStateWaitStart
124
+	} else {
125
+		autobaudState = autobaudStateDisabled
126
+	}
127
+	count := 0
128
+	for sam := range d.input {
129
+		count++
130
+		if count%d.div > 0 {
197 131
 
198
-			d.autobaud.zeroX++
132
+			continue
133
+		}
134
+		//	d.dsp.demod(d.l.update(float64(sam)))
135
+		d.dsp.demod(float64(sam))
136
+
137
+		if d.sigOut {
138
+			m0, m1, m2 := d.dsp.level()
139
+			d.sigOutput <- [3]float64{m0, m1, m2}
140
+
141
+		} else {
142
+			//fmt.Println(d.dsp.level())
143
+			nextState = state
144
+
145
+			switch state {
146
+			case demodStateWaitStart:
147
+				// The demodulator sits in this state when it
148
+				// is waiting for a start bit.  When a
149
+				// "0" start-bit is detected that is
150
+				// above a minimum threshold it transitions
151
+				// out of this state.
152
+
153
+				if d.dsp.zeroPresent() {
154
+					sample = int(float64(d.samplerate) / d.settings.Baud * 1.5)
155
+					baudot = 0
156
+					bit = 0
157
+					nextState = demodStateSample
158
+					//	fmt.Println("demodStateSample")
199 159
 
200
-			if d.dsp.edgeDetected() {
201
-				/* OK, rising edge detected */
160
+				}
202 161
 
203
-				/* determine distance to closest edge assuming 50 baud */
162
+			case demodStateSample:
204 163
 
205
-				dist75 := d.sampleRate / 75
206
-				edge = 0
207
-				for i := 0; i < (d.dataBits + 1); i++ {
208
-					dist = absint(edge - d.autobaud.zeroX)
209
-					if dist < dist75 {
210
-						dist75 = dist
164
+				sample--
165
+				if sample == 0 {
166
+					if d.dsp.onePresent() {
167
+						baudot |= (1 << uint(bit))
168
+					}
169
+					bit++
170
+					if bit == d.settings.DataBits {
171
+
172
+						/* complete baudot char has been received */
173
+
174
+						sample = int(float64(d.samplerate) / d.settings.Baud)
175
+						nextState = demodStateWaitStop
176
+						//	fmt.Println("demodStateWaitStop", baudot)
177
+						d.output <- baudot
178
+					} else {
179
+						/* sample next bit 1 bit-period later */
180
+						sample = int(float64(d.samplerate) / d.settings.Baud)
211 181
 					}
212
-					edge += d.sampleRate / 75
213 182
 				}
214 183
 
215
-				/* determine distance to closest edge assuming 50 baud */
184
+				/* check for d.sampleRateK signal drop out */
216 185
 
217
-				dist50 := d.sampleRate / 50
218
-				edge = 0
219
-				for i := 0; i < (d.dataBits + 1); i++ {
220
-					dist = absint(edge - d.autobaud.zeroX)
221
-					if dist < dist50 {
222
-						dist50 = dist
223
-					}
224
-					edge += d.sampleRate / 50
225
-				}
186
+				if !d.dsp.carrier() {
187
+					d.minSigTimeout++
226 188
 
227
-				/* determine distance to closest edge assuming 47 baud */
189
+					/* If signal drops out for approx one bit period
190
+					   assume we have lost d.sampleRateK signal */
191
+					if d.minSigTimeout > d.samplesPerBit {
192
+						nextState = demodStateWaitStart
193
+						fmt.Println("demodStateWaitStart")
228 194
 
229
-				dist47 := d.sampleRate / 47
230
-				edge = 0
231
-				for i := 0; i < (d.dataBits + 1); i++ {
232
-					dist = absint(edge - d.autobaud.zeroX)
233
-					if dist < dist47 {
234
-						dist47 = dist
235 195
 					}
236
-					edge += d.sampleRate / 47
196
+				} else {
197
+					d.minSigTimeout = 0
237 198
 				}
238 199
 
239
-				/* determine distance to closest edge assuming 45 baud */
200
+			case demodStateWaitStop:
240 201
 
241
-				dist45 := d.sampleRate / 45
242
-				edge = 0
243
-				for i := 0; i < (d.dataBits + 1); i++ {
244
-					dist = absint(edge - d.autobaud.zeroX)
245
-					if dist < dist45 {
246
-						dist45 = dist
247
-					}
248
-					edge += d.sampleRate / 45
249
-				}
202
+				/* wait until we are in the middle of stop bit */
250 203
 
251
-				/* update 3-point smoothed distance estimates */
204
+				sample--
205
+				if sample == 0 {
206
+					nextState = demodStateWaitStart
207
+					//fmt.Println("demodStateWaitStart")
208
+				}
252 209
 
253
-				dist75Smooth := dist75 + demDist75[0] + demDist75[1]
254
-				demDist75[1] = demDist75[0]
255
-				demDist75[0] = dist75
210
+			}
256 211
 
257
-				dist50Smooth := dist50 + demDist50[0] + demDist50[1]
258
-				demDist50[1] = demDist50[0]
259
-				demDist50[0] = dist50
212
+			state = nextState
260 213
 
261
-				dist47Smooth := dist47 + demDist47[0] + demDist47[1]
262
-				demDist47[1] = demDist47[0]
263
-				demDist47[0] = dist47
214
+			// auto baud detection state machine
215
+			// This deviates quite a bit from the original C implementation.
216
+			// Starts a counter at falling edge that marks the
217
+			// beginning of the start bit.  Measures time to
218
+			// next rising edge.  This time is presumably from
219
+			// 1 to 6 bit periods long, so the remainder from divinging
220
+			// the time span by a numbe of bit periods for different baud
221
+			// rates is accumulated. The baud rate that divides the most
222
+			// evenly into all measured bit periods is the estimated baud rate.
264 223
 
265
-				dist45Smooth := dist45 + demDist45[0] + demDist45[1]
266
-				demDist45[1] = demDist45[0]
267
-				demDist45[0] = dist45
224
+			nextState = autobaudState
268 225
 
269
-				/* make a decision on baud rate */
226
+			switch autobaudState {
227
+			case autobaudStateWaitStart:
228
+				/* start bit kicks off this state machine */
229
+				if state == demodStateSample {
230
+					nextState = autobaudStateWaitZeroX
231
+					zeroX = 0
232
+					remainders = map[int]int{45: 0, 47: 0, 50: 0, 75: 0}
270 233
 
271
-				if (dist75Smooth < dist47Smooth) && (dist75Smooth < dist45Smooth) && (dist75Smooth < dist50Smooth) {
272
-					d.autobaud.estimate = 75
273
-				}
274
-				if (dist50Smooth < dist47Smooth) && (dist50Smooth < dist45Smooth) && (dist50Smooth < dist75Smooth) {
275
-					d.autobaud.estimate = 50
276
-				}
277
-				if (dist47Smooth < dist50Smooth) && (dist47Smooth < dist45Smooth) && (dist47Smooth < dist75Smooth) {
278
-					d.autobaud.estimate = 47
279
-				}
280
-				if (dist45Smooth < dist50Smooth) && (dist45Smooth < dist47Smooth) && (dist45Smooth < dist75Smooth) {
281
-					d.autobaud.estimate = 45
282 234
 				}
235
+			case autobaudStateWaitZeroX:
283 236
 
284
-				//printf("obl.autobaud_zerox: %d dist50: %02d dist47: %02d dist45: %02d est: %d\n",
285
-				//       obl.autobaud_zerox, dist50Smooth, dist47Smooth, dist45Smooth, obl.dem_baud_est);
286
-				nextState = autobaudStateWaitStop
287
-			}
237
+				zeroX++
288 238
 
289
-			/* reset autobaud state machine, e.g. if d.sampleRateK drops out before
290
-			   we get a zero crossing */
239
+				if d.dsp.edgeDetected() {
291 240
 
292
-			if d.state != demodStateSample {
293
-				nextState = autobaudStateWaitStop
294
-			}
241
+					estimate := 45 // pick one as a starting point
242
+					minRemainder := remainders[estimate]
295 243
 
296
-		case autobaudStateWaitStop:
297
-			if d.state == demodStateWaitStop {
298
-				nextState = autobaudStateWaitStart
299
-			}
244
+					for b, t := range remainders {
245
+						q := t + (zeroX % (d.samplerate / b))
246
+						remainders[b] = q
247
+						if q < minRemainder {
248
+							minRemainder = q
249
+							estimate = b
250
+						}
251
+					}
252
+
253
+					d.autobaud.estimate = float64(estimate)
254
+					nextState = autobaudStateWaitStop
255
+				}
256
+
257
+				// reset autobaud state machine, e.g. if signal drops out before
258
+				// we get a zero crossing
300 259
 
301
-		case autobaudStateDisabled:
260
+				if state != demodStateSample {
261
+					nextState = autobaudStateWaitStop
262
+				}
263
+
264
+			case autobaudStateWaitStop:
265
+				if state == demodStateWaitStop {
266
+					nextState = autobaudStateWaitStart
267
+				}
302 268
 
303
-		default:
269
+			case autobaudStateDisabled:
304 270
 
271
+			}
272
+			autobaudState = nextState
305 273
 		}
306
-		d.autobaud.state = nextState
307 274
 	}
308 275
 
309 276
 }

+ 110
- 49
dsp.go View File

@@ -11,67 +11,128 @@
11 11
 
12 12
 package openbaudot
13 13
 
14
+import "math"
15
+
14 16
 type dsp struct {
15
-	demOne      [2]float64 // previous 2 output samples of one filter
16
-	demOneQ0    [2]int16   // previous 2 output samples of one filter
17
-	demZero     [2]float64 // previous 2 output samples of zero filter
18
-	demZeroQ0   [2]int16   // previous 2 output samples of one filter
19
-	demLpfIn    []int16    // previous LPFOrder LPF input samples
20
-	pdemLpfIn   int        // ptr to oldest LPF input sample
21
-	demLpf      int        // current LPF output value
22
-	demTotalIn  []int16    // previous LPFOrder energy input samples
23
-	pdemTotalIn int        // ptr to oldest energy sample
24
-	demTotal    int        // current energy output value
25
-	c1, c2, c3  int16      // precomputed values for demod_dsp
26
-	lpfOrder    int        // LPFOrder Low pass filter order
27
-	minThresh   int        // MinThresh ratio for vaild signal
17
+	demLpf    float64 // current LPF output value
18
+	demTotal  float64 // current energy output value
19
+	lpfOrder  int     // LPFOrder Low pass filter order
20
+	minThresh float64 // MinThresh ratio for vaild signal
21
+	f1        *bandPass
22
+	f0        *bandPass
23
+	beta      float64
24
+	meter0    float64
25
+	meter1    float64
26
+	meter2    float64
27
+
28
+	carrierDetect bool
29
+	oneTone       bool
30
+	zeroTone      bool
31
+
32
+	lpfc     *lpf
33
+	lpfTotal *lpf
34
+
35
+	r float64
28 36
 }
29 37
 
38
+func newDsp(oneF, zeroF, sampleRate int) *dsp {
39
+
40
+	lfpOrder := 20
41
+
42
+	beta := .95
43
+
44
+	return &dsp{
45
+		// demLpfIn:   make([]float64, lfpOrder),
46
+		// demTotalIn: make([]float64, lfpOrder),
47
+		lpfOrder:  lfpOrder,
48
+		minThresh: 3,
49
+		f1:        newBandPass(oneF, sampleRate, beta),
50
+		f0:        newBandPass(zeroF, sampleRate, beta),
51
+		lpfc:      newLpf(lfpOrder),
52
+		lpfTotal:  newLpf(lfpOrder),
53
+	}
54
+}
30 55
 func (d *dsp) edgeDetected() bool {
31 56
 	return d.demLpf > d.minThresh*d.demTotal
32 57
 }
33 58
 
34
-func (d *dsp) dropout() bool {
35
-	return absint(d.demLpf) < d.minThresh*d.demTotal || d.demTotal == 0
59
+func (d *dsp) carrier() bool {
60
+	return d.carrierDetect
36 61
 }
37 62
 
38
-func (d *dsp) signalPresent() bool {
39
-	return d.demTotal != 0 && d.demLpf < -1*d.minThresh*d.demTotal
63
+func (d *dsp) level() (float64, float64, float64) {
64
+	return d.meter0, d.meter1, d.meter2
40 65
 }
41 66
 
42 67
 func (d *dsp) onePresent() bool {
43
-	return d.demLpf > 0
68
+	return d.oneTone
44 69
 }
45 70
 
46
-func (d *dsp) demod(sam int16) { //short *buffer, int samples)
47
-
48
-	sam >>= 5
49
-
50
-	oneQ0 := sam
51
-	oneQ0 += multq15(d.c1, d.demOneQ0[0])
52
-	oneQ0 -= multq15(d.c2, d.demOneQ0[1])
53
-	d.demOneQ0[1] = d.demOneQ0[0]
54
-	d.demOneQ0[0] = oneQ0
55
-
56
-	zeroQ0 := sam
57
-	zeroQ0 += multq15(d.c3, d.demZeroQ0[0])
58
-	zeroQ0 -= multq15(d.c2, d.demZeroQ0[1])
59
-	d.demZeroQ0[1] = d.demZeroQ0[0]
60
-	d.demZeroQ0[0] = zeroQ0
61
-
62
-	x := abs16(oneQ0) - abs16(zeroQ0)
63
-	d.demLpf -= int(d.demLpfIn[d.pdemLpfIn])
64
-	d.demLpf += int(x)
65
-	d.demLpfIn[d.pdemLpfIn] = x
66
-	d.pdemLpfIn = (d.pdemLpfIn + 1) % d.lpfOrder
67
-
68
-	/* now work out total energy estimate, this is used to
69
-	   determine if a valid signal is present by comparing
70
-	   the modem tone to total energy ratio */
71
-
72
-	x = abs16(sam)
73
-	d.demTotal -= int(d.demTotalIn[d.pdemTotalIn])
74
-	d.demTotal += int(x)
75
-	d.demTotalIn[d.pdemTotalIn] = x
76
-	d.pdemTotalIn = (d.pdemTotalIn + 1) % d.lpfOrder
71
+func (d *dsp) zeroPresent() bool {
72
+	return d.zeroTone
73
+}
74
+
75
+type lpf struct {
76
+	x []float64
77
+	t float64
78
+}
79
+
80
+func newLpf(size int) *lpf {
81
+	l := new(lpf)
82
+	l.x = make([]float64, size)
83
+	return l
84
+}
85
+
86
+func (l *lpf) update(v float64) float64 {
87
+
88
+	o := l.x[0]
89
+	l.x = append(l.x, v)
90
+	l.x = l.x[1:]
91
+
92
+	l.t -= o
93
+	l.t += v
94
+
95
+	return l.t
96
+}
97
+
98
+type bandPass struct {
99
+	dem0 float64
100
+	dem1 float64
101
+	w    float64
102
+	beta float64
103
+}
104
+
105
+func newBandPass(fc int, fs int, beta float64) *bandPass {
106
+	f := new(bandPass)
107
+	f.beta = beta
108
+	f.w = (2 * math.Pi * float64(fc) / float64(fs))
109
+
110
+	//	fmt.Println("fc", fc, "fs", fs, f.beta, f.w)
111
+	return f
112
+}
113
+
114
+func (f *bandPass) update(v float64) float64 {
115
+	n := v + 2.0*math.Cos(f.w)*f.beta*f.dem0 - f.beta*f.beta*f.dem1
116
+	f.dem1 = f.dem0
117
+	f.dem0 = n
118
+	return n
119
+}
120
+
121
+func (d *dsp) demod(sam float64) {
122
+
123
+	var one float64
124
+	one = d.f1.update(sam)
125
+
126
+	var zero float64
127
+	zero = d.f0.update(sam)
128
+
129
+	d.demLpf = d.lpfc.update(math.Abs(one) - math.Abs(zero))
130
+
131
+	d.demTotal = d.lpfTotal.update(math.Abs(sam))
132
+
133
+	d.carrierDetect = math.Abs(d.demLpf) > d.demTotal*d.minThresh
134
+
135
+	d.oneTone = d.demLpf > d.minThresh*d.demTotal && d.carrierDetect
136
+	d.zeroTone = -1*d.demLpf > d.minThresh*d.demTotal && d.carrierDetect
137
+
77 138
 }

+ 118
- 0
examples/audioin/main.go View File

@@ -0,0 +1,118 @@
1
+package main
2
+
3
+import (
4
+	"fmt"
5
+
6
+	"github.com/gordonklaus/portaudio"
7
+	"github.com/naleek/openbaudot"
8
+)
9
+
10
+func main() {
11
+
12
+	s := &openbaudot.ModemSettings{
13
+		Baud:       45,
14
+		StopBits:   2,
15
+		DataBits:   5,
16
+		SampleRate: 8000,
17
+		OneFreq:    2125,
18
+		ZeroFreq:   2295,
19
+	}
20
+
21
+	portaudio.Initialize()
22
+	defer portaudio.Terminate()
23
+
24
+	in := make([]int16, 64)
25
+	stream, err := portaudio.OpenDefaultStream(1, 0, float64(s.SampleRate), len(in), in)
26
+	if err != nil {
27
+		panic(err)
28
+	}
29
+	defer stream.Close()
30
+	charset := openbaudot.LoadCharset(&openbaudot.USTTY)
31
+	//
32
+	// encoderIn := make(chan rune)
33
+	// modulatorIn := make(chan byte)
34
+
35
+	demodulatorIn := make(chan int16)
36
+	decoderIn := make(chan byte)
37
+	decoderOut := make(chan rune)
38
+
39
+	//done := make(chan struct{})
40
+
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)
46
+
47
+	go func() {
48
+		for {
49
+			r := <-decoderOut
50
+			fmt.Printf("%c", r)
51
+		}
52
+	}()
53
+
54
+	stream.Start()
55
+	for {
56
+		stream.Read()
57
+		for i := range in {
58
+			demodulatorIn <- in[i]
59
+		}
60
+
61
+	}
62
+	//stream.Stop()
63
+
64
+	// go func() {
65
+	// 	encoder.EncodeString("hello world")
66
+	// 	time.Sleep(2000 * time.Millisecond)
67
+	// 	done <- struct{}{}
68
+	// }()
69
+
70
+}
71
+
72
+// // NewOBL is fun
73
+// func NewOBL(baud float64, callback Callback) *OBL {
74
+//
75
+// 	obl := new(OBL)
76
+//
77
+// 	obl.charset = USTTY.initialize()
78
+//
79
+// 	obl.callback = callback
80
+//
81
+// 	obl.mod.sinLutQ15 = make([]int16, 16384)
82
+// 	for i := 0; i < 16384; i++ {
83
+// 		obl.mod.sinLutQ15[i] = int16(32767 * math.Sin(2.0*math.Pi*float64(i)/float64(16384)))
84
+// 	}
85
+//
86
+// 	/* regular reset */
87
+// 	obl.autobaud.enabled = true
88
+// 	obl.Reset(baud)
89
+//
90
+// 	go func() {
91
+// 		portaudio.Initialize()
92
+// 		defer portaudio.Terminate()
93
+//
94
+// 		x := make(chan int16, 300)
95
+//
96
+// 		stream, _ := portaudio.OpenDefaultStream(0, 1, float64(sampleRate), 0, func(out []int16) {
97
+// 			for i := range out {
98
+// 				out[i] = <-x
99
+// 			}
100
+// 		})
101
+//
102
+// 		stream.Start()
103
+//
104
+// 		for {
105
+// 			s := <-obl.mod.output
106
+// 			//go func() {
107
+// 			obl.demod.input <- s
108
+// 			x <- s
109
+// 			//}()
110
+// 			//	binary.Write(f, binary.LittleEndian, s)
111
+// 		}
112
+// 	}()
113
+//
114
+// 	go obl.Modulate()
115
+// 	go obl.Demodulate()
116
+//
117
+// 	return obl
118
+// }

+ 145
- 0
examples/demtest.go View File

@@ -0,0 +1,145 @@
1
+package main
2
+
3
+import (
4
+	"fmt"
5
+	"math"
6
+	"math/rand"
7
+
8
+	"github.com/naleek/openbaudot"
9
+)
10
+
11
+// 96000 = 14
12
+// 48000 = 7
13
+// 44100 = 7
14
+// 22050 = 3
15
+// 11025 = 1
16
+// 8000 = 1
17
+func maisn() {
18
+	// lpf := 100
19
+	// l := newLpf(lpf)
20
+	//	for i := 5000; i <= 44100; i += 1000 {
21
+	// l.update(dostuff(11025))
22
+	//	dostuff(11025)
23
+	dostuff(8000)
24
+	// dostuff(11025)
25
+	// dostuff(22050)
26
+	// dostuff(44100)
27
+	// dostuff(48000)
28
+	// dostuff(96000)
29
+
30
+	// 	fmt.Printf("%d\t%f\n", i-lpf/2, v)
31
+	//}
32
+}
33
+func dostuff(freq int) float64 {
34
+
35
+	done := make(chan bool)
36
+
37
+	s := &openbaudot.ModemSettings{
38
+		Baud:       45,
39
+		StopBits:   2,
40
+		DataBits:   5,
41
+		SampleRate: freq,
42
+		OneFreq:    2125,
43
+		ZeroFreq:   2295,
44
+	}
45
+
46
+	demodulatorIn := make(chan int16)
47
+	demodulatorOut := make(chan [3]float64)
48
+
49
+	go func() {
50
+
51
+		// pulse
52
+		// for i := 0; i <= 200; i++ {
53
+		// 	if i == 50 {
54
+		// 		demodulatorIn <- int16(16384)
55
+		// 	} else {
56
+		// 		demodulatorIn <- int16(0)
57
+		// 	}
58
+		// }
59
+		//
60
+		// for samp := 0; samp <= s.SampleRate/45; samp++ {
61
+		// 	phase := 2 * math.Pi * float64(samp) / float64(s.SampleRate) * 2125 * 2
62
+		// 	demodulatorIn <- int16(math.Sin(phase) * 16384)
63
+		// }
64
+		// //	noise
65
+		for i := 0; i <= s.SampleRate/4; i++ {
66
+			n := rand.Float64()
67
+			demodulatorIn <- int16(n * 16384)
68
+		}
69
+		for x := 0; x <= 5; x++ {
70
+			for samp := 0; samp <= s.SampleRate/45; samp++ {
71
+				phase := 2 * math.Pi * float64(samp) / float64(s.SampleRate) * float64(s.OneFreq)
72
+				n := rand.Float64()
73
+				demodulatorIn <- int16((math.Sin(phase) + n) * 16384)
74
+			}
75
+
76
+			for samp := 0; samp <= s.SampleRate/45; samp++ {
77
+				phase := 2 * math.Pi * float64(samp) / float64(s.SampleRate) * float64(s.ZeroFreq)
78
+				n := rand.Float64()
79
+				demodulatorIn <- int16((math.Sin(phase) + n) * 16384)
80
+			}
81
+		}
82
+
83
+		//
84
+		// for samp := 0; samp <= s.SampleRate/1; samp++ {
85
+		// 	phase := 2 * math.Pi * float64(samp) / float64(s.SampleRate) * float64(s.ZeroFreq)
86
+		// 	demodulatorIn <- int16(math.Sin(phase) * 16384)
87
+		// }
88
+		// for samp := 0; samp <= s.SampleRate/40; samp++ {
89
+		// 	phase := 2 * math.Pi * float64(samp) / float64(s.SampleRate) * float64(s.OneFreq)
90
+		// 	demodulatorIn <- int16(math.Sin(phase) * 16384)
91
+		// }
92
+		//
93
+		// for samp := 0; samp <= s.SampleRate/40; samp++ {
94
+		// 	phase := 2 * math.Pi * float64(samp) / float64(s.SampleRate) * float64(s.ZeroFreq)
95
+		// 	demodulatorIn <- int16(math.Sin(phase) * 16384)
96
+		// }
97
+		// for samp := 0; samp <= s.SampleRate/10; samp++ {
98
+		// 	phase := 2 * math.Pi * float64(samp) / float64(s.SampleRate) * float64(s.ZeroFreq)
99
+		// 	demodulatorIn <- int16(math.Sin(phase) * 16384)
100
+		// }
101
+
102
+		done <- true
103
+	}()
104
+
105
+	openbaudot.NewDumbDemodulator(s, demodulatorIn, demodulatorOut)
106
+	c := 0
107
+	running := true
108
+	var r float64
109
+	for running {
110
+		select {
111
+		case v := <-demodulatorOut:
112
+			fmt.Printf("%f\t%f\t%f\n", v[0], v[1], v[2])
113
+			r = v[0]
114
+
115
+			c++
116
+		case <-done:
117
+
118
+			running = false
119
+		}
120
+	}
121
+	return r
122
+}
123
+
124
+type lpf struct {
125
+	x []float64
126
+	t float64
127
+}
128
+
129
+func newLpf(size int) *lpf {
130
+	l := new(lpf)
131
+	l.x = make([]float64, size)
132
+	return l
133
+}
134
+
135
+func (l *lpf) update(v float64) float64 {
136
+
137
+	o := l.x[0]
138
+	l.x = append(l.x, v)
139
+	l.x = l.x[1:]
140
+
141
+	l.t -= o
142
+	l.t += v
143
+
144
+	return l.t
145
+}

+ 23
- 55
examples/main.go View File

@@ -2,6 +2,7 @@ package main
2 2
 
3 3
 import (
4 4
 	"fmt"
5
+	"time"
5 6
 
6 7
 	"github.com/naleek/openbaudot"
7 8
 )
@@ -21,67 +22,34 @@ func main() {
21 22
 
22 23
 	encoderIn := make(chan rune)
23 24
 	modulatorIn := make(chan byte)
24
-	modulatorOut := make(chan int16)
25 25
 
26
-	// demodulatorIn := make(chan int16)
27
-	// decoderIn := make(chan byte)
28
-	// decoderOut := make(chan rune)
26
+	demodulatorIn := make(chan int16)
27
+	decoderIn := make(chan byte)
28
+	decoderOut := make(chan rune)
29 29
 
30
-	//done := make(chan struct{})
30
+	done := make(chan struct{})
31 31
 
32
-	sig := make(chan int)
32
+	//sig := make(chan [2]float64)
33 33
 	encoder := openbaudot.NewEncoder(encoderIn, modulatorIn, charset)
34
-	openbaudot.NewModulator(s, modulatorIn, modulatorOut)
35
-	openbaudot.NewDumbDemodulator(s, modulatorOut, sig)
36
-
37
-	// go func() {
38
-	// 	for b := range modulatorOut {
39
-	// 		demodulatorIn <- b
40
-	// 		time.Sleep(125 * time.Microsecond)
41
-	// 	}
42
-	// }()
43
-
44
-	// callback := func(event int, data interface{}) {
45
-	// 	switch event {
46
-	// 	case openbaudot.EventIdle:
47
-	// 		//fmt.Println("EventIdle")
48
-	// 		encoder.EncodeString("r")
49
-	//
50
-	// 	}
51
-	//
52
-	// }
53
-	go encoder.EncodeString("ry")
54
-	c := 0
55
-	for b := range sig {
56
-
57
-		fmt.Println(b)
58
-		if c == s.SampleRate {
59
-			break
34
+	openbaudot.NewModulator(s, modulatorIn, demodulatorIn)
35
+	openbaudot.NewDemodulator(s, demodulatorIn, decoderIn)
36
+	openbaudot.NewDecoder(decoderIn, decoderOut, charset)
37
+
38
+	go func() {
39
+		encoder.EncodeString("hello world")
40
+		time.Sleep(2000 * time.Millisecond)
41
+		done <- struct{}{}
42
+	}()
43
+
44
+	running := true
45
+	for running {
46
+		select {
47
+		case r := <-decoderOut:
48
+			fmt.Printf("%c", r)
49
+		case <-done:
50
+			running = false
60 51
 		}
61
-		c++
62 52
 	}
63
-
64
-	// modulator.SetCallback(callback)
65
-
66
-	// go func() {
67
-	// 	for b := range modulatorOut {
68
-	// 		demodulatorIn <- b
69
-	// 		time.Sleep(125 * time.Microsecond)
70
-	// 	}
71
-	// }()
72
-
73
-	// openbaudot.NewDemodulator(s, demodulatorIn, decoderIn)
74
-	// openbaudot.NewDecoder(decoderIn, decoderOut, charset)
75
-	//
76
-	// running := true
77
-	// for running {
78
-	// 	select {
79
-	// 	case r := <-decoderOut:
80
-	// 		fmt.Printf("%c", r)
81
-	// 	case <-done:
82
-	// 		running = false
83
-	// 	}
84
-	// }
85 53
 }
86 54
 
87 55
 // // NewOBL is fun

+ 17
- 16
modulator.go View File

@@ -26,8 +26,8 @@ type Modulator struct {
26 26
 	input  chan byte
27 27
 	output chan int16
28 28
 
29
-	baud            float64 // modulator baud rate
30
-	dataBits        int
29
+	settings *ModemSettings
30
+
31 31
 	samplesPerBit   int     // number of samples for one bit
32 32
 	phaseQ16        uint16  // phase acc 0..2math.Pi -> 0..65536
33 33
 	amplitude       int16   // peak modulator amplitude for one tone
@@ -36,29 +36,25 @@ type Modulator struct {
36 36
 	wOneQ16         uint16  // scaled one freq 0..2math.Pi -> 0..65536
37 37
 	wZeroQ16        uint16  // scaled zero freq 0..2math.Pi -> 0..65536
38 38
 	sinLutQ15       []int16 // sine lookup table
39
-	sampleRate      int     // sample rate
40 39
 	callback        Callback
41 40
 }
42 41
 
43 42
 // NewModulator is fun
44
-func NewModulator(s ModemSettings, input chan byte, output chan int16) *Modulator {
43
+func NewModulator(s *ModemSettings, input chan byte, output chan int16) *Modulator {
45 44
 
46 45
 	m := new(Modulator)
47
-	// mod.input = make(chan code)
48
-	// mod.output = make(chan int16)
49 46
 
50 47
 	m.input = input
51 48
 	m.output = output
52 49
 
53
-	m.baud = s.Baud
54
-	m.dataBits = s.DataBits
55
-	m.sampleRate = s.SampleRate
56
-	m.samplesPerBit = int(float64(m.sampleRate) / m.baud)
57
-	m.numStopHalfBits = int(math.Floor(s.StopBits / .5))
50
+	m.settings = s
51
+
52
+	m.samplesPerBit = int(float64(m.settings.SampleRate) / m.settings.Baud)
53
+	m.numStopHalfBits = int(math.Floor(m.settings.StopBits / .5))
58 54
 	m.numStopSamples = m.numStopHalfBits * m.samplesPerBit / 2
59 55
 	m.amplitude = 16384
60 56
 
61
-	wOne, wZero := calculateW(2125, 2295, m.sampleRate)
57
+	wOne, wZero := calculateW(m.settings.OneFreq, m.settings.ZeroFreq, m.settings.SampleRate)
62 58
 
63 59
 	m.wOneQ16 = uint16((65536 / (2.0 * math.Pi)) * wOne)
64 60
 	m.wZeroQ16 = uint16((65536 / (2.0 * math.Pi)) * wZero)
@@ -114,14 +110,15 @@ func (m *Modulator) run() {
114 110
 			case c = <-m.input:
115 111
 				modSample = 0
116 112
 				nextState = modStateStart
113
+				//fmt.Println("modStateStart")
117 114
 				//obl.callback(EventTransmitState, TransmitStart)
118 115
 				idleSamples = 0
119 116
 			default:
120
-				m.modulate(0)
117
+				m.modulate(1)
121 118
 
122
-				if idleSamples < m.sampleRate {
119
+				if idleSamples < m.settings.SampleRate {
123 120
 					idleSamples++
124
-				} else if idleSamples == m.sampleRate {
121
+				} else if idleSamples == m.settings.SampleRate {
125 122
 					if m.callback != nil {
126 123
 						m.callback(EventIdle, 0)
127 124
 					}
@@ -137,6 +134,8 @@ func (m *Modulator) run() {
137 134
 				modBit = int(c & 0x1)
138 135
 				modSample = 0
139 136
 				nextState = modStateBit
137
+				//fmt.Println("modStateBit")
138
+
140 139
 			}
141 140
 		case modStateBit:
142 141
 			m.modulate(modBit)
@@ -146,8 +145,10 @@ func (m *Modulator) run() {
146 145
 				modSample = 0
147 146
 				modNbit++
148 147
 				modBit = int((c >> uint(modNbit)) & 0x1)
149
-				if modNbit == m.dataBits {
148
+				if modNbit == m.settings.DataBits {
150 149
 					nextState = modStateStop
150
+					//	fmt.Println("modStateStop")
151
+
151 152
 				}
152 153
 			}
153 154
 		case modStateStop: