Skip to content

Commit

Permalink
Update RelayPath to include RSSI and SNR.
Browse files Browse the repository at this point in the history
  • Loading branch information
brocaar committed Jun 25, 2024
1 parent a9af8d9 commit 062bf25
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 29 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"usage",
"derive",
] }
chirpstack_api = { version = "4.9.0-test.1", default-features = false }
chirpstack_api = { version = "4.9.0-test.2", default-features = false }
lrwn_filters = { version = "4.7", features = ["serde"] }
log = "0.4"
simple_logger = "5.0"
Expand Down
24 changes: 20 additions & 4 deletions src/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,19 +166,31 @@ async fn proxy_heartbeat_mesh_packet(pl: &gw::UplinkFrame, packet: MeshPacket) -
packet
);

let heartbeat_pl = gw::MeshStats {
let heartbeat_pl = gw::MeshHeartbeat {
gateway_id: hex::encode(backend::get_gateway_id().await?),
relay_id: hex::encode(mesh_pl.relay_id),
relay_path: mesh_pl.relay_path.iter().map(hex::encode).collect(),
relay_path: mesh_pl
.relay_path
.iter()
.map(|v| gw::MeshHeartbeatRelayPath {
relay_id: hex::encode(&v.relay_id),
rssi: v.rssi.into(),
snr: v.snr.into(),
})
.collect(),
time: Some(mesh_pl.timestamp.into()),
};

proxy::send_mesh_heartbeat(&heartbeat_pl).await
}

