#grpc-client #communication #spacex #user #endpoint #terminal #space-x

starlink

Rust 客户端实现,用于 SpaceX Starlink 用户终端暴露的 gRPC 端点

6 个版本 (1 个稳定版)

1.0.0 2023 年 12 月 14 日
0.3.1 2022 年 12 月 25 日
0.3.0 2021 年 7 月 19 日
0.2.0 2021 年 7 月 17 日
0.1.1 2021 年 5 月 4 日

#1 in #spacex

MIT/Apache

19KB

starlink-rs

CI crates.io docs.rs license: MIT/Apache-2.0

Rust 客户端实现,用于 SpaceX Starlink 用户终端暴露的 gRPC 端点。

免责声明:如果您是 Elon 或其他 SpaceX 相关权威人士,并且不想存在此内容,请直接联系我。

背景

Starlink 天线在其本地网络下 192.168.100.1:9200 暴露了一个未经身份验证的 gRPC HTTP/2 服务器,允许进行 服务器反射。这包含从其中反转出来的(可能仍然有缺陷的)Protobuf 定义以及一个能够与天线通信的 Rust 客户端实现,用于科学研究。

天线暴露了两种方法(据我所知);一种用于请求/响应,另一种用于流式传输

service Device {
    rpc Handle (.SpaceX.API.Device.Request) returns (.SpaceX.API.Device.Response) {}
    rpc Stream (stream .SpaceX.API.Device.ToDevice) returns (stream .SpaceX.API.Device.FromDevice) {}
}

示例用法

请求/响应和流式通信的工作示例在 示例目录 中。

请求 / 响应

use starlink::proto::space_x::api::device::{device_client::DeviceClient, request, GetStatusRequest, Request};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = DeviceClient::connect("http://192.168.100.1:9200").await?;

    let request = tonic::Request::new(Request {
        id: None,
        epoch_id: None,
        target_id: None,
        request: Some(request::Request::GetStatus(GetStatusRequest {})),
    });

    let response = client.handle(request).await?;

    dbg!(response);

    Ok(())
}
cargo run --example request_response

打印类似以下内容

Response {
    metadata: MetadataMap {
        headers: {
            "content-type": "application/grpc",
            "grpc-status": "0",
            "grpc-message": "",
        },
    },
    message: Response {
        id: None,
        status: None,
        response: Some(
            DishGetStatus(
                DishGetStatusResponse {
                    device_info: Some(
                        DeviceInfo {
                            id: Some(
                                "<my-ID>",
                            ),
                            hardware_version: Some(
                                "rev1_pre_production",
                            ),
                            software_version: Some(
                                "1f86ec34-34ea-4e7a-9758-3842e72422fb.release",
                            ),
                            country_code: Some(
                                "DE",
                            ),
                            utc_offset_s: Some(
                                1,
                            ),
                        },
                    ),
                    device_state: Some(
                        DeviceState {
                            uptime_s: Some(
                                26115,
                            ),
                        },
                    ),
                    state: Some(
                        Connected,
                    ),
                    alerts: Some(
                        DishAlerts {
                            motors_stuck: None,
                            thermal_throttle: None,
                            thermal_shutdown: None,
                            mast_not_near_vertical: None,
                            unexpected_location: None,
                            slow_ethernet_speeds: None,
                        },
                    ),
                    snr: Some(
                        6.0,
                    ),
                    seconds_to_first_nonempty_slot: None,
                    pop_ping_drop_rate: None,
                    downlink_throughput_bps: Some(
                        8584784.0,
                    ),
                    uplink_throughput_bps: Some(
                        311510.97,
                    ),
                    pop_ping_latency_ms: Some(
                        38.857143,
                    ),
                    obstruction_stats: Some(
                        DishObstructionStats {
                            currently_obstructed: None,
                            fraction_obstructed: Some(
                                0.0010516815,
                            ),
                            last_24h_obstructed_s: Some(
                                72.0,
                            ),
                            valid_s: Some(
                                21260.67,
                            ),
                            wedge_fraction_obstructed: [
                                1.0,
                                0.0,
                                0.0,
                                0.0,
                                0.0,
                                0.0,
                                0.0,
                                0.0,
                                0.0,
                                0.0,
                                0.0,
                                0.0,
                            ],
                            wedge_abs_fraction_obstructed: [
                                0.0010516815,
                                0.0,
                                0.0,
                                0.0,
                                0.0,
                                0.0,
                                0.0,
                                0.0,
                                0.0,
                                0.0,
                                0.0,
                                0.0,
                            ],
                        },
                    ),
                },
            ),
        ),
    },
}

流式传输

use async_stream::stream;
use std::time::Duration;
use tokio::time::sleep;

use starlink::proto::space_x::api::device::{
    device_client::DeviceClient,
    request,
    to_device,
    GetStatusRequest,
    Request,
    ToDevice,
};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = DeviceClient::connect("http://192.168.100.1:9200").await?;

    let request_stream = stream! {
        loop {
            yield ToDevice {
                message: Some(to_device::Message::Request(Request {
                    id: None,
                    epoch_id: None,
                    target_id: None,
                    request: Some(request::Request::GetStatus(GetStatusRequest {})),
                })),
            };

            sleep(Duration::from_secs(1)).await;
        }
    };

    let request = tonic::Request::new(request_stream);

    let mut response_stream = client.stream(request).await?.into_inner();

    while let Some(message) = response_stream.message().await? {
        dbg!(message);
    }

    Ok(())
}
cargo run --example streaming

开发

Protobuf 代码生成由工作空间中的 codegen crate 处理。生成的 Protobuf 文件已提交。要运行代码生成,请执行

cargo run --package starlink-codegen

许可证

starlink-rs 根据 Apache 许可证 2.0 版或 MIT 许可证进行许可。

您可选择。

依赖项

~4–6MB
~98K SLoC