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.

289 wiersze
6.6KB

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