///|
/// Extract scalar transitions for one signal.
pub fn edges(trace : Trace, signal_id : String) -> Array[EdgeEvent] {
let result : Array[EdgeEvent] = []
let mut previous : Char? = None
for change in trace.changes {
if change.id != signal_id {
continue
}
match change.value {
Scalar(current) =>
match previous {
Some(before) if before != current =>
result.push({
signal_id,
time: change.time,
before,
after: current,
kind: edge_kind(before, current),
})
_ => ()
}
_ => previous = None
}
match change.value {
Scalar(current) => previous = Some(current)
_ => previous = None
}
}
result
}
///|
/// Extract all scalar transitions, grouped by signal identifier.
pub fn all_edges(trace : Trace) -> Array[(String, Array[EdgeEvent])] {
trace.signals.map(fn(signal) { (signal.id, edges(trace, signal.id)) })
}
///|
/// Count rising, falling, and non-binary transitions.
pub fn edge_counts(trace : Trace, signal_id : String) -> (Int, Int, Int) {
let mut rising = 0
let mut falling = 0
let mut other = 0
for event in edges(trace, signal_id) {
match event.kind {
Rising => rising += 1
Falling => falling += 1
_ => other += 1
}
}
(rising, falling, other)
}
///|
/// Convert changes into stable scalar value intervals.
pub fn value_intervals(
trace : Trace,
signal_id : String,
) -> Array[ValueInterval] {
let result : Array[ValueInterval] = []
let mut active : ValueInterval? = None
for change in trace.changes {
if change.id != signal_id {
continue
}
match active {
Some(interval) => {
let end = Int64::max(interval.start, change.time)
result.push({
signal_id,
start: interval.start,
end,
value: interval.value,
})
}
None => ()
}
active = Some({
signal_id,
start: change.time,
end: trace.end_time,
value: change.value,
})
}
match active {
Some(interval) =>
result.push({
signal_id,
start: interval.start,
end: Int64::max(interval.start, trace.end_time),
value: interval.value,
})
None => ()
}
result
}
///|
/// Return intervals with a known scalar level and positive duration.
pub fn driven_intervals(
trace : Trace,
signal_id : String,
) -> Array[ValueInterval] {
value_intervals(trace, signal_id).filter(fn(interval) {
let known = match interval.value {
Scalar(value) => is_known_scalar(value)
_ => false
}
interval.end > interval.start && known
})
}
///|
/// Return pulse widths for scalar high and low levels.
pub fn pulse_widths(trace : Trace, signal_id : String) -> Array[(Char, Int64)] {
driven_intervals(trace, signal_id).filter_map(fn(interval) {
match interval.value {
Scalar(value) => Some((value, interval.end - interval.start))
_ => None
}
})
}
///|
/// Return the intervals occupied by a particular scalar value.
pub fn intervals_for_value(
trace : Trace,
signal_id : String,
value : Char,
) -> Array[ValueInterval] {
value_intervals(trace, signal_id).filter(fn(interval) {
match interval.value {
Scalar(current) => current == value
_ => false
}
})
}
///|
/// Estimate period and frequency from rising edges.
pub fn frequency_estimate(
trace : Trace,
signal_id : String,
) -> FrequencyEstimate {
let rising_times : Array[Int64] = []
for event in edges(trace, signal_id) {
if event.kind is Rising {
rising_times.push(event.time)
}
}
let periods : Array[Int64] = []
if rising_times.length() > 1 {
for i in 1.. Some(Int64::min(value, item))
None => Some(item)
}
})
let max_period = periods.fold(init=None, fn(current, item) {
match current {
Some(value) => Some(Int64::max(value, item))
None => Some(item)
}
})
let frequency = match mean {
Some(value) if value > 0.0 => Some(1.0 / value)
_ => None
}
{
signal_id,
edge_count: rising_times.length(),
period_count: periods.length(),
mean_period: mean,
minimum_period: min_period,
maximum_period: max_period,
frequency,
}
}