Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
H
ham-cats
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
CATS
ham-cats
Compare revisions
6b203d1555ef123d29f77a6a3ec816afb13d926a to master
Compare revisions
Changes are shown as if the
source
revision was being merged into the
target
revision.
Learn more about comparing revisions.
Source
cats/ham-cats
Select target project
No results found
master
Select Git revision
Branches
master
Swap
Target
Quantum_P/ham-cats
Select target project
cats/ham-cats
sam/ham-cats
Quantum_P/ham-cats
Reed/ham-cats
4 results
6b203d1555ef123d29f77a6a3ec816afb13d926a
Select Git revision
Show changes
Only incoming changes from source
Include changes to target since source was created
Compare
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
src/whisker/mod.rs
+135
-37
135 additions, 37 deletions
src/whisker/mod.rs
src/whisker/node_info.rs
+466
-0
466 additions, 0 deletions
src/whisker/node_info.rs
src/whisker/route.rs
+395
-35
395 additions, 35 deletions
src/whisker/route.rs
src/whitener.rs
+80
-6
80 additions, 6 deletions
src/whitener.rs
with
1076 additions
and
78 deletions
src/whisker/mod.rs
View file @
9a18248f
...
...
@@ -3,6 +3,7 @@ mod comment;
mod
destination
;
mod
gps
;
mod
identification
;
mod
node_info
;
mod
route
;
mod
timestamp
;
mod
unknown
;
...
...
@@ -15,7 +16,8 @@ pub use self::{
destination
::
Destination
,
gps
::
Gps
,
identification
::
Identification
,
route
::{
Route
,
RouteIter
,
RouteNode
},
node_info
::{
NodeInfo
,
NodeInfoBuilder
},
route
::{
PastHop
,
Route
,
RouteHop
,
RouteIter
},
timestamp
::
Timestamp
,
unknown
::
Unknown
,
};
...
...
@@ -27,6 +29,7 @@ pub(crate) const COMMENT_TYPE: u8 = 0x03;
pub
(
crate
)
const
ROUTE_TYPE
:
u8
=
0x04
;
pub
(
crate
)
const
DESTINATION_TYPE
:
u8
=
0x05
;
pub
(
crate
)
const
ARBITRARY_TYPE
:
u8
=
0x06
;
pub
(
crate
)
const
NODE_INFO_TYPE
:
u8
=
0x09
;
#[derive(Debug)]
pub
enum
Whisker
{
...
...
@@ -37,6 +40,7 @@ pub enum Whisker {
Route
(
Route
),
Destination
(
Destination
),
Arbitrary
(
Arbitrary
),
NodeInfo
(
NodeInfo
),
Unknown
(
Unknown
),
}
...
...
@@ -137,6 +141,9 @@ impl<'a> WhiskerIter<'a> {
ARBITRARY_TYPE
=>
Whisker
::
Arbitrary
(
Arbitrary
::
decode
(
data
)
.ok_or
(
DecodeError
::
MalformedWhisker
{
position
})
?
,
),
NODE_INFO_TYPE
=>
Whisker
::
NodeInfo
(
NodeInfo
::
decode
(
data
)
.ok_or
(
DecodeError
::
MalformedWhisker
{
position
})
?
,
),
// safe to unwrap because we know len has to be 255 or less, since it's a u8
whisker_type
=>
{
...
...
@@ -148,7 +155,7 @@ impl<'a> WhiskerIter<'a> {
}
}
impl
<
'a
>
Iterator
for
WhiskerIter
<
'
a
>
{
impl
Iterator
for
WhiskerIter
<
'
_
>
{
type
Item
=
Result
<
Whisker
,
DecodeError
>
;
fn
next
(
&
mut
self
)
->
Option
<
Self
::
Item
>
{
...
...
@@ -172,7 +179,7 @@ impl<'a> ValidatedWhiskerIter<'a> {
}
}
impl
<
'a
>
Iterator
for
ValidatedWhiskerIter
<
'
a
>
{
impl
Iterator
for
ValidatedWhiskerIter
<
'
_
>
{
type
Item
=
Whisker
;
fn
next
(
&
mut
self
)
->
Option
<
Self
::
Item
>
{
...
...
@@ -184,7 +191,10 @@ impl<'a> Iterator for ValidatedWhiskerIter<'a> {
mod
tests
{
use
half
::
f16
;
use
crate
::
whisker
::
route
::
RouteNode
;
use
crate
::{
identity
::
Identity
,
whisker
::
route
::{
PastHop
,
RouteHop
},
};
use
super
::
*
;
...
...
@@ -194,7 +204,7 @@ mod tests {
let
call
=
"VE9ABCDEFGZZ4839-???"
;
let
ssid
=
17
;
let
ident
=
Identification
::
new
(
icon
,
call
,
ssid
)
.unwrap
();
let
ident
=
Identification
::
new
(
call
,
ssid
,
icon
)
.unwrap
();
let
mut
buf
=
[
0
;
256
];
let
encoded
=
ident
.encode
(
&
mut
buf
)
.unwrap
();
...
...
@@ -238,7 +248,7 @@ mod tests {
assert_eq!
(
89.99899999704212
,
gps
.latitude
());
assert_eq!
(
-
179.9989999551326
,
gps
.longitude
());
assert_eq!
(
12
0.9
375
,
gps
.heading
());
assert_eq!
(
123
.
75
,
gps
.heading
());
}
#[test]
...
...
@@ -248,11 +258,58 @@ mod tests {
23.45
,
f16
::
from_f32
(
45.02
),
24
,
360.0
,
359.0
,
f16
::
from_f32
(
12.3
),
);
assert_eq!
(
358.59375
,
gps
.heading
());
let
gps
=
Gps
::
new
(
4.0
,
23.45
,
f16
::
from_f32
(
45.02
),
24
,
957.47
,
f16
::
from_f32
(
12.3
),
);
assert_eq!
(
357.1875
,
gps
.heading
());
assert_eq!
(
957.65625
-
360.0
*
2.0
,
gps
.heading
());
}
#[test]
fn
gps_min_heading
()
{
let
gps
=
Gps
::
new
(
4.0
,
23.45
,
f16
::
from_f32
(
45.02
),
24
,
0.0
,
f16
::
from_f32
(
12.3
),
);
assert_eq!
(
0.0
,
gps
.heading
());
let
gps
=
Gps
::
new
(
4.0
,
23.45
,
f16
::
from_f32
(
45.02
),
24
,
-
22.0
,
f16
::
from_f32
(
12.3
),
);
assert_eq!
(
337.5
,
gps
.heading
());
let
gps
=
Gps
::
new
(
4.0
,
23.45
,
f16
::
from_f32
(
45.02
),
24
,
-
1206.0
,
f16
::
from_f32
(
12.3
),
);
assert_eq!
(
233.4375
,
gps
.heading
());
}
#[test]
...
...
@@ -287,43 +344,47 @@ mod tests {
#[test]
fn
route_push_and_iter
()
{
let
mut
route
=
Route
::
new
(
34
);
route
.push_callsign
(
"VE2XYZ"
,
23
,
false
)
.unwrap
();
route
.push_past
(
PastHop
::
new
(
Identity
::
new
(
"VE2XYZ"
,
23
),
None
))
.unwrap
();
route
.push_internet
()
.unwrap
();
route
.push_internet
()
.unwrap
();
route
.push_internet
()
.unwrap
();
route
.push_callsign
(
"VE9AAAAA"
,
94
,
true
)
.unwrap
();
assert!
(
route
.push_callsign
(
"VE9AAAAA"
,
94
,
false
)
.is_none
());
route
.push_future
(
Identity
::
new
(
"VE9AAAAA"
,
94
))
.unwrap
();
// past after future - not allowed
assert!
(
route
.push_past
(
PastHop
::
new
(
Identity
::
new
(
"VE9AAAAA"
,
94
),
None
))
.is_none
());
// too long
assert!
(
route
.push_
callsign
(
.push_
future
(
Identity
::
new
(
"lsdfjslkdfjlksdjflksfjsdklfjsdklfjsdklfjsdklfjsklfsef;jklsdfjkl;sdf;klsdf;klsjdfJSDJFSKL:DFJDSL:KFskldfj;slkdfjsdkl;fjdskl;fjsdfl;kjsdfl;ksdjfkl;ssdfl;kjsdfl;ksdjf;sdklsd;lfkjsdlfk;jsdl;fkjsd;klfjsd;fljsf;oidfjgwper0tujdfgndfjkl;gjnergjol;kehfgo;dijge;oghdfkl;gjdfkl;gjdeior;lgjedr;ioghjdorighndeklo;grjiop["
,
20
,
true
,
)
.is_none
());
))
.is_none
());
route
.push_
callsign
(
"This is the last callsign"
,
0
,
true
)
.push_
future
(
Identity
::
new
(
"This is the last callsign"
,
0
)
)
.unwrap
();
route
.push_internet
()
.unwrap
();
let
mut
iter
=
route
.iter
();
assert_eq!
(
Route
Node
::
Identity
(
"VE2XYZ"
,
23
,
false
),
Route
Hop
::
Past
(
PastHop
::
new
(
Identity
::
new
(
"VE2XYZ"
,
23
)
,
None
)
),
iter
.next
()
.unwrap
()
);
assert_eq!
(
Route
Node
::
Internet
,
iter
.next
()
.unwrap
());
assert_eq!
(
Route
Node
::
Internet
,
iter
.next
()
.unwrap
());
assert_eq!
(
Route
Node
::
Internet
,
iter
.next
()
.unwrap
());
assert_eq!
(
Route
Hop
::
Internet
,
iter
.next
()
.unwrap
());
assert_eq!
(
Route
Hop
::
Internet
,
iter
.next
()
.unwrap
());
assert_eq!
(
Route
Hop
::
Internet
,
iter
.next
()
.unwrap
());
assert_eq!
(
Route
Node
::
Identity
(
"VE9AAAAA"
,
94
,
true
),
Route
Hop
::
Future
(
Identity
::
new
(
"VE9AAAAA"
,
94
)
),
iter
.next
()
.unwrap
()
);
assert_eq!
(
Route
Node
::
Identity
(
"This is the last callsign"
,
0
,
true
),
Route
Hop
::
Future
(
Identity
::
new
(
"This is the last callsign"
,
0
)
),
iter
.next
()
.unwrap
()
);
assert_eq!
(
Route
Node
::
Internet
,
iter
.next
()
.unwrap
());
assert_eq!
(
Route
Hop
::
Internet
,
iter
.next
()
.unwrap
());
assert_eq!
(
None
,
iter
.next
());
assert_eq!
(
34
,
route
.max_hops
);
...
...
@@ -332,13 +393,15 @@ mod tests {
#[test]
fn
route_e2e
()
{
let
mut
route
=
Route
::
new
(
34
);
route
.push_callsign
(
"VE2XYZ"
,
23
,
false
)
.unwrap
();
route
.push_past
(
PastHop
::
new
(
Identity
::
new
(
"VE2XYZ"
,
23
),
Some
(
0.0
)))
.unwrap
();
route
.push_internet
()
.unwrap
();
route
.push_internet
()
.unwrap
();
route
.push_internet
()
.unwrap
();
route
.push_
callsign
(
"VE9AAAAA"
,
94
,
true
)
.unwrap
();
route
.push_
future
(
Identity
::
new
(
"VE9AAAAA"
,
94
)
)
.unwrap
();
route
.push_
callsign
(
"This is the last callsign"
,
0
,
true
)
.push_
future
(
Identity
::
new
(
"This is the last callsign"
,
0
)
)
.unwrap
();
route
.push_internet
()
.unwrap
();
...
...
@@ -352,44 +415,70 @@ mod tests {
#[test]
fn
route_doc_examples
()
{
let
mut
ex1
=
Route
::
new
(
4
);
ex1
.push_callsign
(
"VE1ABC"
,
0
,
false
);
ex1
.push_callsign
(
"VE2DEF"
,
234
,
false
);
ex1
.push_callsign
(
"VE3XYZ"
,
14
,
false
);
ex1
.push_past
(
PastHop
::
new
(
Identity
::
new
(
"VE1ABC"
,
0
),
Some
(
-
96.0
)))
.unwrap
();
ex1
.push_past
(
PastHop
::
new
(
Identity
::
new
(
"VE2DEF"
,
234
),
Some
(
-
13.0
)))
.unwrap
();
ex1
.push_past
(
PastHop
::
new
(
Identity
::
new
(
"VE3XYZ"
,
14
),
Some
(
-
106.0
)))
.unwrap
();
let
mut
buf
=
[
0
;
256
];
let
encoded
=
ex1
.encode
(
&
mut
buf
)
.unwrap
();
assert_eq!
(
&
[
0x19
,
0x04
,
0x56
,
0x45
,
0x31
,
0x41
,
0x42
,
0x43
,
0xFF
,
0x00
,
0x56
,
0x45
,
0x32
,
0x44
,
0x45
,
0x46
,
0xFF
,
0xEA
,
0x56
,
0x45
,
0x33
,
0x58
,
0x59
,
0x5A
,
0xFF
,
0x0E
0x1C
,
0x04
,
0x56
,
0x45
,
0x31
,
0x41
,
0x42
,
0x43
,
0xFF
,
0x00
,
0x60
,
0x56
,
0x45
,
0x32
,
0x44
,
0x45
,
0x46
,
0xFF
,
0xEA
,
0xDC
,
0x56
,
0x45
,
0x33
,
0x58
,
0x59
,
0x5A
,
0xFF
,
0x0E
,
0x51
],
&
encoded
);
let
mut
ex1_5
=
Route
::
new
(
4
);
ex1_5
.push_past
(
PastHop
::
new
(
Identity
::
new
(
"VE1ABC"
,
0
),
Some
(
-
96.0
)))
.unwrap
();
ex1_5
.push_future
(
Identity
::
new
(
"VE2DEF"
,
234
))
.unwrap
();
ex1_5
.push_future
(
Identity
::
new
(
"VE3XYZ"
,
14
))
.unwrap
();
let
mut
buf
=
[
0
;
256
];
let
encoded
=
ex1_5
.encode
(
&
mut
buf
)
.unwrap
();
assert_eq!
(
&
[
0x1A
,
0x04
,
0x56
,
0x45
,
0x31
,
0x41
,
0x42
,
0x43
,
0xFF
,
0x00
,
0x60
,
0x56
,
0x45
,
0x32
,
0x44
,
0x45
,
0x46
,
0xFD
,
0xEA
,
0x56
,
0x45
,
0x33
,
0x58
,
0x59
,
0x5A
,
0xFD
,
0x0E
,
],
&
encoded
);
let
mut
ex2
=
Route
::
new
(
3
);
ex2
.push_callsign
(
"VE1ABC"
,
0
,
false
);
ex2
.push_past
(
PastHop
::
new
(
Identity
::
new
(
"VE1ABC"
,
0
),
None
))
.unwrap
();
ex2
.push_internet
();
ex2
.push_callsign
(
"VE2DEF"
,
234
,
false
);
ex2
.push_past
(
PastHop
::
new
(
Identity
::
new
(
"VE2DEF"
,
234
),
Some
(
-
86.5
)))
.unwrap
();
ex2
.push_internet
();
ex2
.push_callsign
(
"VE3XYZ"
,
14
,
false
);
ex2
.push_past
(
PastHop
::
new
(
Identity
::
new
(
"VE3XYZ"
,
14
),
Some
(
-
65.0
)))
.unwrap
();
let
encoded
=
ex2
.encode
(
&
mut
buf
)
.unwrap
();
assert_eq!
(
&
[
0x1B
,
0x03
,
0x56
,
0x45
,
0x31
,
0x41
,
0x42
,
0x43
,
0xFF
,
0x00
,
0xFE
,
0x56
,
0x45
,
0x32
,
0x44
,
0x45
,
0x46
,
0xFF
,
0xEA
,
0xFE
,
0x56
,
0x45
,
0x33
,
0x58
,
0x59
,
0x5A
,
0xFF
,
0x0E
0x1E
,
0x03
,
0x56
,
0x45
,
0x31
,
0x41
,
0x42
,
0x43
,
0xFF
,
0x00
,
0x00
,
0xFE
,
0x56
,
0x45
,
0x32
,
0x44
,
0x45
,
0x46
,
0xFF
,
0xEA
,
0x6E
,
0xFE
,
0x56
,
0x45
,
0x33
,
0x58
,
0x59
,
0x5A
,
0xFF
,
0x0E
,
0x8E
],
&
encoded
);
let
mut
ex3
=
Route
::
new
(
0
);
ex3
.push_callsign
(
"VE1ABC"
,
0
,
false
);
ex3
.push_past
(
PastHop
::
new
(
Identity
::
new
(
"VE1ABC"
,
0
),
Some
(
-
42.5
)))
.unwrap
();
ex3
.push_internet
();
ex3
.push_internet
();
let
encoded
=
ex3
.encode
(
&
mut
buf
)
.unwrap
();
assert_eq!
(
&
[
0x0
B
,
0x00
,
0x56
,
0x45
,
0x31
,
0x41
,
0x42
,
0x43
,
0xFF
,
0x00
,
0xFE
,
0xFE
],
&
[
0x0
C
,
0x00
,
0x56
,
0x45
,
0x31
,
0x41
,
0x42
,
0x43
,
0xFF
,
0x00
,
0xB0
,
0xFE
,
0xFE
],
&
encoded
);
}
...
...
@@ -407,6 +496,15 @@ mod tests {
assert_eq!
(
dest
,
decoded
);
}
#[test]
fn
dest_ack
()
{
let
dest
=
Destination
::
new
(
true
,
84
,
"abc"
,
17
)
.unwrap
();
assert_eq!
(
84
,
dest
.ack_num
());
assert!
(
dest
.is_ack
());
assert_eq!
(
"abc"
,
dest
.callsign
());
assert_eq!
(
17
,
dest
.ssid
());
}
#[test]
fn
arbitrary_e2e
()
{
let
data
=
b"Hello world! This is an example comment"
;
...
...
This diff is collapsed.
Click to expand it.
src/whisker/node_info.rs
0 → 100644
View file @
9a18248f
#[derive(Copy,
Clone,
Debug)]
pub
struct
NodeInfo
{
hardware_id
:
Option
<
u16
>
,
software_id
:
Option
<
u8
>
,
uptime
:
Option
<
u32
>
,
antenna_height
:
Option
<
u8
>
,
antenna_gain
:
Option
<
u8
>
,
tx_power
:
Option
<
u8
>
,
voltage
:
Option
<
u8
>
,
xcvr_temperature
:
Option
<
i8
>
,
battery_charge
:
Option
<
u8
>
,
altitude
:
Option
<
f32
>
,
balloon
:
bool
,
ambient_temperature
:
Option
<
i8
>
,
ambient_humidity
:
Option
<
u8
>
,
ambient_pressure
:
Option
<
u16
>
,
}
impl
NodeInfo
{
pub
fn
builder
()
->
NodeInfoBuilder
{
NodeInfoBuilder
::
default
()
}
pub
fn
hardware_id
(
&
self
)
->
Option
<
u16
>
{
self
.hardware_id
}
pub
fn
software_id
(
&
self
)
->
Option
<
u8
>
{
self
.software_id
}
/// Seconds
pub
fn
uptime
(
&
self
)
->
Option
<
u32
>
{
self
.uptime
}
/// Meters
pub
fn
antenna_height
(
&
self
)
->
Option
<
u8
>
{
self
.antenna_height
}
/// dBi
pub
fn
antenna_gain
(
&
self
)
->
Option
<
f64
>
{
self
.antenna_gain
.map
(|
g
|
g
as
f64
/
4.0
)
}
/// dBm
pub
fn
tx_power
(
&
self
)
->
Option
<
f64
>
{
self
.tx_power
.map
(|
p
|
p
as
f64
/
4.0
)
}
/// Volts
pub
fn
voltage
(
&
self
)
->
Option
<
f64
>
{
self
.voltage
.map
(|
v
|
v
as
f64
/
10.0
)
}
/// Degrees C
pub
fn
xcvr_temperature
(
&
self
)
->
Option
<
i8
>
{
self
.xcvr_temperature
}
/// Percent
pub
fn
battery_charge
(
&
self
)
->
Option
<
f64
>
{
self
.battery_charge
.map
(|
b
|
b
as
f64
/
2.55
)
}
/// Higher precision altitude than what's available in the GPS whisker. Takes precedent if set. Useful for high altitude balloons.
/// Meters
pub
fn
altitude
(
&
self
)
->
Option
<
f32
>
{
self
.altitude
}
/// If true, this whisker was emitted by a high altitude balloon payload
pub
fn
is_balloon
(
&
self
)
->
bool
{
self
.balloon
}
/// Degrees C
pub
fn
ambient_temperature
(
&
self
)
->
Option
<
i8
>
{
self
.ambient_temperature
}
/// Relative humidity, percent
pub
fn
ambient_humidity
(
&
self
)
->
Option
<
f64
>
{
self
.ambient_humidity
.map
(|
x
|
x
as
f64
/
2.55
)
}
/// Decapascal (daPa)
pub
fn
ambient_pressure
(
&
self
)
->
Option
<
u16
>
{
self
.ambient_pressure
}
pub
fn
encode
<
'a
>
(
&
self
,
buf
:
&
'a
mut
[
u8
])
->
Option
<&
'a
[
u8
]
>
{
let
mut
bitmask
:
u32
=
0
;
let
mut
i
=
4
;
// TODO these could be macros
if
let
Some
(
x
)
=
self
.hardware_id
{
bitmask
|
=
1
;
buf
.get_mut
(
i
..
(
i
+
2
))
?
.copy_from_slice
(
&
x
.to_le_bytes
());
i
+=
2
;
}
if
let
Some
(
x
)
=
self
.software_id
{
bitmask
|
=
2
;
*
buf
.get_mut
(
i
)
?
=
x
;
i
+=
1
;
}
if
let
Some
(
x
)
=
self
.uptime
{
bitmask
|
=
4
;
buf
.get_mut
(
i
..
(
i
+
4
))
?
.copy_from_slice
(
&
x
.to_le_bytes
());
i
+=
4
;
}
if
let
Some
(
x
)
=
self
.antenna_height
{
bitmask
|
=
8
;
*
buf
.get_mut
(
i
)
?
=
x
;
i
+=
1
;
}
if
let
Some
(
x
)
=
self
.antenna_gain
{
bitmask
|
=
16
;
*
buf
.get_mut
(
i
)
?
=
x
;
i
+=
1
;
}
if
let
Some
(
x
)
=
self
.tx_power
{
bitmask
|
=
32
;
*
buf
.get_mut
(
i
)
?
=
x
;
i
+=
1
;
}
if
let
Some
(
x
)
=
self
.voltage
{
bitmask
|
=
64
;
*
buf
.get_mut
(
i
)
?
=
x
;
i
+=
1
;
}
if
let
Some
(
x
)
=
self
.xcvr_temperature
{
bitmask
|
=
128
;
*
buf
.get_mut
(
i
)
?
=
x
.to_le_bytes
()[
0
];
i
+=
1
;
}
if
let
Some
(
x
)
=
self
.battery_charge
{
bitmask
|
=
256
;
*
buf
.get_mut
(
i
)
?
=
x
;
i
+=
1
;
}
if
let
Some
(
x
)
=
self
.altitude
{
bitmask
|
=
512
;
buf
.get_mut
(
i
..
(
i
+
4
))
?
.copy_from_slice
(
&
x
.to_le_bytes
());
i
+=
4
;
}
if
self
.balloon
{
bitmask
|
=
1024
;
}
if
let
Some
(
x
)
=
self
.ambient_temperature
{
bitmask
|
=
2048
;
*
buf
.get_mut
(
i
)
?
=
x
.to_le_bytes
()[
0
];
i
+=
1
;
}
if
let
Some
(
x
)
=
self
.ambient_humidity
{
bitmask
|
=
4096
;
*
buf
.get_mut
(
i
)
?
=
x
;
i
+=
1
;
}
if
let
Some
(
x
)
=
self
.ambient_pressure
{
bitmask
|
=
8192
;
buf
.get_mut
(
i
..
(
i
+
2
))
?
.copy_from_slice
(
&
x
.to_le_bytes
());
i
+=
2
;
}
buf
[
0
]
=
(
i
-
1
)
.try_into
()
.ok
()
?
;
buf
[
1
..
4
]
.copy_from_slice
(
&
bitmask
.to_be_bytes
()[
1
..
]);
Some
(
&
buf
[
..
i
])
}
pub
fn
decode
(
data
:
&
[
u8
])
->
Option
<
Self
>
{
let
bitmask
=
u32
::
from_be_bytes
([
0
,
*
data
.get
(
1
)
?
,
*
data
.get
(
2
)
?
,
*
data
.get
(
3
)
?
]);
let
mut
builder
=
NodeInfoBuilder
::
default
();
let
mut
i
=
4
;
if
bitmask
&
1
>
0
{
builder
.hardware_id
=
Some
(
u16
::
from_le_bytes
([
*
data
.get
(
i
)
?
,
*
data
.get
(
i
+
1
)
?
]));
i
+=
2
;
}
if
bitmask
&
2
>
0
{
builder
.software_id
=
Some
(
*
data
.get
(
i
)
?
);
i
+=
1
;
}
if
bitmask
&
4
>
0
{
builder
.uptime
=
Some
(
u32
::
from_le_bytes
([
*
data
.get
(
i
)
?
,
*
data
.get
(
i
+
1
)
?
,
*
data
.get
(
i
+
2
)
?
,
*
data
.get
(
i
+
3
)
?
,
]));
i
+=
4
;
}
if
bitmask
&
8
>
0
{
builder
.antenna_height
=
Some
(
*
data
.get
(
i
)
?
);
i
+=
1
;
}
if
bitmask
&
16
>
0
{
builder
.antenna_gain
=
Some
(
*
data
.get
(
i
)
?
);
i
+=
1
;
}
if
bitmask
&
32
>
0
{
builder
.tx_power
=
Some
(
*
data
.get
(
i
)
?
);
i
+=
1
;
}
if
bitmask
&
64
>
0
{
builder
.voltage
=
Some
(
*
data
.get
(
i
)
?
);
i
+=
1
;
}
if
bitmask
&
128
>
0
{
builder
.xcvr_temperature
=
Some
(
i8
::
from_le_bytes
([
*
data
.get
(
i
)
?
]));
i
+=
1
;
}
if
bitmask
&
256
>
0
{
builder
.battery_charge
=
Some
(
*
data
.get
(
i
)
?
);
i
+=
1
;
}
if
bitmask
&
512
>
0
{
builder
.altitude
=
Some
(
f32
::
from_le_bytes
(
data
.get
(
i
..
(
i
+
4
))
?
.try_into
()
.unwrap
(),
));
i
+=
4
;
}
builder
.balloon
=
bitmask
&
1024
>
0
;
if
bitmask
&
2048
>
0
{
builder
.ambient_temperature
=
Some
(
i8
::
from_le_bytes
([
*
data
.get
(
i
)
?
]));
i
+=
1
;
}
if
bitmask
&
4096
>
0
{
builder
.ambient_humidity
=
Some
(
*
data
.get
(
i
)
?
);
i
+=
1
;
}
if
bitmask
&
8192
>
0
{
builder
.ambient_pressure
=
Some
(
u16
::
from_le_bytes
(
data
.get
(
i
..
(
i
+
2
))
?
.try_into
()
.unwrap
(),
));
i
+=
2
;
}
// prevent unused variable warning
let
_
=
i
;
Some
(
builder
.build
())
}
}
#[derive(Default,
Copy,
Clone)]
pub
struct
NodeInfoBuilder
{
hardware_id
:
Option
<
u16
>
,
software_id
:
Option
<
u8
>
,
uptime
:
Option
<
u32
>
,
antenna_height
:
Option
<
u8
>
,
antenna_gain
:
Option
<
u8
>
,
tx_power
:
Option
<
u8
>
,
voltage
:
Option
<
u8
>
,
xcvr_temperature
:
Option
<
i8
>
,
battery_charge
:
Option
<
u8
>
,
altitude
:
Option
<
f32
>
,
balloon
:
bool
,
ambient_temperature
:
Option
<
i8
>
,
ambient_humidity
:
Option
<
u8
>
,
ambient_pressure
:
Option
<
u16
>
,
}
impl
NodeInfoBuilder
{
pub
fn
build
(
self
)
->
NodeInfo
{
let
NodeInfoBuilder
{
hardware_id
,
software_id
,
uptime
,
antenna_height
,
antenna_gain
,
tx_power
,
voltage
,
xcvr_temperature
,
battery_charge
,
altitude
,
balloon
,
ambient_temperature
,
ambient_humidity
,
ambient_pressure
,
}
=
self
;
NodeInfo
{
hardware_id
,
software_id
,
uptime
,
antenna_height
,
antenna_gain
,
tx_power
,
voltage
,
xcvr_temperature
,
battery_charge
,
altitude
,
balloon
,
ambient_temperature
,
ambient_humidity
,
ambient_pressure
,
}
}
pub
fn
hardware_id
(
mut
self
,
val
:
u16
)
->
Self
{
self
.hardware_id
=
Some
(
val
);
self
}
pub
fn
software_id
(
mut
self
,
val
:
u8
)
->
Self
{
self
.software_id
=
Some
(
val
);
self
}
/// Seconds
pub
fn
uptime
(
mut
self
,
val
:
u32
)
->
Self
{
self
.uptime
=
Some
(
val
);
self
}
/// Meters
pub
fn
antenna_height
(
mut
self
,
val
:
u8
)
->
Self
{
self
.antenna_height
=
Some
(
val
);
self
}
/// dBi
pub
fn
antenna_gain
(
mut
self
,
val
:
f64
)
->
Self
{
self
.antenna_gain
=
Some
((
val
*
4.0
)
.min
(
255.0
)
as
u8
);
self
}
/// dBm
pub
fn
tx_power
(
mut
self
,
val
:
f64
)
->
Self
{
self
.tx_power
=
Some
((
val
*
4.0
)
.min
(
255.0
)
as
u8
);
self
}
/// Volts
pub
fn
voltage
(
mut
self
,
val
:
f64
)
->
Self
{
self
.voltage
=
Some
((
val
*
10.0
)
.min
(
255.0
)
as
u8
);
self
}
/// Degrees C
pub
fn
xcvr_temperature
(
mut
self
,
val
:
i8
)
->
Self
{
self
.xcvr_temperature
=
Some
(
val
);
self
}
/// Percent
pub
fn
battery_charge
(
mut
self
,
val
:
f64
)
->
Self
{
self
.battery_charge
=
Some
((
val
*
2.55
)
.min
(
255.0
)
as
u8
);
self
}
/// Higher precision altitude than what's available in the GPS whisker. Takes precedent if set. Useful for high altitude balloons.
/// Meters
pub
fn
altitude
(
mut
self
,
val
:
f32
)
->
Self
{
self
.altitude
=
Some
(
val
);
self
}
/// If true, this whisker was emitted by a high altitude balloon payload
/// Please only set this on actual balloon payloads. Otherwise it can cause issues for downstream users!
pub
fn
set_balloon
(
mut
self
)
->
Self
{
self
.balloon
=
true
;
self
}
/// Degrees C
pub
fn
ambient_temperature
(
mut
self
,
val
:
i8
)
->
Self
{
self
.ambient_temperature
=
Some
(
val
);
self
}
/// Relative humidity, percent
pub
fn
ambient_humidity
(
mut
self
,
val
:
f64
)
->
Self
{
self
.ambient_humidity
=
Some
((
val
*
2.55
)
.min
(
255.0
)
as
u8
);
self
}
/// Decapascal (daPa)
pub
fn
ambient_pressure
(
mut
self
,
val
:
u16
)
->
Self
{
self
.ambient_pressure
=
Some
(
val
);
self
}
}
#[cfg(test)]
mod
tests
{
use
super
::
*
;
// verify examples in the standard doc
#[test]
fn
node_info_doc_examples
()
{
let
node_info
=
NodeInfoBuilder
::
default
()
.hardware_id
(
7408
)
.uptime
(
98
)
.tx_power
(
30.0
)
.voltage
(
12.8
)
.build
();
let
mut
buf
=
[
0
;
255
];
let
encoded
=
node_info
.encode
(
&
mut
buf
)
.unwrap
();
assert_eq!
(
[
0x0B
,
0x00
,
0x00
,
0x65
,
0xF0
,
0x1C
,
0x62
,
0x00
,
0x00
,
0x00
,
0x78
,
0x80
],
encoded
);
}
}
This diff is collapsed.
Click to expand it.
src/whisker/route.rs
View file @
9a18248f
use
arrayvec
::
ArrayVec
;
use
crate
::{
error
::
AppendNodeError
,
identity
::
Identity
};
#[derive(Debug,
PartialEq,
Eq,
Clone)]
pub
struct
Route
{
pub
max_hops
:
u8
,
...
...
@@ -19,29 +21,63 @@ impl Route {
}
/// Returns `None` if there isn't enough space for the callsign
/// Returns `None` if attempting to pushed is_used=true after an existing is_used=false
/// is_future=false when this callsign has digipeated
/// is_future=true when this callsign is expected to be part of the route, but isn't yet
/// see the specs for more information
pub
fn
push_callsign
(
&
mut
self
,
callsign
:
&
str
,
ssid
:
u8
,
is_future
:
bool
)
->
Option
<
()
>
{
let
len
=
callsign
.as_bytes
()
.len
()
+
2
;
/// Returns `None` if attempting to push past after future hop
/// See the specs for more information
#[must_use]
pub
fn
push_hop
(
&
mut
self
,
hop
:
RouteHop
)
->
Option
<
()
>
{
match
hop
{
RouteHop
::
Internet
=>
self
.push_internet
(),
RouteHop
::
Past
(
past_hop
)
=>
self
.push_past
(
past_hop
),
RouteHop
::
Future
(
ident
)
=>
self
.push_future
(
ident
),
}
}
/// Returns `None` if there isn't enough space for the callsign
/// Returns `None` if attempting to push after future hop
/// See the specs for more information
#[must_use]
pub
fn
push_past
(
&
mut
self
,
past_hop
:
PastHop
)
->
Option
<
()
>
{
let
ident
=
past_hop
.identity
();
let
len
=
ident
.callsign
()
.as_bytes
()
.len
()
+
3
;
let
free_space
=
self
.path
.capacity
()
-
self
.path
.len
();
if
len
>
free_space
{
return
None
;
}
self
.has_future
=
self
.has_future
||
is_future
;
if
self
.has_future
&&
!
is_future
{
if
self
.has_future
{
return
None
;
}
// safe to unwrap since we already did a length check
self
.path
.try_extend_from_slice
(
ident
.callsign
()
.as_bytes
())
.unwrap
();
self
.path
.push
(
0xFF
);
self
.path
.push
(
ident
.ssid
());
self
.path
.push
(
past_hop
.rssi
);
Some
(())
}
/// Returns `None` if there isn't enough space for the callsign
#[must_use]
pub
fn
push_future
(
&
mut
self
,
ident
:
Identity
)
->
Option
<
()
>
{
let
len
=
ident
.callsign
()
.as_bytes
()
.len
()
+
2
;
let
free_space
=
self
.path
.capacity
()
-
self
.path
.len
();
if
len
>
free_space
{
return
None
;
}
self
.has_future
=
true
;
// safe to unwrap since we already did a length check
self
.path
.try_extend_from_slice
(
callsign
.as_bytes
())
.try_extend_from_slice
(
ident
.
callsign
()
.as_bytes
())
.unwrap
();
self
.path
.push
(
if
is_future
{
0xFD
}
else
{
0xFF
}
);
self
.path
.push
(
ssid
);
self
.path
.push
(
0xFD
);
self
.path
.push
(
ident
.
ssid
()
);
Some
(())
}
...
...
@@ -59,6 +95,68 @@ impl Route {
Some
(())
}
/// Append a callsign/ssid pair to the route, intelligently.
/// I.e. replace future node if possible
/// Returns an Err if the route is out of space, or appending the node doesn't make logical sense
/// (there's a future node that doesn't match)
/// This only appends past hops.
pub
fn
append_hop
(
&
mut
self
,
new_hop
:
PastHop
)
->
Result
<
(),
AppendNodeError
>
{
let
mut
new_route
=
Route
::
new
(
self
.max_hops
);
let
mut
already_inserted
=
false
;
for
rn
in
self
.iter
()
{
match
rn
{
RouteHop
::
Internet
=>
new_route
.push_internet
()
.ok_or
(
AppendNodeError
::
RouteOverflow
)
?
,
RouteHop
::
Past
(
prev_past_hop
)
=>
{
let
us
=
prev_past_hop
.identity
()
==
new_hop
.identity
();
if
us
{
return
Err
(
AppendNodeError
::
DuplicateNode
);
}
else
{
new_route
.push_past
(
prev_past_hop
)
.ok_or
(
AppendNodeError
::
RouteOverflow
)
?
;
}
}
RouteHop
::
Future
(
prev_ident
)
=>
{
let
us
=
prev_ident
==
new_hop
.identity
();
if
us
{
if
already_inserted
{
return
Err
(
AppendNodeError
::
DuplicateNode
);
}
else
{
new_route
.push_past
(
new_hop
)
.ok_or
(
AppendNodeError
::
RouteOverflow
)
?
;
already_inserted
=
true
;
}
}
else
if
already_inserted
{
new_route
.push_future
(
prev_ident
)
.ok_or
(
AppendNodeError
::
RouteOverflow
)
?
;
}
else
{
return
Err
(
AppendNodeError
::
SetFuture
);
}
}
}
}
if
!
already_inserted
{
new_route
.push_past
(
new_hop
)
.ok_or
(
AppendNodeError
::
RouteOverflow
)
?
;
}
if
new_route
.iter
()
.count
()
>
usize
::
from
(
new_route
.max_hops
)
{
return
Err
(
AppendNodeError
::
HopsOverflow
);
}
*
self
=
new_route
;
Ok
(())
}
pub
fn
iter
(
&
'_
self
)
->
RouteIter
<
'_
>
{
RouteIter
::
new
(
self
)
}
...
...
@@ -73,7 +171,6 @@ impl Route {
Some
(
buf
)
}
// TODO reject packets that have a 0xFF anywhere after an FD
pub
fn
decode
(
data
:
&
[
u8
])
->
Option
<
Self
>
{
let
len
:
usize
=
(
*
data
.first
()
?
)
.into
();
let
data
=
data
.get
(
1
..
(
len
+
1
))
?
;
...
...
@@ -85,57 +182,320 @@ impl Route {
let
has_future
=
data
[
1
..
]
.iter
()
.any
(|
x
|
*
x
==
0xFD
);
Some
(
Self
{
let
s
=
Self
{
max_hops
,
path
,
has_future
,
})
};
if
UntrustedRouteIter
::
new
(
&
s
)
.any
(|
v
|
v
.is_err
())
{
return
None
;
}
Some
(
s
)
}
}
#[derive(Debug,
Eq,
PartialEq)]
pub
enum
Route
Node
<
'a
>
{
#[derive(Debug,
Eq,
PartialEq
,
Copy,
Clone
)]
pub
enum
Route
Hop
<
'a
>
{
Internet
,
Identity
(
&
'a
str
,
u8
,
bool
),
Past
(
PastHop
<
'a
>
),
Future
(
Identity
<
'a
>
),
}
#[derive(Debug)]
pub
struct
RouteIter
<
'a
>
{
#[derive(Debug,
Eq,
PartialEq,
Copy,
Clone)]
pub
struct
PastHop
<
'a
>
{
identity
:
Identity
<
'a
>
,
rssi
:
u8
,
}
impl
<
'a
>
PastHop
<
'a
>
{
pub
fn
new
(
identity
:
Identity
<
'a
>
,
rssi
:
Option
<
f64
>
)
->
Self
{
let
rssi
=
match
rssi
{
Some
(
rssi
)
=>
(
rssi
*
1.5
+
240.0
)
.max
(
1.0
)
as
u8
,
None
=>
0
,
};
Self
{
identity
,
rssi
}
}
pub
fn
identity
(
&
self
)
->
Identity
{
self
.identity
}
pub
fn
rssi
(
&
self
)
->
Option
<
f64
>
{
if
self
.rssi
==
0
{
None
}
else
{
Some
(((
self
.rssi
as
f64
)
-
240.0
)
/
1.5
)
}
}
}
#[derive(Debug,
Clone)]
struct
UntrustedRouteIter
<
'a
>
{
route
:
&
'a
Route
,
i
:
usize
,
seen_future
:
bool
,
dead
:
bool
,
}
impl
<
'a
>
RouteIter
<
'a
>
{
impl
<
'a
>
Untrusted
RouteIter
<
'a
>
{
fn
new
(
route
:
&
'a
Route
)
->
Self
{
Self
{
route
,
i
:
0
}
Self
{
route
,
i
:
0
,
seen_future
:
false
,
dead
:
false
,
}
}
// Returns Err(()) if invalid
fn
maybe_next
(
&
mut
self
)
->
Result
<
RouteHop
<
'a
>
,
()
>
{
let
i_start
=
self
.i
;
self
.i
+=
1
;
if
*
self
.route.path
.get
(
i_start
)
.ok_or
(())
?
==
0xFE
{
return
Ok
(
RouteHop
::
Internet
);
}
while
*
self
.route.path
.get
(
self
.i
)
.ok_or
(())
?
!=
0xFD
&&
self
.route.path
[
self
.i
]
!=
0xFF
{
self
.i
+=
1
;
}
let
callsign
=
core
::
str
::
from_utf8
(
self
.route.path
.get
(
i_start
..
self
.i
)
.ok_or
(())
?
)
.map_err
(|
_
|
())
?
;
let
is_future
=
*
self
.route.path
.get
(
self
.i
)
.ok_or
(())
?
==
0xFD
;
if
self
.seen_future
&&
!
is_future
{
// past after future - not allowed
return
Err
(());
}
self
.seen_future
|
=
is_future
;
let
ssid
=
*
self
.route.path
.get
(
self
.i
+
1
)
.ok_or
(())
?
;
self
.i
+=
2
;
if
is_future
{
Ok
(
RouteHop
::
Future
(
Identity
::
new
(
callsign
,
ssid
)))
}
else
{
let
rssi
=
*
self
.route.path
.get
(
self
.i
)
.ok_or
(())
?
;
self
.i
+=
1
;
Ok
(
RouteHop
::
Past
(
PastHop
{
identity
:
Identity
::
new
(
callsign
,
ssid
),
rssi
,
}))
}
}
}
impl
<
'a
>
Iterator
for
RouteIter
<
'a
>
{
type
Item
=
R
outeNode
<
'a
>
;
impl
<
'a
>
Iterator
for
Untrusted
RouteIter
<
'a
>
{
type
Item
=
R
esult
<
RouteHop
<
'a
>
,
()
>
;
fn
next
(
&
mut
self
)
->
Option
<
Self
::
Item
>
{
if
self
.dead
{
return
None
;
}
if
self
.i
==
self
.route.path
.len
()
{
return
None
;
}
let
i_start
=
self
.i
;
self
.i
+=
1
;
let
r
=
self
.maybe_next
();
if
self
.route.path
[
i_start
]
==
0xFE
{
return
Some
(
RouteNode
::
Internet
)
;
if
r
.is_err
()
{
self
.dead
=
true
;
}
while
self
.route.path
[
self
.i
]
!=
0xFD
&&
self
.route.path
[
self
.i
]
!=
0xFF
{
self
.i
+=
1
;
Some
(
r
)
}
}
#[derive(Clone)]
pub
struct
RouteIter
<
'a
>
{
iter
:
UntrustedRouteIter
<
'a
>
,
}
impl
<
'a
>
RouteIter
<
'a
>
{
fn
new
(
route
:
&
'a
Route
)
->
Self
{
Self
{
iter
:
UntrustedRouteIter
::
new
(
route
),
}
}
}
let
callsign
=
core
::
str
::
from_utf8
(
&
self
.route.path
[
i_start
..
self
.i
])
.unwrap
();
let
is_future
=
self
.route.path
[
self
.i
]
==
0xFD
;
self
.i
+=
1
;
let
ssid
=
self
.route.path
[
self
.i
];
self
.i
+=
1
;
impl
<
'a
>
Iterator
for
RouteIter
<
'a
>
{
type
Item
=
RouteHop
<
'a
>
;
fn
next
(
&
mut
self
)
->
Option
<
Self
::
Item
>
{
Some
(
self
.iter
.next
()
?
.unwrap
())
}
}
#[cfg(test)]
mod
tests
{
use
super
::
*
;
#[test]
fn
route_rssi
()
{
let
x
=
-
66.0
;
let
r
=
PastHop
::
new
(
Identity
::
new
(
"C0"
,
23
),
Some
(
x
));
assert_eq!
(
x
,
r
.rssi
()
.unwrap
());
let
x
=
10.0
;
let
r
=
PastHop
::
new
(
Identity
::
new
(
"C0"
,
23
),
Some
(
x
));
assert_eq!
(
x
,
r
.rssi
()
.unwrap
());
let
x
=
-
158.0
;
let
r
=
PastHop
::
new
(
Identity
::
new
(
"C0"
,
23
),
Some
(
x
));
assert_eq!
(
x
,
r
.rssi
()
.unwrap
());
let
r
=
PastHop
::
new
(
Identity
::
new
(
"C0"
,
23
),
None
);
assert_eq!
(
None
,
r
.rssi
());
}
#[test]
fn
append_fails_when_existing_future
()
{
let
mut
r
=
Route
::
new
(
5
);
r
.push_past
(
PastHop
::
new
(
Identity
::
new
(
"C1"
,
0
),
None
))
.unwrap
();
r
.push_past
(
PastHop
::
new
(
Identity
::
new
(
"C2"
,
0
),
None
))
.unwrap
();
r
.push_future
(
Identity
::
new
(
"C3"
,
0
))
.unwrap
();
assert_eq!
(
AppendNodeError
::
SetFuture
,
r
.append_hop
(
PastHop
::
new
(
Identity
::
new
(
"C3"
,
1
),
None
))
.unwrap_err
()
);
assert_eq!
(
AppendNodeError
::
SetFuture
,
r
.append_hop
(
PastHop
::
new
(
Identity
::
new
(
"C4"
,
0
),
None
))
.unwrap_err
()
);
}
#[test]
fn
append_fails_when_would_exceed_max_hops
()
{
let
mut
r
=
Route
::
new
(
3
);
r
.append_hop
(
PastHop
::
new
(
Identity
::
new
(
"C1"
,
0
),
None
))
.unwrap
();
r
.append_hop
(
PastHop
::
new
(
Identity
::
new
(
"C2"
,
0
),
None
))
.unwrap
();
r
.append_hop
(
PastHop
::
new
(
Identity
::
new
(
"C2"
,
1
),
None
))
.unwrap
();
assert_eq!
(
AppendNodeError
::
HopsOverflow
,
r
.append_hop
(
PastHop
::
new
(
Identity
::
new
(
"C4"
,
0
),
None
))
.unwrap_err
()
);
}
#[test]
fn
append_fails_when_already_in_route
()
{
let
mut
r
=
Route
::
new
(
3
);
r
.append_hop
(
PastHop
::
new
(
Identity
::
new
(
"C1"
,
0
),
None
))
.unwrap
();
assert_eq!
(
AppendNodeError
::
DuplicateNode
,
r
.append_hop
(
PastHop
::
new
(
Identity
::
new
(
"C1"
,
0
),
None
))
.unwrap_err
()
);
}
#[test]
fn
append_overwrites_future
()
{
let
mut
r
=
Route
::
new
(
5
);
r
.push_past
(
PastHop
::
new
(
Identity
::
new
(
"C1"
,
0
),
None
))
.unwrap
();
r
.push_past
(
PastHop
::
new
(
Identity
::
new
(
"C2"
,
0
),
None
))
.unwrap
();
r
.push_future
(
Identity
::
new
(
"C3"
,
0
))
.unwrap
();
r
.push_future
(
Identity
::
new
(
"C4"
,
0
))
.unwrap
();
r
.append_hop
(
PastHop
::
new
(
Identity
::
new
(
"C3"
,
0
),
None
))
.unwrap
();
let
mut
iter
=
r
.iter
();
assert_eq!
(
Some
(
RouteHop
::
Past
(
PastHop
::
new
(
Identity
::
new
(
"C1"
,
0
),
None
))),
iter
.next
()
);
assert_eq!
(
Some
(
RouteHop
::
Past
(
PastHop
::
new
(
Identity
::
new
(
"C2"
,
0
),
None
))),
iter
.next
()
);
assert_eq!
(
Some
(
RouteHop
::
Past
(
PastHop
::
new
(
Identity
::
new
(
"C3"
,
0
),
None
))),
iter
.next
()
);
assert_eq!
(
Some
(
RouteHop
::
Future
(
Identity
::
new
(
"C4"
,
0
))),
iter
.next
());
assert_eq!
(
None
,
iter
.next
());
}
#[test]
fn
append_overwrites_future_even_when_at_hop_limit
()
{
let
mut
r
=
Route
::
new
(
4
);
r
.push_past
(
PastHop
::
new
(
Identity
::
new
(
"C1"
,
0
),
None
))
.unwrap
();
r
.push_past
(
PastHop
::
new
(
Identity
::
new
(
"C2"
,
0
),
None
))
.unwrap
();
r
.push_future
(
Identity
::
new
(
"C3"
,
0
))
.unwrap
();
r
.push_future
(
Identity
::
new
(
"C4"
,
0
))
.unwrap
();
r
.append_hop
(
PastHop
::
new
(
Identity
::
new
(
"C3"
,
0
),
None
))
.unwrap
();
let
mut
iter
=
r
.iter
();
assert_eq!
(
Some
(
RouteHop
::
Past
(
PastHop
::
new
(
Identity
::
new
(
"C1"
,
0
),
None
))),
iter
.next
()
);
assert_eq!
(
Some
(
RouteHop
::
Past
(
PastHop
::
new
(
Identity
::
new
(
"C2"
,
0
),
None
))),
iter
.next
()
);
assert_eq!
(
Some
(
RouteHop
::
Past
(
PastHop
::
new
(
Identity
::
new
(
"C3"
,
0
),
None
))),
iter
.next
()
);
assert_eq!
(
Some
(
RouteHop
::
Future
(
Identity
::
new
(
"C4"
,
0
,))),
iter
.next
());
assert_eq!
(
None
,
iter
.next
());
}
#[test]
fn
append_appends_when_no_future
()
{
let
mut
r
=
Route
::
new
(
5
);
r
.push_past
(
PastHop
::
new
(
Identity
::
new
(
"C1"
,
0
),
None
))
.unwrap
();
r
.push_past
(
PastHop
::
new
(
Identity
::
new
(
"C2"
,
0
),
None
))
.unwrap
();
r
.push_past
(
PastHop
::
new
(
Identity
::
new
(
"C3"
,
0
),
None
))
.unwrap
();
r
.append_hop
(
PastHop
::
new
(
Identity
::
new
(
"C4"
,
0
),
None
))
.unwrap
();
Some
(
RouteNode
::
Identity
(
callsign
,
ssid
,
is_future
))
let
mut
iter
=
r
.iter
();
assert_eq!
(
Some
(
RouteHop
::
Past
(
PastHop
::
new
(
Identity
::
new
(
"C1"
,
0
),
None
))),
iter
.next
()
);
assert_eq!
(
Some
(
RouteHop
::
Past
(
PastHop
::
new
(
Identity
::
new
(
"C2"
,
0
),
None
))),
iter
.next
()
);
assert_eq!
(
Some
(
RouteHop
::
Past
(
PastHop
::
new
(
Identity
::
new
(
"C3"
,
0
),
None
))),
iter
.next
()
);
assert_eq!
(
Some
(
RouteHop
::
Past
(
PastHop
::
new
(
Identity
::
new
(
"C4"
,
0
),
None
))),
iter
.next
()
);
assert_eq!
(
None
,
iter
.next
());
}
}
This diff is collapsed.
Click to expand it.
src/whitener.rs
View file @
9a18248f
const
WHITE
:
[
u8
;
16
]
=
[
0xe9
,
0xcf
,
0x67
,
0x20
,
0x19
,
0x1a
,
0x07
,
0xdc
,
0xc0
,
0x72
,
0x79
,
0x97
,
0x51
,
0xf7
,
0xdd
,
0x93
,
];
const
START_STATE
:
u16
=
0xE9CF
;
pub
(
crate
)
fn
whiten
(
data
:
&
mut
[
u8
])
{
for
(
i
,
d
)
in
data
.iter_mut
()
.enumerate
()
{
*
d
^=
WHITE
[
i
%
15
];
let
mut
state
=
START_STATE
;
for
d
in
data
.iter_mut
()
{
let
b
;
(
b
,
state
)
=
lfsr_byte
(
state
);
*
d
^=
b
;
}
}
// (byte, state)
fn
lfsr_byte
(
mut
state
:
u16
)
->
(
u8
,
u16
)
{
let
mut
out
=
0
;
for
i
in
(
0
..
8
)
.rev
()
{
out
|
=
u8
::
try_from
(
state
&
1
)
.unwrap
()
<<
i
;
state
=
lfsr
(
state
);
}
(
out
,
state
)
}
// https://en.wikipedia.org/wiki/Linear-feedback_shift_register#Galois_LFSRs
fn
lfsr
(
mut
state
:
u16
)
->
u16
{
let
lsb
=
state
&
1
;
state
>>=
1
;
if
lsb
>
0
{
state
^=
0xB400
;
// apply toggle mask
}
state
}
#[cfg(test)]
...
...
@@ -17,7 +41,7 @@ mod tests {
let
mut
data
=
[
0
;
64
];
data
[
0
..
57
]
.clone_from_slice
(
&
b"Hello world! The quick brown fox jumped over the lazy dog"
[
..
]);
let
orig
=
data
.clone
()
;
let
orig
=
data
;
whiten
(
&
mut
data
);
assert_ne!
(
orig
,
data
);
...
...
@@ -25,4 +49,54 @@ mod tests {
whiten
(
&
mut
data
);
assert_eq!
(
orig
,
data
);
}
#[test]
fn
test_lfsr
()
{
let
start
=
0xACE1
;
let
end_expected
=
0xE270
;
let
state
=
lfsr
(
start
);
assert_eq!
(
end_expected
,
state
);
}
#[test]
fn
test_lfsr_byte
()
{
let
start
=
0xE9CF
;
let
(
out
,
state
)
=
lfsr_byte
(
start
);
assert_eq!
(
0xF3
,
out
);
assert_eq!
(
0xE3B1
,
state
);
}
#[test]
fn
test_doc_example
()
{
let
start
=
0xE9CF
;
let
expected_out
=
[
0xF3
,
0x8D
,
0xD0
,
0x6E
,
0x1F
,
0x65
,
0x75
,
0x75
,
0xA5
,
0xBA
,
0xA9
,
0xD0
,
0x7A
,
0x1D
,
0x1
,
0x21
,
];
let
mut
actual_out
=
[
0
;
16
];
let
mut
state
=
start
;
for
a
in
&
mut
actual_out
{
let
(
out
,
ns
)
=
lfsr_byte
(
state
);
state
=
ns
;
*
a
=
out
;
}
assert_eq!
(
expected_out
,
actual_out
);
}
#[test]
fn
test_doc_example_through_whitener
()
{
let
expected_out
=
[
0xF3
,
0x8D
,
0xD0
,
0x6E
,
0x1F
,
0x65
,
0x75
,
0x75
,
0xA5
,
0xBA
,
0xA9
,
0xD0
,
0x7A
,
0x1D
,
0x1
,
0x21
,
];
let
mut
actual_out
=
[
0
;
16
];
whiten
(
&
mut
actual_out
);
assert_eq!
(
expected_out
,
actual_out
);
}
}
This diff is collapsed.
Click to expand it.
Prev
1
2
Next