Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

137 рядки
2.8KB

  1. package rds
  2. import (
  3. "math"
  4. )
  5. const (
  6. defaultBitRate = 1187.5
  7. defaultSubcarrier = 57000
  8. defaultAmplitude = 0.02
  9. )
  10. // Encoder emits a simple BPSK-like RDS subcarrier stream for offline MPX builds.
  11. type Encoder struct {
  12. config RDSConfig
  13. sampleRate float64
  14. bits []float64
  15. bitRate float64
  16. subFreq float64
  17. amplitude float64
  18. bitPhase float64
  19. bitIndex int
  20. subPhase float64
  21. }
  22. // NewEncoder builds a new encoder for the provided configuration and sample rate.
  23. func NewEncoder(cfg RDSConfig) (*Encoder, error) {
  24. if cfg.SampleRate <= 0 {
  25. cfg.SampleRate = 48000
  26. }
  27. cfg.PS = normalizePS(cfg.PS)
  28. cfg.RT = normalizeRT(cfg.RT)
  29. bits := buildBits(cfg)
  30. if len(bits) == 0 {
  31. bits = []float64{1}
  32. }
  33. return &Encoder{
  34. config: cfg,
  35. sampleRate: cfg.SampleRate,
  36. bits: bits,
  37. bitRate: defaultBitRate,
  38. subFreq: defaultSubcarrier,
  39. amplitude: defaultAmplitude,
  40. }, nil
  41. }
  42. // Reset restarts the encoder phases so Generate outputs from the beginning of the bit stream again.
  43. func (e *Encoder) Reset() {
  44. e.bitPhase = 0
  45. e.bitIndex = 0
  46. e.subPhase = 0
  47. }
  48. // Generate produces the requested number of RDS samples.
  49. func (e *Encoder) Generate(samples int) []float64 {
  50. out := make([]float64, samples)
  51. if len(e.bits) == 0 || samples == 0 {
  52. return out
  53. }
  54. for i := 0; i < samples; i++ {
  55. out[i] = e.nextSample()
  56. }
  57. return out
  58. }
  59. func (e *Encoder) nextSample() float64 {
  60. symbol := e.bits[e.bitIndex]
  61. value := e.amplitude * symbol * math.Sin(2*math.Pi*e.subPhase)
  62. e.subPhase += e.subFreq / e.sampleRate
  63. if e.subPhase >= 1 {
  64. e.subPhase -= math.Floor(e.subPhase)
  65. }
  66. e.bitPhase += e.bitRate / e.sampleRate
  67. if e.bitPhase >= 1 {
  68. steps := int(e.bitPhase)
  69. e.bitIndex = (e.bitIndex + steps) % len(e.bits)
  70. e.bitPhase -= float64(steps)
  71. }
  72. return value
  73. }
  74. func buildBits(cfg RDSConfig) []float64 {
  75. var bits []float64
  76. bits = append(bits, wordToBits(cfg.PI)...)
  77. status := uint8(cfg.PTY&0x1F) | boolToBit(cfg.TP)<<7 | boolToBit(cfg.TA)<<6
  78. bits = append(bits, byteToBits(status)...)
  79. bits = append(bits, stringToBits(cfg.PS)...)
  80. bits = append(bits, stringToBits(cfg.RT)...)
  81. return bits
  82. }
  83. func wordToBits(word uint16) []float64 {
  84. bits := make([]float64, 0, 16)
  85. for i := 15; i >= 0; i-- {
  86. bits = append(bits, bitToSymbol(uint8((word>>i)&1)))
  87. }
  88. return bits
  89. }
  90. func byteToBits(b uint8) []float64 {
  91. bits := make([]float64, 0, 8)
  92. for i := 7; i >= 0; i-- {
  93. bits = append(bits, bitToSymbol(uint8((b>>i)&1)))
  94. }
  95. return bits
  96. }
  97. func stringToBits(text string) []float64 {
  98. bits := make([]float64, 0, len(text)*8)
  99. for i := 0; i < len(text); i++ {
  100. for bit := 7; bit >= 0; bit-- {
  101. bits = append(bits, bitToSymbol(uint8((text[i]>>bit)&1)))
  102. }
  103. }
  104. return bits
  105. }
  106. func bitToSymbol(bit uint8) float64 {
  107. if bit == 0 {
  108. return -1
  109. }
  110. return 1
  111. }
  112. func boolToBit(value bool) uint8 {
  113. if value {
  114. return 1
  115. }
  116. return 0
  117. }