#!/usr/bin/env python3
"""
modes_downlink.py - Stream Mode S downlink frames from a GRX receiver.

This example subscribes to Mode S downlink frames (SSR replies and
squitters) with optional downlink format filtering. It prints each
received frame with its metadata.

Usage:
    python modes_downlink.py [HOST]

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

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

import binascii
import sys

import grpc
import pyModeS as pms

# Import the generated gRPC stubs for the Receiver API
import Receiverd_pb2 as receiverd_pb2
import Receiverd_pb2_grpc

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

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

# Only subscribe to ADS-B (DF17) and All-Call replies (DF11).
# Set to list(range(25)) for all downlink formats.
DOWNLINK_FORMATS = [11, 17]

# ---------------------------------------------------------------------------
# Connect and subscribe
# ---------------------------------------------------------------------------

print(f"Connecting to {host}:{RECEIVERD_PORT}...")
channel = grpc.insecure_channel(f"{host}:{RECEIVERD_PORT}")
receiverd = Receiverd_pb2_grpc.ReceiverdStub(channel)

# Build the subscription request with the desired downlink format filter.
# The downlink_formats field controls which DFs are delivered in the stream.
request = receiverd_pb2.GetModeSDownlinkFramesRequest(
    downlink_formats=DOWNLINK_FORMATS
)

# GetModeSDownlinkFrames returns a server-side streaming RPC.
# Each element in the stream is a ModeSDownlinkFrameWithStreamInfo message.
stream = receiverd.GetModeSDownlinkFrames(request)

print(f"Subscribed to Mode S downlink formats {DOWNLINK_FORMATS}. Press Ctrl+C to stop.\n")

# ---------------------------------------------------------------------------
# Process incoming frames
# ---------------------------------------------------------------------------

last_dropped = 0
try:
    for reply in stream:
        # Check for dropped frames (indicates network overload)
        if reply.frames_dropped > last_dropped:
            dropped = reply.frames_dropped - last_dropped
            print(f"  ** WARNING: {dropped} frame(s) dropped (network overload) **")
            last_dropped = reply.frames_dropped

        frame = reply.frame

        # Convert raw payload bytes to hex string for decoding
        payload_hex = binascii.hexlify(frame.payload).decode("ascii")

        # Use pyModeS to extract the downlink format and ICAO address
        df = pms.df(payload_hex)
        icao = pms.icao(payload_hex)

        print(f"DF{df:2d}  ICAO={icao}  "
              f"Signal={frame.level_signal:.1f} dBm  "
              f"Noise={frame.level_noise:.1f} dBm  "
              f"CFO={frame.carrier_frequency_offset:+.0f} Hz  "
              f"Payload={payload_hex}")

except KeyboardInterrupt:
    print("\nStopped.")

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

stream.cancel()
channel.close()
