|
@@ -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
|
}
|