Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

265 rindas
5.8KB

  1. package rds
  2. import (
  3. "math"
  4. )
  5. const (
  6. defaultSubcarrierHz = 57000.0
  7. defaultBitRateHz = 1187.5
  8. // Each RDS group has 4 blocks of 26 bits each (16 data + 10 check).
  9. bitsPerBlock = 26
  10. bitsPerGroup = 4 * bitsPerBlock // 104
  11. )
  12. // -----------------------------------------------------------------------
  13. // CRC / offset words per IEC 62106 / EN 50067
  14. // -----------------------------------------------------------------------
  15. const crcPoly = 0x1B9
  16. var offsetWords = map[byte]uint16{
  17. 'A': 0x0FC,
  18. 'B': 0x198,
  19. 'C': 0x168,
  20. 'c': 0x350,
  21. 'D': 0x1B4,
  22. }
  23. func crc10(data uint16) uint16 {
  24. var reg uint32 = uint32(data) << 10
  25. for i := 15; i >= 0; i-- {
  26. if reg&(1<<(uint(i)+10)) != 0 {
  27. reg ^= uint32(crcPoly) << uint(i)
  28. }
  29. }
  30. return uint16(reg & 0x3FF)
  31. }
  32. func encodeBlock(data uint16, offset byte) uint32 {
  33. check := crc10(data) ^ offsetWords[offset]
  34. return (uint32(data) << 10) | uint32(check)
  35. }
  36. // -----------------------------------------------------------------------
  37. // Group building
  38. // -----------------------------------------------------------------------
  39. func buildGroup0A(pi uint16, pty uint8, tp, ta bool, segIdx int, ps string) [4]uint16 {
  40. ps = normalizePS(ps)
  41. blockA := pi
  42. var blockB uint16
  43. if tp {
  44. blockB |= 1 << 10
  45. }
  46. blockB |= uint16(pty&0x1F) << 5
  47. if ta {
  48. blockB |= 1 << 4
  49. }
  50. blockB |= 1 << 3
  51. blockB |= uint16(segIdx & 0x03)
  52. blockC := pi
  53. ci := segIdx * 2
  54. blockD := (uint16(ps[ci]) << 8) | uint16(ps[ci+1])
  55. return [4]uint16{blockA, blockB, blockC, blockD}
  56. }
  57. func buildGroup2A(pi uint16, pty uint8, tp bool, abFlag bool, segIdx int, rt string) [4]uint16 {
  58. rt = normalizeRT(rt)
  59. blockA := pi
  60. var blockB uint16
  61. blockB = 2 << 12
  62. if tp {
  63. blockB |= 1 << 10
  64. }
  65. blockB |= uint16(pty&0x1F) << 5
  66. if abFlag {
  67. blockB |= 1 << 4
  68. }
  69. blockB |= uint16(segIdx & 0x0F)
  70. ci := segIdx * 4
  71. ch0, ch1, ch2, ch3 := padRT(rt, ci)
  72. blockC := (uint16(ch0) << 8) | uint16(ch1)
  73. blockD := (uint16(ch2) << 8) | uint16(ch3)
  74. return [4]uint16{blockA, blockB, blockC, blockD}
  75. }
  76. func padRT(rt string, offset int) (byte, byte, byte, byte) {
  77. get := func(i int) byte {
  78. if i < len(rt) {
  79. return rt[i]
  80. }
  81. return ' '
  82. }
  83. return get(offset), get(offset + 1), get(offset + 2), get(offset + 3)
  84. }
  85. // -----------------------------------------------------------------------
  86. // Group scheduler
  87. // -----------------------------------------------------------------------
  88. type GroupScheduler struct {
  89. cfg RDSConfig
  90. psIdx int
  91. rtIdx int
  92. rtABFlag bool
  93. phase int
  94. }
  95. func newGroupScheduler(cfg RDSConfig) *GroupScheduler {
  96. return &GroupScheduler{cfg: cfg}
  97. }
  98. func (gs *GroupScheduler) NextGroup() [4]uint16 {
  99. if gs.phase < 4 {
  100. g := buildGroup0A(gs.cfg.PI, gs.cfg.PTY, gs.cfg.TP, gs.cfg.TA, gs.psIdx, gs.cfg.PS)
  101. gs.psIdx = (gs.psIdx + 1) % 4
  102. gs.phase++
  103. return g
  104. }
  105. g := buildGroup2A(gs.cfg.PI, gs.cfg.PTY, gs.cfg.TP, gs.rtABFlag, gs.rtIdx, gs.cfg.RT)
  106. gs.rtIdx++
  107. rtSegs := rtSegmentCount(gs.cfg.RT)
  108. if gs.rtIdx >= rtSegs {
  109. gs.rtIdx = 0
  110. gs.rtABFlag = !gs.rtABFlag
  111. }
  112. gs.phase++
  113. if gs.phase >= 4+rtSegs {
  114. gs.phase = 0
  115. }
  116. return g
  117. }
  118. func rtSegmentCount(rt string) int {
  119. rt = normalizeRT(rt)
  120. n := (len(rt) + 3) / 4
  121. if n == 0 {
  122. n = 1
  123. }
  124. if n > 16 {
  125. n = 16
  126. }
  127. return n
  128. }
  129. // -----------------------------------------------------------------------
  130. // Differential encoder
  131. // -----------------------------------------------------------------------
  132. type diffEncoder struct {
  133. prev uint8
  134. }
  135. func (d *diffEncoder) encode(bit uint8) uint8 {
  136. out := d.prev ^ bit
  137. d.prev = out
  138. return out
  139. }
  140. // -----------------------------------------------------------------------
  141. // Encoder
  142. // -----------------------------------------------------------------------
  143. // Encoder produces a standards-grade RDS BPSK subcarrier at 57 kHz.
  144. // Output is unity-normalized (peak ±1.0). The caller (combiner) controls
  145. // the actual injection level.
  146. type Encoder struct {
  147. config RDSConfig
  148. sampleRate float64
  149. scheduler *GroupScheduler
  150. diff diffEncoder
  151. bitBuf [bitsPerGroup]uint8
  152. bitLen int
  153. bitPos int
  154. bitPhase float64
  155. subPhase float64
  156. }
  157. // NewEncoder builds a new encoder for the provided configuration and sample rate.
  158. func NewEncoder(cfg RDSConfig) (*Encoder, error) {
  159. if cfg.SampleRate <= 0 {
  160. cfg.SampleRate = 228000
  161. }
  162. cfg.PS = normalizePS(cfg.PS)
  163. cfg.RT = normalizeRT(cfg.RT)
  164. enc := &Encoder{
  165. config: cfg,
  166. sampleRate: cfg.SampleRate,
  167. scheduler: newGroupScheduler(cfg),
  168. }
  169. enc.loadNextGroup()
  170. return enc, nil
  171. }
  172. // Reset restarts the encoder.
  173. func (e *Encoder) Reset() {
  174. e.bitPhase = 0
  175. e.subPhase = 0
  176. e.diff = diffEncoder{}
  177. e.scheduler = newGroupScheduler(e.config)
  178. e.bitLen = 0
  179. e.bitPos = 0
  180. e.loadNextGroup()
  181. }
  182. // NextSample returns the next RDS subcarrier sample.
  183. // Zero-allocation hot path for real-time use.
  184. func (e *Encoder) NextSample() float64 {
  185. var symbol float64
  186. if e.bitLen == 0 || e.bitBuf[e.bitPos] == 0 {
  187. symbol = -1
  188. } else {
  189. symbol = 1
  190. }
  191. value := symbol * math.Sin(2*math.Pi*e.subPhase)
  192. e.subPhase += defaultSubcarrierHz / e.sampleRate
  193. if e.subPhase >= 1 {
  194. e.subPhase -= math.Floor(e.subPhase)
  195. }
  196. e.bitPhase += defaultBitRateHz / e.sampleRate
  197. if e.bitPhase >= 1 {
  198. steps := int(e.bitPhase)
  199. e.bitPhase -= float64(steps)
  200. e.bitPos += steps
  201. if e.bitPos >= e.bitLen {
  202. e.loadNextGroup()
  203. }
  204. }
  205. return value
  206. }
  207. // Generate produces n RDS samples. Convenience wrapper; prefer NextSample()
  208. // in real-time paths.
  209. func (e *Encoder) Generate(samples int) []float64 {
  210. out := make([]float64, samples)
  211. for i := range out {
  212. out[i] = e.NextSample()
  213. }
  214. return out
  215. }
  216. func (e *Encoder) loadNextGroup() {
  217. group := e.scheduler.NextGroup()
  218. e.bitLen = 0
  219. offsets := [4]byte{'A', 'B', 'C', 'D'}
  220. for blk := 0; blk < 4; blk++ {
  221. encoded := encodeBlock(group[blk], offsets[blk])
  222. for bit := bitsPerBlock - 1; bit >= 0; bit-- {
  223. raw := uint8((encoded >> uint(bit)) & 1)
  224. diffBit := e.diff.encode(raw)
  225. e.bitBuf[e.bitLen] = diffBit
  226. e.bitLen++
  227. }
  228. }
  229. e.bitPos = 0
  230. }