///|
/// Create deterministic analysis options for the complete trace.
pub fn default_analysis_options(trace : Trace) -> AnalysisOptions {
{
start: Some(0L),
end: Some(trace.end_time),
step: 1L,
include_initial: true,
include_unknown: true,
}
}
///|
/// Return a normalized copy of analysis options.
pub fn AnalysisOptions::normalized(
self : AnalysisOptions,
trace : Trace,
) -> AnalysisOptions {
let start = match self.start {
Some(value) => Int64::max(0L, value)
None => 0L
}
let end = match self.end {
Some(value) => Int64::min(trace.end_time, Int64::max(start, value))
None => trace.end_time
}
{
start: Some(start),
end: Some(end),
step: Int64::max(1L, self.step),
include_initial: self.include_initial,
include_unknown: self.include_unknown,
}
}
///|
/// Sample one signal at regular timestamps.
pub fn sample_signal(
trace : Trace,
signal_id : String,
options : AnalysisOptions,
) -> Array[Sample] {
let safe = options.normalized(trace)
let start = safe.start.unwrap()
let end = safe.end.unwrap()
let result : Array[Sample] = []
let mut cursor = start
while cursor <= end {
let current = value_at(trace, signal_id, cursor)
if safe.include_unknown || current is Some(_) {
result.push({ signal_id, time: cursor, value: current })
}
cursor += safe.step
}
if safe.include_initial && result.length() == 0 {
result.push({
signal_id,
time: start,
value: value_at(trace, signal_id, start),
})
}
result
}
///|
/// Sample every declared signal using the same time grid.
pub fn sample_all(
trace : Trace,
options : AnalysisOptions,
) -> Array[(String, Array[Sample])] {
trace.signals.map(fn(signal) {
(signal.id, sample_signal(trace, signal.id, options))
})
}
///|
/// Compute time-weighted activity for one signal.
pub fn activity(trace : Trace, signal_id : String) -> SignalActivity {
let intervals = value_intervals(trace, signal_id)
let mut known = 0
let mut unknown = 0
let mut active : Int64 = 0L
let mut high : Int64 = 0L
let mut low : Int64 = 0L
for interval in intervals {
let duration = Int64::max(0L, interval.end - interval.start)
match interval.value {
Scalar(value) =>
if is_known_scalar(value) {
known += 1
active += duration
if value == '1' {
high += duration
} else {
low += duration
}
} else {
unknown += 1
}
_ => unknown += 1
}
}
{
signal_id,
samples: intervals.length(),
known_samples: known,
unknown_samples: unknown,
active_time: active,
high_time: high,
low_time: low,
transition_count: edges(trace, signal_id).length(),
}
}
///|
/// Compute activity summaries for all signals.
pub fn all_activity(trace : Trace) -> Array[SignalActivity] {
trace.signals.map(fn(signal) { activity(trace, signal.id) })
}
///|
/// Return the proportion of the trace spent at logical high.
pub fn high_ratio(trace : Trace, signal_id : String) -> Double? {
if trace.end_time <= 0L {
None
} else {
Some(
activity(trace, signal_id).high_time.to_double() /
trace.end_time.to_double(),
)
}
}
///|
/// Return the proportion of the trace spent at logical low.
pub fn low_ratio(trace : Trace, signal_id : String) -> Double? {
if trace.end_time <= 0L {
None
} else {
Some(
activity(trace, signal_id).low_time.to_double() /
trace.end_time.to_double(),
)
}
}
///|
/// Return the number of transitions per trace time unit.
pub fn transition_rate(trace : Trace, signal_id : String) -> Double? {
if trace.end_time <= 0L {
None
} else {
Some(
edges(trace, signal_id).length().to_double() / trace.end_time.to_double(),
)
}
}
///|
/// Calculate the total time for a scalar value.
pub fn dwell_time(trace : Trace, signal_id : String, value : Char) -> Int64 {
intervals_for_value(trace, signal_id, value).fold(init=0L, fn(
total,
interval,
) {
total + Int64::max(0L, interval.end - interval.start)
})
}
///|
/// Return the number of samples with unknown or high-impedance values.
pub fn unknown_sample_count(trace : Trace, signal_id : String) -> Int {
value_intervals(trace, signal_id).count_if(fn(interval) {
interval.value.is_unknown()
})
}