#!/usr/bin/env python3
"""
tunable_channel.py - Configure and query tunable radio channels on a GRX receiver.

This example uses the Tunable Channel API to list available tunable
channels, query their current settings (frequency, bandwidth, sample
rate, gain), and optionally tune to a new frequency. This API is only
available on receivers equipped with a tunable SDR-like daughterboard.

Usage:
    python tunable_channel.py [HOST]

Arguments:
    HOST    IP address or hostname of the GRX (default: YOUR_GRX_IP)

Prerequisites:
    pip install grpcio grpcio-tools
    Run gen_pb.sh to generate the Python bindings.

Note:
    The Tunable Channel API is only available on GRX receivers with a
    tunable radio front-end (BAND_TUNABLE). Calling these methods on
    a receiver without tunable channels will return INVALID_ARGUMENT.
"""

import sys

import grpc
from google.protobuf.empty_pb2 import Empty

# Import the generated gRPC stubs
import Common_pb2 as common_pb2
import TunableChanneld_pb2 as tunablechanneld_pb2
import TunableChanneld_pb2_grpc

# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------

host = sys.argv[1] if len(sys.argv) >= 2 else "YOUR_GRX_IP"

# The Tunable Channel API listens on TCP port 5309
TUNABLECHANNELD_PORT = 5309

# ---------------------------------------------------------------------------
# Connect to the GRX receiver
# ---------------------------------------------------------------------------

print(f"Connecting to {host}:{TUNABLECHANNELD_PORT}...")
channel = grpc.insecure_channel(f"{host}:{TUNABLECHANNELD_PORT}")
tunabled = TunableChanneld_pb2_grpc.TunableChanneldStub(channel)

# ---------------------------------------------------------------------------
# List available tunable channels
# ---------------------------------------------------------------------------

try:
    channels_reply = tunabled.GetChannels(Empty())
except grpc.RpcError as e:
    print(f"Error: {e.details()}")
    print("This receiver may not have tunable channels.")
    channel.close()
    sys.exit(1)

print(f"\nFound {len(channels_reply.channels)} tunable channel(s):\n")

# ---------------------------------------------------------------------------
# Query settings for each tunable channel
# ---------------------------------------------------------------------------

for ch in channels_reply.channels:
    # Build RadioIdentification for this channel
    radio_id = common_pb2.RadioIdentification(
        band=ch.band,
        per_band_index=ch.per_band_index,
    )

    print(f"  Channel: band={ch.band} index={ch.per_band_index}")

    # Query current center frequency
    freq = tunabled.GetCenterFrequency(
        tunablechanneld_pb2.GetCenterFrequencyRequest(channel=radio_id)
    )
    print(f"    Center frequency:   {freq.center_frequency_hz / 1e6:.3f} MHz")

    # Query current analog bandwidth
    bw = tunabled.GetAnalogBandwidth(
        tunablechanneld_pb2.GetAnalogBandwidthRequest(channel=radio_id)
    )
    print(f"    Analog bandwidth:   {bw.analog_bandwidth_hz / 1e6:.3f} MHz")

    # Query current sample rate
    sr = tunabled.GetSampleRate(
        tunablechanneld_pb2.GetSampleRateRequest(channel=radio_id)
    )
    print(f"    Sample rate:        {sr.sample_rate_sps / 1e6:.3f} MSps")

    # Query current hardware gain
    gain = tunabled.GetHardwareGain(
        tunablechanneld_pb2.GetHardwareGainRequest(channel=radio_id)
    )
    print(f"    Hardware gain:      {gain.hardware_gain_db} dB")

    # List available RX ports
    ports = tunabled.GetRXPorts(
        tunablechanneld_pb2.GetRXPortsRequest(channel=radio_id)
    )
    if ports.rx_port_to_label:
        print(f"    RX ports:")
        for port_id, label in ports.rx_port_to_label.items():
            print(f"      Port {port_id}: {label}")

    # Get currently active RX port
    current_port = tunabled.GetRXPort(
        tunablechanneld_pb2.GetRXPortRequest(channel=radio_id)
    )
    print(f"    Active RX port:     {current_port.rx_port}")
    print()

# ---------------------------------------------------------------------------
# Example: tune to a new frequency (commented out by default)
# ---------------------------------------------------------------------------

# To change the center frequency of a tunable channel, uncomment the
# following code and adjust the target frequency and channel ID:
#
# target_radio_id = common_pb2.RadioIdentification(
#     band=common_pb2.BAND_TUNABLE,
#     per_band_index=0,
# )
#
# tunabled.SetCenterFrequency(
#     tunablechanneld_pb2.SetCenterFrequencyRequest(
#         channel=target_radio_id,
#         center_frequency_hz=433_000_000,  # 433 MHz
#     )
# )
# print("Tuned to 433 MHz.")

# ---------------------------------------------------------------------------
# Clean up
# ---------------------------------------------------------------------------

channel.close()
