// Copyright (C) 2017 Keelan Lightfoot // Copyright (C) 2007-2008 Board of Regents of the University of Wisconsin // System (Univ. of Wisconsin-Madison, Trace R&D Center) // Copyright (C) 2007-2008 Omnitor AB // Copyright (C) 2007-2008 Voiceriver Inc // // This library is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation; either version 2.1 of the License, or (at // your option) any later version. package openbaudot import "math" const ( modStateIdle = iota modStateStart modStateBit modStateStop modStateHold ) // Modulator is fun type Modulator struct { input chan byte output chan int16 baud float64 // modulator baud rate dataBits int samplesPerBit int // number of samples for one bit phaseQ16 uint16 // phase acc 0..2math.Pi -> 0..65536 amplitude int16 // peak modulator amplitude for one tone numStopHalfBits int // number of stop bits numStopSamples int // number of samples for stop bit wOneQ16 uint16 // scaled one freq 0..2math.Pi -> 0..65536 wZeroQ16 uint16 // scaled zero freq 0..2math.Pi -> 0..65536 sinLutQ15 []int16 // sine lookup table sampleRate int // sample rate callback Callback } // NewModulator is fun func NewModulator(s ModemSettings, input chan byte, output chan int16) *Modulator { m := new(Modulator) // mod.input = make(chan code) // mod.output = make(chan int16) m.input = input m.output = output m.baud = s.Baud m.dataBits = s.DataBits m.sampleRate = s.SampleRate m.samplesPerBit = int(float64(m.sampleRate) / m.baud) m.numStopHalfBits = int(math.Floor(s.StopBits / .5)) m.numStopSamples = m.numStopHalfBits * m.samplesPerBit / 2 m.amplitude = 16384 wOne, wZero := calculateW(2125, 2295, m.sampleRate) m.wOneQ16 = uint16((65536 / (2.0 * math.Pi)) * wOne) m.wZeroQ16 = uint16((65536 / (2.0 * math.Pi)) * wZero) m.sinLutQ15 = make([]int16, 16384) for i := 0; i < 16384; i++ { m.sinLutQ15[i] = int16(32767 * math.Sin(2.0*math.Pi*float64(i)/float64(16384))) } go m.run() return m } // SetCallback is fun func (m *Modulator) SetCallback(callback Callback) { m.callback = callback } func (m *Modulator) modulate(bit int) { //short *buffer, int samples) /* use of unsigned short in Q16 for format for phase accumulator leads to natural wrap-around at 2PI boundaries */ /* sin_lut_q15 is in Q15 format, therefore we rightshift 15 to get Q0 at modulator output. We cast multiply arguments to 32 bit ints to ensure compiler uses a multiply with a 32 bit result. */ if bit != 0 { m.phaseQ16 += m.wOneQ16 m.output <- multq15(m.amplitude, m.sinLutQ15[m.phaseQ16>>2]) } else { m.phaseQ16 += m.wZeroQ16 m.output <- multq15(m.amplitude, m.sinLutQ15[m.phaseQ16>>2]) } } func (m *Modulator) run() { var modState int // modulator state machine var nextState int // next state of state machine var c byte // current baudot char being modulated var modNbit int // num bits modulated so far in this char var modBit int // current bit in modBaudot being modulated var modSample int // current sample in bit being modulated var idleSamples int for { nextState = modState switch modState { case modStateIdle: select { case c = <-m.input: modSample = 0 nextState = modStateStart //obl.callback(EventTransmitState, TransmitStart) idleSamples = 0 default: m.modulate(0) if idleSamples < m.sampleRate { idleSamples++ } else if idleSamples == m.sampleRate { if m.callback != nil { m.callback(EventIdle, 0) } idleSamples++ } } case modStateStart: m.modulate(0) modSample++ // after sending start bit send LSB data bit if modSample == m.samplesPerBit { modNbit = 0 modBit = int(c & 0x1) modSample = 0 nextState = modStateBit } case modStateBit: m.modulate(modBit) modSample++ // when this data bit complete, send next data bit if modSample == m.samplesPerBit { modSample = 0 modNbit++ modBit = int((c >> uint(modNbit)) & 0x1) if modNbit == m.dataBits { nextState = modStateStop } } case modStateStop: m.modulate(1) modSample++ // when stop bit complete start transmission of next char, or enter idle state if modSample == m.numStopSamples { modSample = 0 nextState = modStateIdle } // (obl.callback)(obl,OBL_EVENT_TX_STATE, OBL_TRANSMIT_STOP); } modState = nextState } }