use embassy_time::Instant;
use portable_atomic::{AtomicBool, AtomicU8, AtomicU32, AtomicU64, Ordering};
use crate::wire::{Envelope, SessionInfo, SourceKind, Stimulus};
static NODE_ID: AtomicU64 = AtomicU64::new(0);
static SESSION_ID: AtomicU32 = AtomicU32::new(0);
static SESSION_PINNED: AtomicBool = AtomicBool::new(false);
static EPOCH_UNIX_US: AtomicU64 = AtomicU64::new(0);
static EPOCH_AT_TICKS: AtomicU64 = AtomicU64::new(0);
static EPOCH_SET: AtomicBool = AtomicBool::new(false);
static STREAM_SEQ: AtomicU32 = AtomicU32::new(0);
static TS_LAST_RAW: AtomicU32 = AtomicU32::new(0);
static TS_WRAPS: AtomicU32 = AtomicU32::new(0);
static ANCHOR_PENDING: AtomicBool = AtomicBool::new(false);
static STIMULUS_CONTROLLED: AtomicBool = AtomicBool::new(false);
static SETUP_ID: AtomicU8 = AtomicU8::new(0);
static SOUNDING_INSTANCE: AtomicU32 = AtomicU32::new(0);
static HEADER_DIGEST: AtomicBool = AtomicBool::new(false);
pub fn set_session(session_id: u32, epoch_unix_us: Option<u64>) {
SESSION_ID.store(session_id, Ordering::Relaxed);
SESSION_PINNED.store(true, Ordering::Relaxed);
match epoch_unix_us {
Some(us) => {
EPOCH_UNIX_US.store(us, Ordering::Relaxed);
EPOCH_AT_TICKS.store(Instant::now().as_micros(), Ordering::Relaxed);
EPOCH_SET.store(true, Ordering::Release);
}
None => EPOCH_SET.store(false, Ordering::Release),
}
}
pub fn session_id() -> u32 {
SESSION_ID.load(Ordering::Relaxed)
}
pub fn node_id() -> [u8; 6] {
let mut packed = NODE_ID.load(Ordering::Relaxed);
if packed == 0 {
let mac = esp_hal::efuse::base_mac_address();
let mut v = 0u64;
for &b in mac.as_bytes() {
v = (v << 8) | b as u64;
}
NODE_ID.store(v, Ordering::Relaxed);
packed = v;
}
let mut out = [0u8; 6];
for (i, b) in out.iter_mut().enumerate() {
*b = (packed >> (8 * (5 - i))) as u8;
}
out
}
pub fn set_header_digest_enabled(enabled: bool) {
HEADER_DIGEST.store(enabled, Ordering::Relaxed);
}
pub fn header_digest_enabled() -> bool {
HEADER_DIGEST.load(Ordering::Relaxed)
}
pub(crate) fn begin_run(controlled: bool, header_digest: bool) {
let _ = node_id();
if !SESSION_PINNED.load(Ordering::Relaxed) {
SESSION_ID.store(esp_hal::rng::Rng::new().random(), Ordering::Relaxed);
}
STREAM_SEQ.store(0, Ordering::Relaxed);
TS_LAST_RAW.store(0, Ordering::Relaxed);
TS_WRAPS.store(0, Ordering::Relaxed);
STIMULUS_CONTROLLED.store(controlled, Ordering::Relaxed);
SETUP_ID.store(0, Ordering::Relaxed);
SOUNDING_INSTANCE.store(0, Ordering::Relaxed);
HEADER_DIGEST.store(header_digest, Ordering::Relaxed);
ANCHOR_PENDING.store(true, Ordering::Release);
}
pub(crate) fn set_sounding_instance(instance: u32) {
SOUNDING_INSTANCE.store(instance, Ordering::Relaxed);
}
pub(crate) fn set_setup_id(id: u8) {
SETUP_ID.store(id, Ordering::Relaxed);
}
pub(crate) fn widen_timestamp(raw: u32) -> u64 {
const HALF: u32 = u32::MAX / 2;
let last = TS_LAST_RAW.load(Ordering::Relaxed);
let mut wraps = TS_WRAPS.load(Ordering::Relaxed);
if raw < last {
if last - raw > HALF {
wraps = wraps.wrapping_add(1);
TS_WRAPS.store(wraps, Ordering::Relaxed);
TS_LAST_RAW.store(raw, Ordering::Relaxed);
}
} else if raw - last > HALF && wraps > 0 {
return (((wraps - 1) as u64) << 32) | raw as u64;
} else {
TS_LAST_RAW.store(raw, Ordering::Relaxed);
}
((wraps as u64) << 32) | raw as u64
}
pub(crate) fn next_envelope(source: SourceKind) -> Envelope {
Envelope::new(
node_id(),
SESSION_ID.load(Ordering::Relaxed),
source,
STREAM_SEQ.fetch_add(1, Ordering::Relaxed),
)
}
pub(crate) fn controlled() -> bool {
STIMULUS_CONTROLLED.load(Ordering::Relaxed)
}
pub(crate) fn stimulus(ta: [u8; 6], instance: Option<u32>) -> Stimulus {
if STIMULUS_CONTROLLED.load(Ordering::Relaxed) {
Stimulus::Controlled {
setup_id: SETUP_ID.load(Ordering::Relaxed),
instance_id: instance.unwrap_or_else(|| SOUNDING_INSTANCE.load(Ordering::Relaxed)) as u16,
ta,
}
} else {
Stimulus::Ambient { ta }
}
}
pub(crate) fn take_announcement(timestamp_us: u64) -> Option<SessionInfo> {
if !ANCHOR_PENDING.swap(false, Ordering::AcqRel) {
return None;
}
let epoch = if EPOCH_SET.load(Ordering::Acquire) {
let elapsed = Instant::now()
.as_micros()
.saturating_sub(EPOCH_AT_TICKS.load(Ordering::Relaxed));
Some(EPOCH_UNIX_US.load(Ordering::Relaxed) + elapsed)
} else {
None
};
Some(SessionInfo::new(
super::esp::CHIP,
crate_version(),
timestamp_us,
epoch,
))
}
pub(crate) const fn crate_version() -> [u8; 3] {
const fn parse(s: &str) -> u8 {
let b = s.as_bytes();
let mut v = 0u8;
let mut i = 0;
while i < b.len() {
v = v * 10 + (b[i] - b'0');
i += 1;
}
v
}
[
parse(env!("CARGO_PKG_VERSION_MAJOR")),
parse(env!("CARGO_PKG_VERSION_MINOR")),
parse(env!("CARGO_PKG_VERSION_PATCH")),
]
}