Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

57 lines
1.8KB

  1. package app
  2. import (
  3. "context"
  4. "encoding/binary"
  5. "fmt"
  6. "path/filepath"
  7. "time"
  8. cfgpkg "github.com/jan/fm-rds-tx/internal/config"
  9. offpkg "github.com/jan/fm-rds-tx/internal/offline"
  10. "github.com/jan/fm-rds-tx/internal/output"
  11. "github.com/jan/fm-rds-tx/internal/platform"
  12. )
  13. func RunSimulatedTransmit(cfg cfgpkg.Config, outPath string, duration time.Duration) (string, error) {
  14. if outPath == "" {
  15. outPath = filepath.Join("build", "sim", "simulated-soapy.iqf32")
  16. }
  17. fileBackend, err := output.NewFileBackend(outPath, binary.LittleEndian, output.BackendInfo{
  18. Name: "simulated-soapy-file",
  19. Description: "simulated soapy sink to file",
  20. })
  21. if err != nil {
  22. return "", err
  23. }
  24. defer fileBackend.Close(context.Background())
  25. soapyCfg := platform.SoapyConfig{
  26. BackendConfig: output.BackendConfig{
  27. SampleRateHz: float64(cfg.FM.CompositeRateHz),
  28. Channels: 2,
  29. IQLevel: float32(cfg.FM.OutputDrive),
  30. },
  31. Driver: "simulated",
  32. Device: cfg.Backend.Device,
  33. CenterFreqHz: cfg.FM.FrequencyMHz * 1_000_000,
  34. Simulated: true,
  35. SimulationPath: outPath,
  36. }
  37. backend := platform.NewSoapyBackend(soapyCfg, platform.NewSimulatedDriver(fileBackend))
  38. if err := backend.Configure(context.Background(), soapyCfg.BackendConfig); err != nil {
  39. return "", err
  40. }
  41. gen := offpkg.NewGenerator(cfg)
  42. frame := gen.GenerateFrame(duration)
  43. if _, err := backend.Write(context.Background(), frame); err != nil {
  44. return "", err
  45. }
  46. if err := backend.Flush(context.Background()); err != nil {
  47. return "", err
  48. }
  49. return fmt.Sprintf("simulated transmit: backend=%s output=%s duration=%s", backend.Info().Name, outPath, duration), nil
  50. }