Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

377 wiersze
8.8KB

  1. package rds
  2. // RDS encoder following the PiFmRds reference implementation.
  3. // Generates shaped biphase BPSK at 228 kHz (4× 57 kHz).
  4. //
  5. // Architecture:
  6. // raw bits → CRC+offset → differential encoding → shaped biphase waveform
  7. // → 57 kHz modulation via {0,+1,0,-1} at 228 kHz
  8. //
  9. // The shaped biphase waveform is a pre-computed RRC-filtered Manchester pulse.
  10. // Successive symbols overlap-add in a ring buffer, eliminating ISI.
  11. // 57 kHz carrier is exact at 228 kHz (period = 4 samples), no sin() needed.
  12. //
  13. // Call NextSample228k() at exactly 228 kHz to get the modulated RDS subcarrier.
  14. const (
  15. rdsRate = 228000.0 // internal sample rate, must be 4×57000
  16. rdsBitRate = 1187.5 // bits per second
  17. samplesPerBit = 192 // rdsRate / rdsBitRate = 228000/1187.5 = 192
  18. bitsPerBlock = 26
  19. bitsPerGroup = 4 * bitsPerBlock // 104
  20. )
  21. const crcPoly = 0x1B9
  22. var offsetWords = map[byte]uint16{
  23. 'A': 0x0FC, 'B': 0x198, 'C': 0x168, 'c': 0x350, 'D': 0x1B4,
  24. }
  25. func crc10(data uint16) uint16 {
  26. var reg uint32 = uint32(data) << 10
  27. for i := 15; i >= 0; i-- {
  28. if reg&(1<<(uint(i)+10)) != 0 {
  29. reg ^= uint32(crcPoly) << uint(i)
  30. }
  31. }
  32. return uint16(reg & 0x3FF)
  33. }
  34. func encodeBlock(data uint16, offset byte) uint32 {
  35. return (uint32(data) << 10) | uint32(crc10(data)^offsetWords[offset])
  36. }
  37. // --- Group building (unchanged) ---
  38. func buildGroup0A(pi uint16, pty uint8, tp, ta bool, segIdx int, ps string) [4]uint16 {
  39. ps = normalizePS(ps)
  40. var bB uint16
  41. if tp {
  42. bB |= 1 << 10
  43. }
  44. bB |= uint16(pty&0x1F) << 5
  45. if ta {
  46. bB |= 1 << 4
  47. }
  48. bB |= 1 << 3
  49. bB |= uint16(segIdx & 0x03)
  50. ci := segIdx * 2
  51. return [4]uint16{pi, bB, pi, (uint16(ps[ci]) << 8) | uint16(ps[ci+1])}
  52. }
  53. func buildGroup2A(pi uint16, pty uint8, tp bool, abFlag bool, segIdx int, rt string) [4]uint16 {
  54. rt = normalizeRT(rt)
  55. var bB uint16 = 2 << 12
  56. if tp {
  57. bB |= 1 << 10
  58. }
  59. bB |= uint16(pty&0x1F) << 5
  60. if abFlag {
  61. bB |= 1 << 4
  62. }
  63. bB |= uint16(segIdx & 0x0F)
  64. ci := segIdx * 4
  65. c0, c1, c2, c3 := padRT(rt, ci)
  66. return [4]uint16{pi, bB, (uint16(c0) << 8) | uint16(c1), (uint16(c2) << 8) | uint16(c3)}
  67. }
  68. func padRT(rt string, off int) (byte, byte, byte, byte) {
  69. g := func(i int) byte {
  70. if i < len(rt) {
  71. return rt[i]
  72. }
  73. return ' '
  74. }
  75. return g(off), g(off + 1), g(off + 2), g(off + 3)
  76. }
  77. // --- Group scheduler (unchanged) ---
  78. type GroupScheduler struct {
  79. cfg RDSConfig
  80. psIdx int
  81. rtIdx int
  82. phase int
  83. rtABFlag bool
  84. }
  85. func newGroupScheduler(cfg RDSConfig) *GroupScheduler {
  86. return &GroupScheduler{cfg: cfg}
  87. }
  88. func (gs *GroupScheduler) NextGroup() [4]uint16 {
  89. if gs.phase < 4 {
  90. g := buildGroup0A(gs.cfg.PI, gs.cfg.PTY, gs.cfg.TP, gs.cfg.TA, gs.psIdx, gs.cfg.PS)
  91. gs.psIdx = (gs.psIdx + 1) % 4
  92. gs.phase++
  93. return g
  94. }
  95. g := buildGroup2A(gs.cfg.PI, gs.cfg.PTY, gs.cfg.TP, gs.rtABFlag, gs.rtIdx, gs.cfg.RT)
  96. gs.rtIdx++
  97. rtSegs := rtSegmentCount(gs.cfg.RT)
  98. if gs.rtIdx >= rtSegs {
  99. gs.rtIdx = 0
  100. gs.rtABFlag = !gs.rtABFlag
  101. }
  102. gs.phase++
  103. if gs.phase >= 4+rtSegs {
  104. gs.phase = 0
  105. }
  106. return g
  107. }
  108. func rtSegmentCount(rt string) int {
  109. rt = normalizeRT(rt)
  110. n := (len(rt) + 3) / 4
  111. if n == 0 {
  112. n = 1
  113. }
  114. if n > 16 {
  115. n = 16
  116. }
  117. return n
  118. }
  119. // --- Differential encoder ---
  120. type diffEncoder struct{ prev uint8 }
  121. func (d *diffEncoder) encode(bit uint8) uint8 {
  122. out := d.prev ^ bit
  123. d.prev = out
  124. return out
  125. }
  126. // --- Biphase waveform ---
  127. // A biphase symbol at 228 kHz / 1187.5 bps = 192 samples per bit.
  128. // First half (96 samples): +1, second half (96 samples): -1.
  129. // This is the "rectangular biphase pulse". PiFmRds uses an RRC-shaped
  130. // version, but even rectangular works for most receivers. We apply a
  131. // simple raised-cosine taper at the transitions to reduce spectral splatter.
  132. //
  133. // The waveform is 192 samples long. It gets overlap-added into the ring
  134. // buffer, so adjacent symbols blend at boundaries.
  135. const waveformLen = samplesPerBit // 192
  136. const taperLen = 8 // smooth transitions over 8 samples
  137. var biphaseWaveform [waveformLen]float32
  138. func init() {
  139. // Generate biphase pulse: +1 for first half, -1 for second half,
  140. // with raised-cosine tapered transitions.
  141. half := waveformLen / 2
  142. for i := 0; i < waveformLen; i++ {
  143. if i < half {
  144. biphaseWaveform[i] = 1.0
  145. } else {
  146. biphaseWaveform[i] = -1.0
  147. }
  148. }
  149. // Taper at start (0 → +1)
  150. for i := 0; i < taperLen; i++ {
  151. t := float32(i) / float32(taperLen)
  152. biphaseWaveform[i] *= t
  153. }
  154. // Taper at midpoint (+1 → -1): ramp over taperLen samples centered at half
  155. for i := 0; i < taperLen; i++ {
  156. t := float32(i) / float32(taperLen)
  157. // Crossfade from +1 to -1
  158. idx := half - taperLen/2 + i
  159. if idx >= 0 && idx < waveformLen {
  160. biphaseWaveform[idx] = 1.0 - 2.0*t
  161. }
  162. }
  163. // Taper at end (-1 → 0)
  164. for i := 0; i < taperLen; i++ {
  165. t := float32(i) / float32(taperLen)
  166. biphaseWaveform[waveformLen-1-i] *= t
  167. }
  168. }
  169. // --- Encoder ---
  170. const ringSize = samplesPerBit + waveformLen // overlap region
  171. // Encoder generates RDS subcarrier samples at 228 kHz.
  172. // Call NextSample228k() at 228 kHz rate to get modulated output.
  173. type Encoder struct {
  174. config RDSConfig
  175. scheduler *GroupScheduler
  176. diff diffEncoder
  177. // Bit stream
  178. bitBuf [bitsPerGroup]int
  179. bitLen int
  180. bitPos int
  181. // Ring buffer for overlap-add shaped biphase
  182. ring [ringSize]float32
  183. ringWrite int // next write position for new symbol
  184. ringRead int // current read position
  185. // Sample counter within current bit
  186. sampleCount int
  187. // 57 kHz carrier phase: cycles through 0,1,2,3 at 228 kHz
  188. carrierPhase int
  189. // For backward-compat Generate() used in tests
  190. SampleRate float64
  191. }
  192. func NewEncoder(cfg RDSConfig) (*Encoder, error) {
  193. if cfg.SampleRate <= 0 {
  194. cfg.SampleRate = rdsRate
  195. }
  196. cfg.PS = normalizePS(cfg.PS)
  197. cfg.RT = normalizeRT(cfg.RT)
  198. enc := &Encoder{
  199. config: cfg,
  200. SampleRate: cfg.SampleRate,
  201. scheduler: newGroupScheduler(cfg),
  202. }
  203. enc.loadNextGroup()
  204. // Pre-load first symbol into ring buffer
  205. enc.emitSymbol()
  206. return enc, nil
  207. }
  208. func (e *Encoder) Reset() {
  209. e.diff = diffEncoder{}
  210. e.scheduler = newGroupScheduler(e.config)
  211. e.bitLen = 0
  212. e.bitPos = 0
  213. e.sampleCount = 0
  214. e.carrierPhase = 0
  215. e.ringWrite = 0
  216. e.ringRead = 0
  217. for i := range e.ring {
  218. e.ring[i] = 0
  219. }
  220. e.loadNextGroup()
  221. e.emitSymbol()
  222. }
  223. // NextSample228k returns the next RDS subcarrier sample.
  224. // MUST be called at exactly 228000 Hz.
  225. // Output is the shaped biphase envelope modulated onto 57 kHz.
  226. func (e *Encoder) NextSample228k() float64 {
  227. // Read shaped envelope from ring buffer
  228. envelope := float64(e.ring[e.ringRead])
  229. e.ring[e.ringRead] = 0 // clear after reading
  230. e.ringRead++
  231. if e.ringRead >= ringSize {
  232. e.ringRead = 0
  233. }
  234. // Advance sample counter; emit next symbol when bit period ends
  235. e.sampleCount++
  236. if e.sampleCount >= samplesPerBit {
  237. e.sampleCount = 0
  238. e.bitPos++
  239. if e.bitPos >= e.bitLen {
  240. e.loadNextGroup()
  241. }
  242. e.emitSymbol()
  243. }
  244. // 57 kHz modulation: at 228 kHz, period = 4 samples.
  245. // Phase pattern: {0, +1, 0, -1} → multiply envelope by carrier
  246. var carrier float64
  247. switch e.carrierPhase {
  248. case 0:
  249. carrier = 0
  250. case 1:
  251. carrier = 1
  252. case 2:
  253. carrier = 0
  254. case 3:
  255. carrier = -1
  256. }
  257. e.carrierPhase++
  258. if e.carrierPhase >= 4 {
  259. e.carrierPhase = 0
  260. }
  261. return envelope * carrier
  262. }
  263. // emitSymbol writes the shaped biphase waveform for the current bit
  264. // into the ring buffer using overlap-add.
  265. func (e *Encoder) emitSymbol() {
  266. // Differential encoding output determines polarity
  267. diffBit := e.bitBuf[e.bitPos]
  268. sign := float32(1.0)
  269. if diffBit == 1 {
  270. sign = -1.0
  271. }
  272. // Overlap-add the biphase waveform into the ring buffer
  273. idx := e.ringWrite
  274. for i := 0; i < waveformLen; i++ {
  275. e.ring[idx] += biphaseWaveform[i] * sign
  276. idx++
  277. if idx >= ringSize {
  278. idx = 0
  279. }
  280. }
  281. e.ringWrite += samplesPerBit
  282. if e.ringWrite >= ringSize {
  283. e.ringWrite -= ringSize
  284. }
  285. }
  286. func (e *Encoder) loadNextGroup() {
  287. group := e.scheduler.NextGroup()
  288. e.bitLen = 0
  289. for blk, off := range [4]byte{'A', 'B', 'C', 'D'} {
  290. enc := encodeBlock(group[blk], off)
  291. for bit := bitsPerBlock - 1; bit >= 0; bit-- {
  292. raw := uint8((enc >> uint(bit)) & 1)
  293. e.bitBuf[e.bitLen] = int(e.diff.encode(raw))
  294. e.bitLen++
  295. }
  296. }
  297. e.bitPos = 0
  298. }
  299. // --- Backward-compatible API for tests ---
  300. // NextSample returns a single RDS sample at 228 kHz. Same as NextSample228k.
  301. func (e *Encoder) NextSample() float64 {
  302. return e.NextSample228k()
  303. }
  304. // Generate produces n RDS samples at 228 kHz.
  305. func (e *Encoder) Generate(n int) []float64 {
  306. out := make([]float64, n)
  307. for i := range out {
  308. out[i] = e.NextSample228k()
  309. }
  310. return out
  311. }
  312. // Symbol returns +1/-1 for the current differential bit (NRZ, no biphase).
  313. // Only for the software decoder test path.
  314. func (e *Encoder) Symbol() float64 {
  315. if e.bitLen == 0 {
  316. return -1
  317. }
  318. sym := float64(1)
  319. if e.bitBuf[e.bitPos] == 0 {
  320. sym = -1
  321. }
  322. // Advance bit clock at 228 kHz rate
  323. e.sampleCount++
  324. if e.sampleCount >= samplesPerBit {
  325. e.sampleCount = 0
  326. e.bitPos++
  327. if e.bitPos >= e.bitLen {
  328. e.loadNextGroup()
  329. }
  330. }
  331. return sym
  332. }