|
- package platform
-
- import (
- "context"
- "fmt"
- "sync"
-
- "github.com/jan/fm-rds-tx/internal/output"
- )
-
- // SoapyConfig exposes SoapySDR-specific knobs that drive hardware or simulated drivers.
- type SoapyConfig struct {
- output.BackendConfig
- Driver string
- Device string
- CenterFreqHz float64
- GainDB float64
- Channels []int
- DeviceArgs map[string]string
- Simulated bool
- SimulationPath string
- }
-
- // SoapyDriver is the low-level contract for talking to Soapy-style devices.
- type SoapyDriver interface {
- Name() string
- Configure(ctx context.Context, cfg SoapyConfig) error
- Write(ctx context.Context, frame *output.CompositeFrame) (int, error)
- Flush(ctx context.Context) error
- Close(ctx context.Context) error
- }
-
- // SoapyBackend wraps a driver and exposes the output.Backend interface.
- type SoapyBackend struct {
- mu sync.Mutex
- driver SoapyDriver
- cfg SoapyConfig
- info output.BackendInfo
- }
-
- // NewSoapyBackend returns an output-aware backend that drives the provided driver.
- func NewSoapyBackend(cfg SoapyConfig, driver SoapyDriver) *SoapyBackend {
- if driver == nil {
- driver = NewSimulatedDriver(nil)
- }
- info := output.BackendInfo{
- Name: fmt.Sprintf("soapy/%s", cfg.Driver),
- Description: "SoapySDR-friendly backend",
- Capabilities: output.BackendCapabilities{
- SupportsComposite: true,
- FixedRate: cfg.SampleRateHz > 0,
- MaxSamplesPerWrite: 8192,
- },
- }
- return &SoapyBackend{driver: driver, cfg: cfg, info: info}
- }
-
- // Configure propagates the latest backend config to the driver.
- func (sb *SoapyBackend) Configure(ctx context.Context, cfg output.BackendConfig) error {
- sb.mu.Lock()
- sb.cfg.BackendConfig = cfg
- sb.mu.Unlock()
- return sb.driver.Configure(ctx, sb.cfg)
- }
-
- // Write delegates to the driver.
- func (sb *SoapyBackend) Write(ctx context.Context, frame *output.CompositeFrame) (int, error) {
- return sb.driver.Write(ctx, frame)
- }
-
- // Flush asks the driver to drain any buffers.
- func (sb *SoapyBackend) Flush(ctx context.Context) error {
- return sb.driver.Flush(ctx)
- }
-
- // Close shuts down the driver cleanly.
- func (sb *SoapyBackend) Close(ctx context.Context) error {
- return sb.driver.Close(ctx)
- }
-
- // Info reports the configured backend metadata.
- func (sb *SoapyBackend) Info() output.BackendInfo {
- sb.mu.Lock()
- defer sb.mu.Unlock()
- return sb.info
- }
-
- // SimulatedDriver keeps samples in a downstream backend for testing without hardware.
- type SimulatedDriver struct {
- mu sync.Mutex
- fallback output.Backend
- cfg SoapyConfig
- }
-
- // NewSimulatedDriver uses the provided backend or falls back to an in-memory dummy.
- func NewSimulatedDriver(writer output.Backend) *SimulatedDriver {
- if writer == nil {
- writer = output.NewDummyBackend("simulated-soapy")
- }
- return &SimulatedDriver{fallback: writer}
- }
-
- // Name returns the runtime label of the simulated driver.
- func (sd *SimulatedDriver) Name() string {
- return sd.fallback.Info().Name
- }
-
- // Configure pushes the SoapyConfig into the fallback backend.
- func (sd *SimulatedDriver) Configure(ctx context.Context, cfg SoapyConfig) error {
- sd.mu.Lock()
- sd.cfg = cfg
- sd.mu.Unlock()
- return sd.fallback.Configure(ctx, cfg.BackendConfig)
- }
-
- // Write simply plants the frame into the fallback pipeline.
- func (sd *SimulatedDriver) Write(ctx context.Context, frame *output.CompositeFrame) (int, error) {
- return sd.fallback.Write(ctx, frame)
- }
-
- // Flush is delegated.
- func (sd *SimulatedDriver) Flush(ctx context.Context) error {
- return sd.fallback.Flush(ctx)
- }
-
- // Close finalizes the fallback backend.
- func (sd *SimulatedDriver) Close(ctx context.Context) error {
- return sd.fallback.Close(ctx)
- }
|