Skip to main content

openipc_core/
channel.rs

1/// OpenIPC's observed default link id from the reference browser receiver.
2///
3/// The Zig project notes this as the SHA1-derived id for
4/// `link_domain = "default"`.
5pub const DEFAULT_LINK_ID: u32 = 7_669_206;
6
7/// Low-byte port selector inside an OpenIPC/WFB channel id.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub enum RadioPort {
10    /// Air-unit to ground-station video RTP downlink.
11    Video,
12    /// Air-unit to ground-station telemetry downlink.
13    ///
14    /// OpenIPC commonly carries MAVLink or MSP/OSD-style telemetry here,
15    /// depending on the transmitter-side telemetry router.
16    TelemetryRx,
17    /// Ground-station to air-unit telemetry uplink.
18    TelemetryTx,
19    /// Air-unit to ground-station tunnel/data downlink.
20    TunnelRx,
21    /// Ground-station to air-unit tunnel/data uplink.
22    ///
23    /// Adaptive-link feedback is sent over this path in aviateur, PixelPilot,
24    /// and current OpenIPC firmware setups.
25    TunnelTx,
26    /// Air-unit to ground-station audio profile downlink.
27    AudioRx,
28    /// Ground-station to air-unit audio profile uplink.
29    AudioTx,
30    /// Legacy alias for [`RadioPort::TelemetryRx`].
31    #[deprecated(note = "use RadioPort::TelemetryRx; port 0x10 is telemetry, not always MAVLink")]
32    MavlinkRx,
33    /// Legacy alias for [`RadioPort::TunnelTx`].
34    ///
35    /// Earlier openipc-rs builds used this for adaptive-link feedback. The
36    /// wire value remains `0xa0`, but the accurate OpenIPC name is tunnel/data
37    /// uplink. Use [`RadioPort::TelemetryTx`] when you mean telemetry port
38    /// `0x90`.
39    #[deprecated(
40        note = "use RadioPort::TunnelTx for adaptive-link or RadioPort::TelemetryTx for telemetry uplink"
41    )]
42    MavlinkTx,
43    /// Legacy alias for [`RadioPort::TunnelRx`].
44    #[deprecated(note = "use RadioPort::TunnelRx")]
45    DataRx,
46    /// Caller-defined radio port for custom payload channels.
47    Custom(u8),
48}
49
50impl RadioPort {
51    /// Return the low byte used in an OpenIPC/WFB channel id.
52    #[allow(deprecated)]
53    pub const fn as_u8(self) -> u8 {
54        match self {
55            Self::Video => 0,
56            Self::TelemetryRx | Self::MavlinkRx => 0x10,
57            Self::TelemetryTx => 0x90,
58            Self::TunnelRx | Self::DataRx => 0x20,
59            Self::TunnelTx | Self::MavlinkTx => 0xa0,
60            Self::AudioRx => 0x30,
61            Self::AudioTx => 0xb0,
62            Self::Custom(value) => value,
63        }
64    }
65}
66
67/// OpenIPC/WFB logical channel id.
68///
69/// The high 24 bits are the link id and the low byte is the radio port.
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
71pub struct ChannelId(u32);
72
73impl ChannelId {
74    /// Wrap a raw 32-bit channel id.
75    pub const fn new(raw: u32) -> Self {
76        Self(raw)
77    }
78
79    /// Build a channel id from a link id and radio port.
80    pub const fn from_link_port(link_id: u32, port: RadioPort) -> Self {
81        Self((link_id << 8) | port.as_u8() as u32)
82    }
83
84    /// Return the default OpenIPC video channel.
85    pub const fn default_video() -> Self {
86        Self::from_link_port(DEFAULT_LINK_ID, RadioPort::Video)
87    }
88
89    /// Return the raw big-endian channel id value.
90    pub const fn raw(self) -> u32 {
91        self.0
92    }
93
94    /// Return the raw channel id encoded for 802.11 address fields.
95    pub const fn to_be_bytes(self) -> [u8; 4] {
96        self.0.to_be_bytes()
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn default_video_channel_matches_reference_value() {
106        let id = ChannelId::default_video();
107        assert_eq!(id.raw(), (DEFAULT_LINK_ID << 8));
108        assert_eq!(id.to_be_bytes(), id.raw().to_be_bytes());
109    }
110
111    #[test]
112    fn radio_ports_match_openipc_ground_station_conventions() {
113        assert_eq!(RadioPort::Video.as_u8(), 0x00);
114        assert_eq!(RadioPort::TelemetryRx.as_u8(), 0x10);
115        assert_eq!(RadioPort::TunnelRx.as_u8(), 0x20);
116        assert_eq!(RadioPort::AudioRx.as_u8(), 0x30);
117        assert_eq!(RadioPort::TelemetryTx.as_u8(), 0x90);
118        assert_eq!(RadioPort::TunnelTx.as_u8(), 0xa0);
119        assert_eq!(RadioPort::AudioTx.as_u8(), 0xb0);
120    }
121}