async fn relay_mesh_packet(_: &gw::UplinkFrame, mut packet: MeshPacket) -> Result<()> {
async fn relay_mesh_packet(pl: &gw::UplinkFrame, mut packet: MeshPacket) -> Result<()> {
let conf = config::get();
let relay_id = backend::get_relay_id().await?;
let rx_info = pl
.rx_info
.as_ref()
.ok_or_else(|| anyhow!("rx_info is None"))?;

match &mut packet.payload {
packets::Payload::Uplink(pl) => {
Expand Down Expand Up @@ -237,7 +249,11 @@ async fn relay_mesh_packet(_: &gw::UplinkFrame, mut packet: MeshPacket) -> Resul
}

// Add our Relay ID to the path.
pl.relay_path.push(relay_id);
pl.relay_path.push(packets::RelayPath {
relay_id,
rssi: rx_info.rssi as i16,
snr: rx_info.snr as i8,
});
}
}

Expand Down
105 changes: 92 additions & 13 deletions src/packets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ impl DownlinkMetadata {
pub struct HeartbeatPayload {
pub timestamp: SystemTime,
pub relay_id: [u8; 4],
pub relay_path: Vec<[u8; 4]>,
pub relay_path: Vec<RelayPath>,
}

impl HeartbeatPayload {
Expand All @@ -429,8 +429,8 @@ impl HeartbeatPayload {
return Err(anyhow!("At least 8 bytes are expected"));
}

if (b.len() - 8) % 4 != 0 {
return Err(anyhow!("Invalid amoutn of Relay path bytes"));
if (b.len() - 8) % 6 != 0 {
return Err(anyhow!("Invalid amount of Relay path bytes"));
}

let mut ts_b: [u8; 4] = [0; 4];
Expand All @@ -443,12 +443,12 @@ impl HeartbeatPayload {
let mut relay_id: [u8; 4] = [0; 4];
relay_id.copy_from_slice(&b[4..8]);

let relay_path: Vec<[u8; 4]> = b[8..]
.chunks(4)
let relay_path: Vec<RelayPath> = b[8..]
.chunks(6)
.map(|v| {
let mut relay_id: [u8; 4] = [0; 4];
relay_id.copy_from_slice(v);
relay_id
let mut b: [u8; 6] = [0; 6];
b.copy_from_slice(v);
RelayPath::from_bytes(b)
})
.collect();

Expand All @@ -464,12 +464,67 @@ impl HeartbeatPayload {
let mut b = timestamp.to_be_bytes().to_vec();
b.extend_from_slice(&self.relay_id);
for relay_path in &self.relay_path {
b.extend_from_slice(relay_path);
b.extend_from_slice(&relay_path.to_bytes()?);
}
Ok(b)
}
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct RelayPath {
pub relay_id: [u8; 4],
pub rssi: i16,
pub snr: i8,
}

impl RelayPath {
pub fn from_bytes(b: [u8; 6]) -> Self {
let mut relay_id = [0; 4];
relay_id.copy_from_slice(&b[0..4]);

let snr = b[5] & 0x3f;
let snr = if snr > 31 {
(snr as i8) - 64
} else {
snr as i8
};

RelayPath {
relay_id,
snr,
rssi: -(b[4] as i16),
}
}

pub fn to_bytes(&self) -> Result<[u8; 6]> {
if self.rssi > 0 {
return Err(anyhow!("Max rssi value is 0"));
}
if self.rssi < -255 {
return Err(anyhow!("Min rssi value is -255"));
}
if self.snr < -32 {
return Err(anyhow!("Min snr value is -32"));
}
if self.snr > 31 {
return Err(anyhow!("Max snr value is 31"));
}

Ok([
self.relay_id[0],
self.relay_id[1],
self.relay_id[2],
self.relay_id[3],
-self.rssi as u8,
if self.snr < 0 {
(self.snr + 64) as u8
} else {
self.snr as u8
},
])
}
}

pub fn encode_freq(freq: u32) -> Result<[u8; 3]> {
let mut freq = freq;
// Support LoRaWAN 2.4GHz, in which case the stepping is 200Hz:
Expand Down Expand Up @@ -964,15 +1019,28 @@ mod test {

#[test]
fn test_heartbeat_payload_from_slice() {
let b = vec![59, 154, 202, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
let b = vec![
59, 154, 202, 0, 1, 2, 3, 4, 5, 6, 7, 8, 120, 52, 9, 10, 11, 12, 120, 52,
];
let heartbeat_pl = HeartbeatPayload::from_slice(&b).unwrap();
assert_eq!(
HeartbeatPayload {
timestamp: UNIX_EPOCH
.checked_add(Duration::from_secs(1_000_000_000))
.unwrap(),
relay_id: [1, 2, 3, 4],
relay_path: vec![[5, 6, 7, 8], [9, 10, 11, 12]],
relay_path: vec![
RelayPath {
relay_id: [5, 6, 7, 8],
rssi: -120,
snr: -12,
},
RelayPath {
relay_id: [9, 10, 11, 12],
rssi: -120,
snr: -12,
},
],
},
heartbeat_pl,
);
Expand All @@ -985,11 +1053,22 @@ mod test {
.checked_add(Duration::from_secs(1_000_000_000))
.unwrap(),
relay_id: [1, 2, 3, 4],
relay_path: vec![[5, 6, 7, 8], [9, 10, 11, 12]],
relay_path: vec![
RelayPath {
relay_id: [5, 6, 7, 8],
rssi: -120,
snr: -12,
},
RelayPath {
relay_id: [9, 10, 11, 12],
rssi: -120,
snr: -12,
},
],
};
let b = heartbeat_pl.to_vec().unwrap();
assert_eq!(
vec![59, 154, 202, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
vec![59, 154, 202, 0, 1, 2, 3, 4, 5, 6, 7, 8, 120, 52, 9, 10, 11, 12, 120, 52],
b
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ pub async fn send_stats(pl: &gw::GatewayStats) -> Result<()> {
Ok(())
}

pub async fn send_mesh_heartbeat(pl: &gw::MeshStats) -> Result<()> {
pub async fn send_mesh_heartbeat(pl: &gw::MeshHeartbeat) -> Result<()> {
info!("Sending mesh heartbeat event");

let event_chan = EVENT_CHAN
Expand Down
36 changes: 29 additions & 7 deletions tests/border_gateway_mesh_heartbeat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,18 @@ async fn test_border_gateway_mesh_heartbeat() {
payload: packets::Payload::Heartbeat(packets::HeartbeatPayload {
relay_id: [2, 2, 2, 2],
timestamp: UNIX_EPOCH,
relay_path: vec![[1, 2, 3, 4], [5, 6, 7, 8]],
relay_path: vec![
packets::RelayPath {
relay_id: [1, 2, 3, 4],
rssi: -120,
snr: -12,
},
packets::RelayPath {
relay_id: [5, 6, 7, 8],
rssi: -120,
snr: -12,
},
],
}),
mic: None,
};
Expand Down Expand Up @@ -70,24 +81,35 @@ async fn test_border_gateway_mesh_heartbeat() {
.unwrap();
}

// We expect to receive the MeshStats to be received by the forwarder.
let mesh_stats: gw::MeshStats = {
// We expect to receive the MeshHeartbeat to be received by the forwarder.
let mesh_heartbeat: gw::MeshHeartbeat = {
let mut event_sock = common::FORWARDER_EVENT_SOCK.get().unwrap().lock().await;
let msg = event_sock.recv().await.unwrap();

let cmd = String::from_utf8(msg.get(0).map(|v| v.to_vec()).unwrap()).unwrap();
assert_eq!("mesh_heartbeat", cmd);

gw::MeshStats::decode(msg.get(1).cloned().unwrap()).unwrap()
gw::MeshHeartbeat::decode(msg.get(1).cloned().unwrap()).unwrap()
};

assert_eq!(
gw::MeshStats {
gw::MeshHeartbeat {
gateway_id: "0101010101010101".to_string(),
time: Some(UNIX_EPOCH.into()),
relay_id: "02020202".to_string(),
relay_path: vec!["01020304".to_string(), "05060708".to_string(),],
relay_path: vec![
gw::MeshHeartbeatRelayPath {
relay_id: "01020304".into(),
rssi: -120,
snr: -12,
},
gw::MeshHeartbeatRelayPath {
relay_id: "05060708".into(),
rssi: -120,
snr: -12,
},
],
},
mesh_stats
mesh_heartbeat
);
}
6 changes: 5 additions & 1 deletion tests/relay_gateway_relay_mesh_heartbeat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,11 @@ async fn test_relay_gateway_relay_mesh_heartbeat() {

packet.mhdr.hop_count += 1;
if let packets::Payload::Heartbeat(v) = &mut packet.payload {
v.relay_path.push([2, 2, 2, 2]);
v.relay_path.push(packets::RelayPath {
relay_id: [2, 2, 2, 2],
rssi: -60,
snr: 12,
});
}
packet.set_mic(Aes128Key::null()).unwrap();

Expand Down

0 comments on commit 062bf25

Please sign in to comment.