Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

172 lines
3.7KB

  1. package rds
  2. import "math"
  3. // Decoder performs a simple RDS baseband decode (BPSK, 1187.5 bps).
  4. type Decoder struct {
  5. ps [8]rune
  6. rt [64]rune
  7. lastPI uint16
  8. }
  9. type Result struct {
  10. PI uint16 `json:"pi"`
  11. PS string `json:"ps"`
  12. RT string `json:"rt"`
  13. }
  14. // Decode takes baseband samples at ~2400 Hz and attempts to extract PI/PS/RT.
  15. // NOTE: lightweight decoder with CRC+block sync; not a full RDS implementation.
  16. func (d *Decoder) Decode(base []float32, sampleRate int) Result {
  17. if len(base) == 0 || sampleRate <= 0 {
  18. return Result{}
  19. }
  20. // crude clock: 1187.5 bps
  21. baud := 1187.5
  22. spb := float64(sampleRate) / baud
  23. bits := make([]int, 0, int(float64(len(base))/spb))
  24. phase := 0.0
  25. for i := 0; i < len(base); i++ {
  26. phase += 1.0
  27. if phase >= spb {
  28. phase -= spb
  29. if base[i] >= 0 {
  30. bits = append(bits, 1)
  31. } else {
  32. bits = append(bits, 0)
  33. }
  34. }
  35. }
  36. if len(bits) < 26*4 {
  37. return Result{PI: d.lastPI, PS: d.psString(), RT: d.rtString()}
  38. }
  39. // search for block sync
  40. for i := 0; i+26*4 <= len(bits); i++ {
  41. bA, okA := decodeBlock(bits[i : i+26])
  42. bB, okB := decodeBlock(bits[i+26 : i+52])
  43. bC, okC := decodeBlock(bits[i+52 : i+78])
  44. bD, okD := decodeBlock(bits[i+78 : i+104])
  45. if !(okA && okB && okC && okD) {
  46. continue
  47. }
  48. if bA.offset != offA || bB.offset != offB || bC.offset != offC || bD.offset != offD {
  49. continue
  50. }
  51. pi := bA.data
  52. if pi != 0 {
  53. d.lastPI = pi
  54. }
  55. groupType := (bB.data >> 12) & 0xF
  56. versionA := ((bB.data >> 11) & 0x1) == 0
  57. if groupType == 0 && versionA {
  58. addr := bB.data & 0x3
  59. chars := []byte{byte(bD.data >> 8), byte(bD.data & 0xFF)}
  60. idx := int(addr) * 2
  61. if idx+1 < len(d.ps) {
  62. d.ps[idx] = sanitizeRune(chars[0])
  63. d.ps[idx+1] = sanitizeRune(chars[1])
  64. }
  65. }
  66. if groupType == 2 && versionA {
  67. addr := bB.data & 0xF
  68. chars := []byte{byte(bC.data >> 8), byte(bC.data & 0xFF), byte(bD.data >> 8), byte(bD.data & 0xFF)}
  69. idx := int(addr) * 4
  70. for j := 0; j < 4 && idx+j < len(d.rt); j++ {
  71. d.rt[idx+j] = sanitizeRune(chars[j])
  72. }
  73. }
  74. break
  75. }
  76. return Result{PI: d.lastPI, PS: d.psString(), RT: d.rtString()}
  77. }
  78. type block struct {
  79. data uint16
  80. offset uint16
  81. }
  82. const (
  83. offA uint16 = 0x0FC
  84. offB uint16 = 0x198
  85. offC uint16 = 0x168
  86. offD uint16 = 0x1B4
  87. )
  88. func decodeBlock(bits []int) (block, bool) {
  89. if len(bits) != 26 {
  90. return block{}, false
  91. }
  92. var raw uint32
  93. for _, b := range bits {
  94. raw = (raw << 1) | uint32(b&1)
  95. }
  96. data := uint16(raw >> 10)
  97. synd := crcSyndrome(raw)
  98. switch synd {
  99. case offA, offB, offC, offD:
  100. return block{data: data, offset: uint16(synd)}, true
  101. default:
  102. return block{}, false
  103. }
  104. }
  105. func crcSyndrome(raw uint32) uint16 {
  106. // polynomial 0x1B9 (10-bit)
  107. var reg uint32 = raw
  108. poly := uint32(0x1B9)
  109. for i := 25; i >= 10; i-- {
  110. if (reg>>uint(i))&1 == 1 {
  111. reg ^= poly << uint(i-10)
  112. }
  113. }
  114. return uint16(reg & 0x3FF)
  115. }
  116. func sanitizeRune(b byte) rune {
  117. if b < 32 || b > 126 {
  118. return ' '
  119. }
  120. return rune(b)
  121. }
  122. func (d *Decoder) psString() string {
  123. out := make([]rune, 0, len(d.ps))
  124. for _, r := range d.ps {
  125. if r == 0 {
  126. r = ' '
  127. }
  128. out = append(out, r)
  129. }
  130. return trimRight(out)
  131. }
  132. func (d *Decoder) rtString() string {
  133. out := make([]rune, 0, len(d.rt))
  134. for _, r := range d.rt {
  135. if r == 0 {
  136. r = ' '
  137. }
  138. out = append(out, r)
  139. }
  140. return trimRight(out)
  141. }
  142. func trimRight(in []rune) string {
  143. end := len(in)
  144. for end > 0 && in[end-1] == ' ' {
  145. end--
  146. }
  147. return string(in[:end])
  148. }
  149. // BPSKCostas returns a simple carrier-locked version of baseband (placeholder).
  150. func BPSKCostas(in []float32) []float32 {
  151. out := make([]float32, len(in))
  152. var phase float64
  153. for i, v := range in {
  154. phase += 0.0001 * float64(v) * math.Sin(phase)
  155. out[i] = float32(float64(v) * math.Cos(phase))
  156. }
  157. return out
  158. }