Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
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.

135 lines
2.7KB

  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. bits := buildBits(cfg)
  28. if len(bits) == 0 {
  29. bits = []float64{1}
  30. }
  31. return &Encoder{
  32. config: cfg,
  33. sampleRate: cfg.SampleRate,
  34. bits: bits,
  35. bitRate: defaultBitRate,
  36. subFreq: defaultSubcarrier,
  37. amplitude: defaultAmplitude,
  38. }, nil
  39. }
  40. // Reset restarts the encoder phases so Generate outputs from the beginning of the bit stream again.
  41. func (e *Encoder) Reset() {
  42. e.bitPhase = 0
  43. e.bitIndex = 0
  44. e.subPhase = 0
  45. }
  46. // Generate produces the requested number of RDS samples.
  47. func (e *Encoder) Generate(samples int) []float64 {
  48. out := make([]float64, samples)
  49. if len(e.bits) == 0 || samples == 0 {
  50. return out
  51. }
  52. for i := 0; i < samples; i++ {
  53. out[i] = e.nextSample()
  54. }
  55. return out
  56. }
  57. func (e *Encoder) nextSample() float64 {
  58. symbol := e.bits[e.bitIndex]
  59. value := e.amplitude * symbol * math.Sin(2*math.Pi*e.subPhase)
  60. e.subPhase += e.subFreq / e.sampleRate
  61. if e.subPhase >= 1 {
  62. e.subPhase -= math.Floor(e.subPhase)
  63. }
  64. e.bitPhase += e.bitRate / e.sampleRate
  65. if e.bitPhase >= 1 {
  66. steps := int(e.bitPhase)
  67. e.bitIndex = (e.bitIndex + steps) % len(e.bits)
  68. e.bitPhase -= float64(steps)
  69. }
  70. return value
  71. }
  72. func buildBits(cfg RDSConfig) []float64 {
  73. var bits []float64
  74. bits = append(bits, wordToBits(cfg.PI)...)
  75. status := uint8(cfg.PTY&0x1F) | boolToBit(cfg.TP)<<7 | boolToBit(cfg.TA)<<6
  76. bits = append(bits, byteToBits(status)...)
  77. bits = append(bits, stringToBits(cfg.PS)...)
  78. bits = append(bits, stringToBits(cfg.RT)...)
  79. return bits
  80. }
  81. func wordToBits(word uint16) []float64 {
  82. bits := make([]float64, 0, 16)
  83. for i := 15; i >= 0; i-- {
  84. bits = append(bits, bitToSymbol(uint8((word>>i)&1)))
  85. }
  86. return bits
  87. }
  88. func byteToBits(b uint8) []float64 {
  89. bits := make([]float64, 0, 8)
  90. for i := 7; i >= 0; i-- {
  91. bits = append(bits, bitToSymbol(uint8((b>>i)&1)))
  92. }
  93. return bits
  94. }
  95. func stringToBits(text string) []float64 {
  96. bits := make([]float64, 0, len(text)*8)
  97. for i := 0; i < len(text); i++ {
  98. for bit := 7; bit >= 0; bit-- {
  99. bits = append(bits, bitToSymbol(uint8((text[i]>>bit)&1)))
  100. }
  101. }
  102. return bits
  103. }
  104. func bitToSymbol(bit uint8) float64 {
  105. if bit == 0 {
  106. return -1
  107. }
  108. return 1
  109. }
  110. func boolToBit(value bool) uint8 {
  111. if value {
  112. return 1
  113. }
  114. return 0
  115. }