|
- package rds
-
- import (
- "math"
- )
-
- const (
- defaultBitRate = 1187.5
- defaultSubcarrier = 57000
- defaultAmplitude = 0.02
- )
-
- // Encoder emits a simple BPSK-like RDS subcarrier stream for offline MPX builds.
- type Encoder struct {
- config RDSConfig
- sampleRate float64
- bits []float64
- bitRate float64
- subFreq float64
- amplitude float64
-
- bitPhase float64
- bitIndex int
- subPhase float64
- }
-
- // NewEncoder builds a new encoder for the provided configuration and sample rate.
- func NewEncoder(cfg RDSConfig) (*Encoder, error) {
- if cfg.SampleRate <= 0 {
- cfg.SampleRate = 48000
- }
-
- bits := buildBits(cfg)
- if len(bits) == 0 {
- bits = []float64{1}
- }
-
- return &Encoder{
- config: cfg,
- sampleRate: cfg.SampleRate,
- bits: bits,
- bitRate: defaultBitRate,
- subFreq: defaultSubcarrier,
- amplitude: defaultAmplitude,
- }, nil
- }
-
- // Reset restarts the encoder phases so Generate outputs from the beginning of the bit stream again.
- func (e *Encoder) Reset() {
- e.bitPhase = 0
- e.bitIndex = 0
- e.subPhase = 0
- }
-
- // Generate produces the requested number of RDS samples.
- func (e *Encoder) Generate(samples int) []float64 {
- out := make([]float64, samples)
- if len(e.bits) == 0 || samples == 0 {
- return out
- }
-
- for i := 0; i < samples; i++ {
- out[i] = e.nextSample()
- }
- return out
- }
-
- func (e *Encoder) nextSample() float64 {
- symbol := e.bits[e.bitIndex]
- value := e.amplitude * symbol * math.Sin(2*math.Pi*e.subPhase)
- e.subPhase += e.subFreq / e.sampleRate
- if e.subPhase >= 1 {
- e.subPhase -= math.Floor(e.subPhase)
- }
-
- e.bitPhase += e.bitRate / e.sampleRate
- if e.bitPhase >= 1 {
- steps := int(e.bitPhase)
- e.bitIndex = (e.bitIndex + steps) % len(e.bits)
- e.bitPhase -= float64(steps)
- }
-
- return value
- }
-
- func buildBits(cfg RDSConfig) []float64 {
- var bits []float64
- bits = append(bits, wordToBits(cfg.PI)...)
- status := uint8(cfg.PTY&0x1F) | boolToBit(cfg.TP)<<7 | boolToBit(cfg.TA)<<6
- bits = append(bits, byteToBits(status)...)
- bits = append(bits, stringToBits(cfg.PS)...)
- bits = append(bits, stringToBits(cfg.RT)...)
- return bits
- }
-
- func wordToBits(word uint16) []float64 {
- bits := make([]float64, 0, 16)
- for i := 15; i >= 0; i-- {
- bits = append(bits, bitToSymbol(uint8((word>>i)&1)))
- }
- return bits
- }
-
- func byteToBits(b uint8) []float64 {
- bits := make([]float64, 0, 8)
- for i := 7; i >= 0; i-- {
- bits = append(bits, bitToSymbol(uint8((b>>i)&1)))
- }
- return bits
- }
-
- func stringToBits(text string) []float64 {
- bits := make([]float64, 0, len(text)*8)
- for i := 0; i < len(text); i++ {
- for bit := 7; bit >= 0; bit-- {
- bits = append(bits, bitToSymbol(uint8((text[i]>>bit)&1)))
- }
- }
- return bits
- }
-
- func bitToSymbol(bit uint8) float64 {
- if bit == 0 {
- return -1
- }
- return 1
- }
-
- func boolToBit(value bool) uint8 {
- if value {
- return 1
- }
- return 0
- }
|