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 }