|
- package rds
-
- import (
- "math"
- )
-
- // Decoder performs a simple RDS baseband decode (BPSK, 1187.5 bps).
- type Decoder struct {
- lastPS string
- lastPI uint16
- }
-
- type Result struct {
- PI uint16 `json:"pi"`
- PS string `json:"ps"`
- }
-
- // Decode takes baseband samples at ~2400 Hz and attempts to extract PI/PS.
- func (d *Decoder) Decode(base []float32, sampleRate int) Result {
- if len(base) == 0 || sampleRate <= 0 {
- return Result{}
- }
- // crude clock: 1187.5 bps
- baud := 1187.5
- spb := float64(sampleRate) / baud
- // carrier recovery simplified: assume baseband already mixed
- bits := make([]int, 0, int(float64(len(base))/spb))
- phase := 0.0
- for i := 0; i < len(base); i++ {
- phase += 1.0
- if phase >= spb {
- phase -= spb
- // slice decision
- v := base[i]
- if v >= 0 {
- bits = append(bits, 1)
- } else {
- bits = append(bits, 0)
- }
- }
- }
- // parse groups (very naive): look for 16-bit blocks and decode group type 0A for PS
- // This is a placeholder: real RDS needs CRC and block sync.
- if len(bits) < 104 {
- return Result{PI: d.lastPI, PS: d.lastPS}
- }
- // best effort: just map first 16 bits to PI and next 8 chars from consecutive bytes
- pi := bitsToU16(bits[0:16])
- ps := decodePS(bits)
- if pi != 0 {
- d.lastPI = pi
- }
- if ps != "" {
- d.lastPS = ps
- }
- return Result{PI: d.lastPI, PS: d.lastPS}
- }
-
- func bitsToU16(bits []int) uint16 {
- var v uint16
- for _, b := range bits {
- v = (v << 1) | uint16(b&1)
- }
- return v
- }
-
- func decodePS(bits []int) string {
- // naive: take next 64 bits as 8 ASCII chars
- if len(bits) < 16+64 {
- return ""
- }
- start := 16
- out := make([]rune, 0, 8)
- for i := 0; i < 8; i++ {
- var c byte
- for j := 0; j < 8; j++ {
- c = (c << 1) | byte(bits[start+i*8+j]&1)
- }
- if c < 32 || c > 126 {
- c = ' '
- }
- out = append(out, rune(c))
- }
- // trim
- for len(out) > 0 && out[len(out)-1] == ' ' {
- out = out[:len(out)-1]
- }
- return string(out)
- }
-
- // BPSKCostas returns a simple carrier-locked version of baseband (placeholder).
- func BPSKCostas(in []float32) []float32 {
- out := make([]float32, len(in))
- var phase float64
- for i, v := range in {
- phase += 0.0001 * float64(v) * math.Sin(phase)
- out[i] = float32(float64(v) * math.Cos(phase))
- }
- return out
- }
|