mirror of
https://github.com/chirpstack/chirpstack.git
synced 2025-06-21 08:39:55 +00:00
@ -172,6 +172,15 @@ pub fn run() {
|
|||||||
# ChirpStack will be allowed.
|
# ChirpStack will be allowed.
|
||||||
allow_unknown_gateways={{ gateway.allow_unknown_gateways }}
|
allow_unknown_gateways={{ gateway.allow_unknown_gateways }}
|
||||||
|
|
||||||
|
# RX timestamp max. drift.
|
||||||
|
#
|
||||||
|
# If the delta between the gateway reported RX timestamp vs ChirpStack
|
||||||
|
# server time is bigger than the configured value, then ChirpStack will
|
||||||
|
# ignore it. ChirpStack will then use the RX timestamp from the other
|
||||||
|
# receiving gateways, or failing that, will fall back onto the current
|
||||||
|
# server time.
|
||||||
|
rx_timestamp_max_drift="{{ gateway.rx_timestamp_max_drift }}"
|
||||||
|
|
||||||
|
|
||||||
# Network related configuration.
|
# Network related configuration.
|
||||||
[network]
|
[network]
|
||||||
|
@ -137,6 +137,8 @@ pub struct Gateway {
|
|||||||
pub ca_cert: String,
|
pub ca_cert: String,
|
||||||
pub ca_key: String,
|
pub ca_key: String,
|
||||||
pub allow_unknown_gateways: bool,
|
pub allow_unknown_gateways: bool,
|
||||||
|
#[serde(with = "humantime_serde")]
|
||||||
|
pub rx_timestamp_max_drift: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Gateway {
|
impl Default for Gateway {
|
||||||
@ -146,6 +148,7 @@ impl Default for Gateway {
|
|||||||
ca_cert: "".to_string(),
|
ca_cert: "".to_string(),
|
||||||
ca_key: "".to_string(),
|
ca_key: "".to_string(),
|
||||||
allow_unknown_gateways: false,
|
allow_unknown_gateways: false,
|
||||||
|
rx_timestamp_max_drift: Duration::from_secs(30),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,12 @@ use std::str::FromStr;
|
|||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, TimeDelta, Utc};
|
||||||
|
|
||||||
use crate::gpstime::ToDateTime;
|
|
||||||
use crate::region;
|
|
||||||
use chirpstack_api::{common, gw};
|
use chirpstack_api::{common, gw};
|
||||||
|
|
||||||
|
use crate::{config, gpstime::ToDateTime, region};
|
||||||
|
|
||||||
pub fn get_uplink_dr(
|
pub fn get_uplink_dr(
|
||||||
region_config_id: &str,
|
region_config_id: &str,
|
||||||
tx_info: &chirpstack_api::gw::UplinkTxInfo,
|
tx_info: &chirpstack_api::gw::UplinkTxInfo,
|
||||||
@ -54,6 +54,9 @@ pub fn get_uplink_ch(region_config_id: &str, frequency: u32, dr: u8) -> Result<u
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_rx_timestamp(rx_info: &[gw::UplinkRxInfo]) -> SystemTime {
|
pub fn get_rx_timestamp(rx_info: &[gw::UplinkRxInfo]) -> SystemTime {
|
||||||
|
let conf = config::get();
|
||||||
|
let rx_timestamp_max_drift = conf.gateway.rx_timestamp_max_drift;
|
||||||
|
|
||||||
// First search for time_since_gps_epoch.
|
// First search for time_since_gps_epoch.
|
||||||
for rxi in rx_info {
|
for rxi in rx_info {
|
||||||
if let Some(gps_time) = &rxi.time_since_gps_epoch {
|
if let Some(gps_time) = &rxi.time_since_gps_epoch {
|
||||||
@ -71,7 +74,14 @@ pub fn get_rx_timestamp(rx_info: &[gw::UplinkRxInfo]) -> SystemTime {
|
|||||||
if let Some(ts) = &rxi.gw_time {
|
if let Some(ts) = &rxi.gw_time {
|
||||||
let ts: Result<DateTime<Utc>> = (*ts).try_into().map_err(anyhow::Error::msg);
|
let ts: Result<DateTime<Utc>> = (*ts).try_into().map_err(anyhow::Error::msg);
|
||||||
if let Ok(ts) = ts {
|
if let Ok(ts) = ts {
|
||||||
return ts.into();
|
let mut delta = Utc::now() - ts;
|
||||||
|
if delta < TimeDelta::default() {
|
||||||
|
delta = -delta;
|
||||||
|
}
|
||||||
|
let delta = delta.to_std().unwrap_or_default();
|
||||||
|
if delta < rx_timestamp_max_drift {
|
||||||
|
return ts.into();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,6 +91,9 @@ pub fn get_rx_timestamp(rx_info: &[gw::UplinkRxInfo]) -> SystemTime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_rx_timestamp_chrono(rx_info: &[gw::UplinkRxInfo]) -> DateTime<Utc> {
|
pub fn get_rx_timestamp_chrono(rx_info: &[gw::UplinkRxInfo]) -> DateTime<Utc> {
|
||||||
|
let conf = config::get();
|
||||||
|
let rx_timestamp_max_drift = conf.gateway.rx_timestamp_max_drift;
|
||||||
|
|
||||||
// First search for time_since_gps_epoch.
|
// First search for time_since_gps_epoch.
|
||||||
for rxi in rx_info {
|
for rxi in rx_info {
|
||||||
if let Some(gps_time) = &rxi.time_since_gps_epoch {
|
if let Some(gps_time) = &rxi.time_since_gps_epoch {
|
||||||
@ -98,7 +111,14 @@ pub fn get_rx_timestamp_chrono(rx_info: &[gw::UplinkRxInfo]) -> DateTime<Utc> {
|
|||||||
if let Some(ts) = &rxi.gw_time {
|
if let Some(ts) = &rxi.gw_time {
|
||||||
let ts: Result<DateTime<Utc>> = (*ts).try_into().map_err(anyhow::Error::msg);
|
let ts: Result<DateTime<Utc>> = (*ts).try_into().map_err(anyhow::Error::msg);
|
||||||
if let Ok(ts) = ts {
|
if let Ok(ts) = ts {
|
||||||
return ts;
|
let mut delta = Utc::now() - ts;
|
||||||
|
if delta < TimeDelta::default() {
|
||||||
|
delta = -delta;
|
||||||
|
}
|
||||||
|
let delta = delta.to_std().unwrap_or_default();
|
||||||
|
if delta < rx_timestamp_max_drift {
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -188,3 +208,74 @@ pub fn set_uplink_modulation(
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_rx_timestamp_no_drift() {
|
||||||
|
let now = Utc::now();
|
||||||
|
let rx_info = gw::UplinkRxInfo {
|
||||||
|
gw_time: Some(now.try_into().unwrap()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let res: DateTime<Utc> = get_rx_timestamp(&[rx_info]).into();
|
||||||
|
assert_eq!(res, now);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_rx_timestamp_drift() {
|
||||||
|
let now = Utc::now() - chrono::Duration::seconds(60);
|
||||||
|
let rx_info = gw::UplinkRxInfo {
|
||||||
|
gw_time: Some(now.try_into().unwrap()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let res: DateTime<Utc> = get_rx_timestamp(&[rx_info]).into();
|
||||||
|
assert_ne!(res, now);
|
||||||
|
|
||||||
|
let now = Utc::now() + chrono::Duration::seconds(60);
|
||||||
|
let rx_info = gw::UplinkRxInfo {
|
||||||
|
gw_time: Some(now.try_into().unwrap()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let res: DateTime<Utc> = get_rx_timestamp(&[rx_info]).into();
|
||||||
|
assert_ne!(res, now);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_rx_timestamp_chrono_no_drift() {
|
||||||
|
let now = Utc::now();
|
||||||
|
let rx_info = gw::UplinkRxInfo {
|
||||||
|
gw_time: Some(now.try_into().unwrap()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = get_rx_timestamp_chrono(&[rx_info]);
|
||||||
|
assert_eq!(res, now);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_rx_timestamp_chrono_drift() {
|
||||||
|
let now = Utc::now() - chrono::Duration::seconds(60);
|
||||||
|
let rx_info = gw::UplinkRxInfo {
|
||||||
|
gw_time: Some(now.try_into().unwrap()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = get_rx_timestamp_chrono(&[rx_info]);
|
||||||
|
assert_ne!(res, now);
|
||||||
|
|
||||||
|
let now = Utc::now() + chrono::Duration::seconds(60);
|
||||||
|
let rx_info = gw::UplinkRxInfo {
|
||||||
|
gw_time: Some(now.try_into().unwrap()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = get_rx_timestamp_chrono(&[rx_info]);
|
||||||
|
assert_ne!(res, now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user