Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f8769012b
|
||
|
|
57891ed230
|
||
|
|
fce4946369
|
No files matched your search
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "vivaldi-nc"
|
name = "vivaldi-nc"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Stephen Waits <steve@waits.net>"]
|
authors = ["Stephen Waits <steve@waits.net>"]
|
||||||
description = "Vivaldi network coordinates with a simple interface."
|
description = "Vivaldi network coordinates with a simple interface."
|
||||||
|
|||||||
@@ -114,24 +114,36 @@ usage examples.
|
|||||||
|
|
||||||
The repository includes an example which loads a n-to-n latency sample from
|
The repository includes an example which loads a n-to-n latency sample from
|
||||||
PlanetLab and iterates on `NetworkCoordinate`s until some low-enough mean error
|
PlanetLab and iterates on `NetworkCoordinate`s until some low-enough mean error
|
||||||
is reached. The output is a JSON array of elements that look like this:
|
is reached. The output is a JSON array of elements which contain the NC's
|
||||||
|
position, its height (or stem latency estimate), and its estimation of error
|
||||||
|
(lower is better) that look like this:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"position": [
|
"position": [
|
||||||
{
|
5.563593,
|
||||||
"inner": [ # Euclidean position (3D in this case):
|
-2.332495,
|
||||||
9.249476, # first dimension; "x"
|
7.3957834
|
||||||
23.747225, # second dimension; "x"
|
],
|
||||||
-6.473008 # third dimension; "x"
|
"height": 55.56651,
|
||||||
]
|
"error": 3.241348
|
||||||
},
|
}
|
||||||
72.10599 # Height component (estimate of stem latency in milliseconds)
|
|
||||||
],
|
|
||||||
"error": 2.9166403 # Estimated error (lower is better)
|
|
||||||
},
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
Adding some detail to these three fields:
|
||||||
|
|
||||||
|
- `position`: Estimated position of this node (or `NetworkCoordinate`) on the
|
||||||
|
network core. Think of this like the node's endpoint into the Internet
|
||||||
|
backbone. This is a cartesian coordinate in a multi-dimensional latency space
|
||||||
|
(in this case 3D), measured in milliseconds.
|
||||||
|
- `height`: Estimated stem time, which is the time from the node itself to the
|
||||||
|
backbone. So, for example this might represent the latency between your
|
||||||
|
node's actual location, like your home, and the point at which it gets into
|
||||||
|
the highest-speed, core part of the Internet. Height helps adjust for
|
||||||
|
[triangle inequality violations](https://en.wikipedia.org/wiki/Triangle_inequality),
|
||||||
|
which are common on the Internet.
|
||||||
|
- `error`: Estimated error of the current `position`, and `height`. Lower is
|
||||||
|
better.
|
||||||
|
|
||||||
To run the example, first clone the repository locally:
|
To run the example, first clone the repository locally:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
+61
-32
@@ -63,7 +63,11 @@ use crate::vector::Vector;
|
|||||||
///
|
///
|
||||||
/// - `N`: the dimensionality of the vector portion (i.e. non-height) of the Vivaldi height vector
|
/// - `N`: the dimensionality of the vector portion (i.e. non-height) of the Vivaldi height vector
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub(crate) struct HeightVector<const N: usize>(Vector<f32, N>, f32);
|
pub(crate) struct HeightVector<const N: usize> {
|
||||||
|
#[serde(flatten)]
|
||||||
|
position: Vector<f32, N>,
|
||||||
|
height: f32,
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// **** Implementations ****
|
// **** Implementations ****
|
||||||
@@ -83,13 +87,17 @@ impl<const N: usize> HeightVector<N> {
|
|||||||
*i = rng.generate::<f32>();
|
*i = rng.generate::<f32>();
|
||||||
}
|
}
|
||||||
let height = rng.generate::<f32>().abs();
|
let height = rng.generate::<f32>().abs();
|
||||||
Self(Vector::<f32, N>::from(vec), height).normalized()
|
Self {
|
||||||
|
position: Vector::<f32, N>::from(vec),
|
||||||
|
height,
|
||||||
|
}
|
||||||
|
.normalized()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The magnitude of a Vivaldi height vector is defined as the magnitude of the vector plus the
|
/// The magnitude of a Vivaldi height vector is defined as the magnitude of the vector plus the
|
||||||
/// height value.
|
/// height value.
|
||||||
pub(crate) fn len(&self) -> f32 {
|
pub(crate) fn len(&self) -> f32 {
|
||||||
self.0.len() + self.1
|
self.position.len() + self.height
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A normalized Vivaldi height vector is just like a normalized vector: the vector scaled
|
/// A normalized Vivaldi height vector is just like a normalized vector: the vector scaled
|
||||||
@@ -101,7 +109,10 @@ impl<const N: usize> HeightVector<N> {
|
|||||||
Self::new()
|
Self::new()
|
||||||
} else {
|
} else {
|
||||||
// otherwise scale it by the inverse of its magnitude like any normal vector
|
// otherwise scale it by the inverse of its magnitude like any normal vector
|
||||||
Self(self.0 / len, self.1 / len)
|
Self {
|
||||||
|
position: self.position / len,
|
||||||
|
height: self.height / len,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +129,10 @@ impl<const N: usize> HeightVector<N> {
|
|||||||
/// In this case, valid means the height is positive, and none of the components are NaN or
|
/// In this case, valid means the height is positive, and none of the components are NaN or
|
||||||
/// Inf.
|
/// Inf.
|
||||||
pub(crate) fn is_invalid(&self) -> bool {
|
pub(crate) fn is_invalid(&self) -> bool {
|
||||||
self.0.is_invalid() || self.1.is_nan() || self.1.is_infinite() || self.1 < 0.0
|
self.position.is_invalid()
|
||||||
|
|| self.height.is_nan()
|
||||||
|
|| self.height.is_infinite()
|
||||||
|
|| self.height < 0.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,14 +143,20 @@ impl<const N: usize> HeightVector<N> {
|
|||||||
impl<const N: usize> Default for HeightVector<N> {
|
impl<const N: usize> Default for HeightVector<N> {
|
||||||
/// Default value for a Vivaldi height vector is just the defaults of its children types.
|
/// Default value for a Vivaldi height vector is just the defaults of its children types.
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self(Default::default(), Default::default())
|
Self {
|
||||||
|
position: Default::default(),
|
||||||
|
height: Default::default(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const N: usize> From<([f32; N], f32)> for HeightVector<N> {
|
impl<const N: usize> From<([f32; N], f32)> for HeightVector<N> {
|
||||||
/// Convert from a `([f32; N], f32)` (vector, height) to a Vivaldi height vector type.
|
/// Convert from a `([f32; N], f32)` (vector, height) to a Vivaldi height vector type.
|
||||||
fn from(value: ([f32; N], f32)) -> Self {
|
fn from(value: ([f32; N], f32)) -> Self {
|
||||||
let ret = Self(Vector::<f32, N>::from(value.0), value.1);
|
let ret = Self {
|
||||||
|
position: Vector::<f32, N>::from(value.0),
|
||||||
|
height: value.1,
|
||||||
|
};
|
||||||
if ret.is_valid() {
|
if ret.is_valid() {
|
||||||
ret
|
ret
|
||||||
} else {
|
} else {
|
||||||
@@ -150,7 +170,10 @@ impl<const N: usize> Add for HeightVector<N> {
|
|||||||
|
|
||||||
/// Add two Vivaldi height vectors.
|
/// Add two Vivaldi height vectors.
|
||||||
fn add(self, rhs: Self) -> Self::Output {
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
let ret = Self(self.0 + rhs.0, self.1 + rhs.1);
|
let ret = Self {
|
||||||
|
position: self.position + rhs.position,
|
||||||
|
height: self.height + rhs.height,
|
||||||
|
};
|
||||||
if ret.is_valid() {
|
if ret.is_valid() {
|
||||||
ret
|
ret
|
||||||
} else {
|
} else {
|
||||||
@@ -165,7 +188,10 @@ impl<const N: usize> Sub for HeightVector<N> {
|
|||||||
/// Subtract two Vivaldi height vectors. Note that this is the difference in the vectors
|
/// Subtract two Vivaldi height vectors. Note that this is the difference in the vectors
|
||||||
/// and the summation of the heights, as defined by Vivaldi's author.
|
/// and the summation of the heights, as defined by Vivaldi's author.
|
||||||
fn sub(self, rhs: Self) -> Self::Output {
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
let ret = Self(self.0 - rhs.0, self.1 + rhs.1);
|
let ret = Self {
|
||||||
|
position: self.position - rhs.position,
|
||||||
|
height: self.height + rhs.height,
|
||||||
|
};
|
||||||
if ret.is_valid() {
|
if ret.is_valid() {
|
||||||
ret
|
ret
|
||||||
} else {
|
} else {
|
||||||
@@ -179,7 +205,10 @@ impl<const N: usize> Mul<f32> for HeightVector<N> {
|
|||||||
|
|
||||||
/// Multiply a Vivaldi height vector by a scalar. Works the same as normal vector scaling.
|
/// Multiply a Vivaldi height vector by a scalar. Works the same as normal vector scaling.
|
||||||
fn mul(self, rhs: f32) -> Self::Output {
|
fn mul(self, rhs: f32) -> Self::Output {
|
||||||
let ret = Self(self.0 * rhs, self.1 * rhs);
|
let ret = Self {
|
||||||
|
position: self.position * rhs,
|
||||||
|
height: self.height * rhs,
|
||||||
|
};
|
||||||
if ret.is_valid() {
|
if ret.is_valid() {
|
||||||
ret
|
ret
|
||||||
} else {
|
} else {
|
||||||
@@ -220,9 +249,9 @@ mod tests {
|
|||||||
let a = HeightVector::<2>::from(([fx0 ,fy0 ],fh0 ));
|
let a = HeightVector::<2>::from(([fx0 ,fy0 ],fh0 ));
|
||||||
let b = HeightVector::<2>::from(([fx1 ,fy1 ],fh1 ));
|
let b = HeightVector::<2>::from(([fx1 ,fy1 ],fh1 ));
|
||||||
let c = a+b;
|
let c = a+b;
|
||||||
assert_eq!(c.0[0], fx0 + fx1);
|
assert_eq!(c.position[0], fx0 + fx1);
|
||||||
assert_eq!(c.0[1], fy0 + fy1);
|
assert_eq!(c.position[1], fy0 + fy1);
|
||||||
assert_eq!(c.1, fh0 + fh1);
|
assert_eq!(c.height, fh0 + fh1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -234,9 +263,9 @@ mod tests {
|
|||||||
let a = HeightVector::<2>::from(([fx0 ,fy0 ],fh0 ));
|
let a = HeightVector::<2>::from(([fx0 ,fy0 ],fh0 ));
|
||||||
let b = HeightVector::<2>::from(([fx1 ,fy1 ],fh1 ));
|
let b = HeightVector::<2>::from(([fx1 ,fy1 ],fh1 ));
|
||||||
let c = a-b;
|
let c = a-b;
|
||||||
assert_eq!(c.0[0], fx0 - fx1);
|
assert_eq!(c.position[0], fx0 - fx1);
|
||||||
assert_eq!(c.0[1], fy0 - fy1);
|
assert_eq!(c.position[1], fy0 - fy1);
|
||||||
assert_eq!(c.1, fh0 + fh1);
|
assert_eq!(c.height, fh0 + fh1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -249,9 +278,9 @@ mod tests {
|
|||||||
if fm < 0.0 {
|
if fm < 0.0 {
|
||||||
assert_approx_eq!(b.len(), 1.0)
|
assert_approx_eq!(b.len(), 1.0)
|
||||||
} else {
|
} else {
|
||||||
assert_eq!(b.0[0], fx * fm);
|
assert_eq!(b.position[0], fx * fm);
|
||||||
assert_eq!(b.0[1], fy * fm);
|
assert_eq!(b.position[1], fy * fm);
|
||||||
assert_eq!(b.1, fh * fm);
|
assert_eq!(b.height, fh * fm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,9 +308,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_from() {
|
fn test_from() {
|
||||||
let a = HeightVector::<2>::from(([1.0, 2.0], 3.0));
|
let a = HeightVector::<2>::from(([1.0, 2.0], 3.0));
|
||||||
assert_eq!(a.0[0], 1.0);
|
assert_eq!(a.position[0], 1.0);
|
||||||
assert_eq!(a.0[1], 2.0);
|
assert_eq!(a.position[1], 2.0);
|
||||||
assert_eq!(a.1, 3.0);
|
assert_eq!(a.height, 3.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -289,9 +318,9 @@ mod tests {
|
|||||||
let a = HeightVector::<2>::from(([1.0, 2.0], 3.0));
|
let a = HeightVector::<2>::from(([1.0, 2.0], 3.0));
|
||||||
let b = HeightVector::<2>::from(([3.0, 2.0], 1.0));
|
let b = HeightVector::<2>::from(([3.0, 2.0], 1.0));
|
||||||
let c = a + b;
|
let c = a + b;
|
||||||
assert_eq!(c.0[0], 4.0);
|
assert_eq!(c.position[0], 4.0);
|
||||||
assert_eq!(c.0[1], 4.0);
|
assert_eq!(c.position[1], 4.0);
|
||||||
assert_eq!(c.1, 4.0);
|
assert_eq!(c.height, 4.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -299,23 +328,23 @@ mod tests {
|
|||||||
let a = HeightVector::<2>::from(([1.0, 2.0], 3.0));
|
let a = HeightVector::<2>::from(([1.0, 2.0], 3.0));
|
||||||
let b = HeightVector::<2>::from(([3.0, 2.0], 1.0));
|
let b = HeightVector::<2>::from(([3.0, 2.0], 1.0));
|
||||||
let c = a - b;
|
let c = a - b;
|
||||||
assert_eq!(c.0[0], -2.0);
|
assert_eq!(c.position[0], -2.0);
|
||||||
assert_eq!(c.0[1], 0.0);
|
assert_eq!(c.position[1], 0.0);
|
||||||
assert_eq!(c.1, 4.0);
|
assert_eq!(c.height, 4.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mul_scalar() {
|
fn test_mul_scalar() {
|
||||||
let a = HeightVector::<2>::from(([1.0, 2.0], 3.0)) * 10.0;
|
let a = HeightVector::<2>::from(([1.0, 2.0], 3.0)) * 10.0;
|
||||||
assert_eq!(a.0[0], 10.0);
|
assert_eq!(a.position[0], 10.0);
|
||||||
assert_eq!(a.0[1], 20.0);
|
assert_eq!(a.position[1], 20.0);
|
||||||
assert_eq!(a.1, 30.0);
|
assert_eq!(a.height, 30.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serde() {
|
fn test_serde() {
|
||||||
// start with JSON, deserialize it
|
// start with JSON, deserialize it
|
||||||
let s = "[{\"inner\":[1.0,2.0,3.0]},4.0]";
|
let s = "{\"position\":[1.0,2.0,3.0],\"height\":4.0}";
|
||||||
let a: HeightVector<3> = serde_json::from_str(s).unwrap();
|
let a: HeightVector<3> = serde_json::from_str(s).unwrap();
|
||||||
|
|
||||||
// make sure it's the right length and works like we expect a normal NC
|
// make sure it's the right length and works like we expect a normal NC
|
||||||
|
|||||||
+22
-5
@@ -51,7 +51,7 @@
|
|||||||
//!
|
//!
|
||||||
//! // mock up a remote NC for the sake of this example (normally the node would actually receive
|
//! // mock up a remote NC for the sake of this example (normally the node would actually receive
|
||||||
//! // this from a remote node)
|
//! // this from a remote node)
|
||||||
//! let received_msg = "{\"position\":[{\"inner\":[1.5,0.5,2.0]},0.1],\"error\":1.0}";
|
//! let received_msg = "{\"position\":[1.5,0.5,2.0],\"height\":0.1,\"error\":1.0}";
|
||||||
//! let remote_nc: NetworkCoordinate<3> = serde_json::from_str(received_msg).unwrap();
|
//! let remote_nc: NetworkCoordinate<3> = serde_json::from_str(received_msg).unwrap();
|
||||||
//!
|
//!
|
||||||
//! // mock up a measured RTT between us and that node (normally the nodes would coordinate
|
//! // mock up a measured RTT between us and that node (normally the nodes would coordinate
|
||||||
@@ -69,16 +69,19 @@
|
|||||||
//! forwarded to us by someone else. And in this case, we aren't measuring the actual RTT. Instead,
|
//! forwarded to us by someone else. And in this case, we aren't measuring the actual RTT. Instead,
|
||||||
//! we're using the NCs to estimate RTT.
|
//! we're using the NCs to estimate RTT.
|
||||||
//!
|
//!
|
||||||
|
//! This example also uses the type alias [`NetworkCoordinate3D`] instead of its equivalent
|
||||||
|
//! [`NetworkCoordinate<3>`].
|
||||||
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! use vivaldi_nc::NetworkCoordinate;
|
//! use vivaldi_nc::NetworkCoordinate3D;
|
||||||
//!
|
//!
|
||||||
//! // create our local NC as a 3-dimensional Vivaldi coordinate
|
//! // create our local NC as a 3-dimensional Vivaldi coordinate
|
||||||
//! let mut my_nc = NetworkCoordinate::<3>::new();
|
//! let mut my_nc = NetworkCoordinate3D::new();
|
||||||
//!
|
//!
|
||||||
//! // mock up a remote NC for the sake of this example (normally the node would actually receive
|
//! // mock up a remote NC for the sake of this example (normally the node would actually receive
|
||||||
//! // this from a remote node)
|
//! // this from a remote node)
|
||||||
//! let received_msg = "{\"position\":[{\"inner\":[1.5,0.5,2.0]},0.1],\"error\":1.0}";
|
//! let received_msg = "{\"position\":[1.5,0.5,2.0],\"height\":0.1,\"error\":1.0}";
|
||||||
//! let remote_nc: NetworkCoordinate<3> = serde_json::from_str(received_msg).unwrap();
|
//! let remote_nc: NetworkCoordinate3D = serde_json::from_str(received_msg).unwrap();
|
||||||
//!
|
//!
|
||||||
//! // estimate the RTT to this remote node without measuring it directly
|
//! // estimate the RTT to this remote node without measuring it directly
|
||||||
//! let rtt_estimated = my_nc.estimated_rtt(&remote_nc);
|
//! let rtt_estimated = my_nc.estimated_rtt(&remote_nc);
|
||||||
@@ -94,3 +97,17 @@ mod vector;
|
|||||||
// publish our interface
|
// publish our interface
|
||||||
pub mod network_coordinate;
|
pub mod network_coordinate;
|
||||||
pub use network_coordinate::NetworkCoordinate;
|
pub use network_coordinate::NetworkCoordinate;
|
||||||
|
|
||||||
|
// type aliases for convenience
|
||||||
|
|
||||||
|
/// A 2D [`NetworkCoordinate`]. Includes a 2D Euclidean position and a height.
|
||||||
|
///
|
||||||
|
/// This type alias is just for convenience. It's functionally equivalent to
|
||||||
|
/// `NetworkCoordinate<2>`.
|
||||||
|
pub type NetworkCoordinate2D = NetworkCoordinate<2>;
|
||||||
|
|
||||||
|
/// A 3D [`NetworkCoordinate`]. Includes a 3D Euclidean position and a height.
|
||||||
|
///
|
||||||
|
/// This type alias is just for convenience. It's functionally equivalent to
|
||||||
|
/// `NetworkCoordinate<3>`.
|
||||||
|
pub type NetworkCoordinate3D = NetworkCoordinate<3>;
|
||||||
+13
-11
@@ -51,7 +51,8 @@ const MIN_ERROR: f32 = f32::EPSILON;
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct NetworkCoordinate<const N: usize> {
|
pub struct NetworkCoordinate<const N: usize> {
|
||||||
position: HeightVector<N>,
|
#[serde(flatten)]
|
||||||
|
heightvec: HeightVector<N>,
|
||||||
error: f32,
|
error: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +107,7 @@ impl<const N: usize> NetworkCoordinate<N> {
|
|||||||
///
|
///
|
||||||
pub fn estimated_rtt(&self, rhs: &Self) -> Duration {
|
pub fn estimated_rtt(&self, rhs: &Self) -> Duration {
|
||||||
// estimated rss is euclidean distance between the two plus the sum of the heights
|
// estimated rss is euclidean distance between the two plus the sum of the heights
|
||||||
Duration::from_secs_f32((self.position - rhs.position).len() / 1000.0)
|
Duration::from_secs_f32((self.heightvec - rhs.heightvec).len() / 1000.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given another Vivaldi [`NetworkCoordinate`], adjust our coordinateto better represent the actual round
|
/// Given another Vivaldi [`NetworkCoordinate`], adjust our coordinateto better represent the actual round
|
||||||
@@ -166,11 +167,12 @@ impl<const N: usize> NetworkCoordinate<N> {
|
|||||||
// δ = cc × w
|
// δ = cc × w
|
||||||
let delta = C_DELTA * w;
|
let delta = C_DELTA * w;
|
||||||
// xi = xi + δ × (rtt − ‖xi − xj ‖) × u(xi − xj)
|
// xi = xi + δ × (rtt − ‖xi − xj ‖) × u(xi − xj)
|
||||||
self.position = self.position + (self.position - rhs.position).normalized() * delta * error;
|
self.heightvec =
|
||||||
|
self.heightvec + (self.heightvec - rhs.heightvec).normalized() * delta * error;
|
||||||
|
|
||||||
// if we ended up with an invalid coordinate, return a new random coordinate with default
|
// if we ended up with an invalid coordinate, return a new random coordinate with default
|
||||||
// error
|
// error
|
||||||
if self.position.is_invalid() {
|
if self.heightvec.is_invalid() {
|
||||||
*self = Self::new()
|
*self = Self::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,7 +195,7 @@ impl<const N: usize> Default for NetworkCoordinate<N> {
|
|||||||
/// A default `NetworkCoordinate` has a random position and DEFAULT_ERROR
|
/// A default `NetworkCoordinate` has a random position and DEFAULT_ERROR
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
position: HeightVector::<N>::random(),
|
heightvec: HeightVector::<N>::random(),
|
||||||
error: DEFAULT_ERROR,
|
error: DEFAULT_ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,8 +260,8 @@ mod tests {
|
|||||||
|
|
||||||
// verify the initial error
|
// verify the initial error
|
||||||
let error =
|
let error =
|
||||||
(slc.error.powf(2.0) + nyc.error.powf(2.0) + lax.error.powf(2.0) + mad.error.powf(2.0))
|
(slc.error.powf(2.0) + nyc.error.powf(2.0) + lax.error.powf(2.0) + mad.error.powf(2.0))
|
||||||
.sqrt();
|
.sqrt();
|
||||||
assert_eq!(error, 400.0);
|
assert_eq!(error, 400.0);
|
||||||
|
|
||||||
// iterate plenty of times to converge and minimize error
|
// iterate plenty of times to converge and minimize error
|
||||||
@@ -292,11 +294,11 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_serde() {
|
fn test_serde() {
|
||||||
// start with JSON, deserialize it
|
// start with JSON, deserialize it
|
||||||
let s = "{\"position\":[{\"inner\":[1.5,0.5,2.0]},0.1],\"error\":1.0}";
|
let s = "{\"position\":[1.5,0.5,2.0],\"height\":0.1,\"error\":1.0}";
|
||||||
let a: NetworkCoordinate<3> = serde_json::from_str(s).unwrap();
|
let a: NetworkCoordinate<3> = serde_json::from_str(s).unwrap();
|
||||||
|
|
||||||
// make sure it's the right length and works like we expect a normal NC
|
// make sure it's the right length and works like we expect a normal NC
|
||||||
assert_approx_eq!(a.position.len(), 2.649_509, 0.001);
|
assert_approx_eq!(a.heightvec.len(), 2.649_509, 0.001);
|
||||||
assert_eq!(a.error, 1.0);
|
assert_eq!(a.error, 1.0);
|
||||||
assert_eq!(a.estimated_rtt(&a).as_millis(), 0);
|
assert_eq!(a.estimated_rtt(&a).as_millis(), 0);
|
||||||
|
|
||||||
@@ -308,9 +310,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_estimated_rtt() {
|
fn test_estimated_rtt() {
|
||||||
// start with JSON, deserialize it
|
// start with JSON, deserialize it
|
||||||
let s = "{\"position\":[{\"inner\":[1.5,0.5,2.0]},25.0],\"error\":1.0}";
|
let s = "{\"position\":[1.5,0.5,2.0],\"height\":25.0,\"error\":1.0}";
|
||||||
let a: NetworkCoordinate<3> = serde_json::from_str(s).unwrap();
|
let a: NetworkCoordinate<3> = serde_json::from_str(s).unwrap();
|
||||||
let s = "{\"position\":[{\"inner\":[-1.5,-0.5,-2.0]},50.0],\"error\":1.0}";
|
let s = "{\"position\":[-1.5,-0.5,-2.0],\"height\":50.0,\"error\":1.0}";
|
||||||
let b: NetworkCoordinate<3> = serde_json::from_str(s).unwrap();
|
let b: NetworkCoordinate<3> = serde_json::from_str(s).unwrap();
|
||||||
|
|
||||||
let estimate = a.estimated_rtt(&b);
|
let estimate = a.estimated_rtt(&b);
|
||||||
|
|||||||
+2
-1
@@ -24,6 +24,7 @@ pub(crate) struct Vector<T, const N: usize>
|
|||||||
where
|
where
|
||||||
T: Float + Serialize + for<'d> Deserialize<'d>,
|
T: Float + Serialize + for<'d> Deserialize<'d>,
|
||||||
{
|
{
|
||||||
|
#[serde(rename = "position")]
|
||||||
#[serde_as(as = "[_; N]")]
|
#[serde_as(as = "[_; N]")]
|
||||||
inner: [T; N],
|
inner: [T; N],
|
||||||
}
|
}
|
||||||
@@ -279,7 +280,7 @@ mod tests {
|
|||||||
fn test_serde() {
|
fn test_serde() {
|
||||||
let a = Vector::<f32, 3>::from([1.0, 2.0, 3.0]);
|
let a = Vector::<f32, 3>::from([1.0, 2.0, 3.0]);
|
||||||
let s = serde_json::to_string(&a);
|
let s = serde_json::to_string(&a);
|
||||||
assert_eq!(s.as_ref().unwrap(), "{\"inner\":[1.0,2.0,3.0]}");
|
assert_eq!(s.as_ref().unwrap(), "{\"position\":[1.0,2.0,3.0]}");
|
||||||
assert!(s.is_ok());
|
assert!(s.is_ok());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user