///|
/// A short driven pulse detected by a width threshold.
pub struct GlitchEvent {
signal_id : String
start : Int64
end : Int64
width : Int64
value : Char
} derive(Debug, Eq)
///|
/// A deterministic clock-quality summary.
pub struct JitterReport {
signal_id : String
edges : Int
periods : Int
mean_period : Double?
minimum_period : Int64?
maximum_period : Int64?
mean_absolute_deviation : Double?
} derive(Debug, Eq)
///|
/// Find short known pulses, including both high and low pulses.
pub fn detect_glitches(
trace : Trace,
signal_id : String,
maximum_width : Int64,
) -> Array[GlitchEvent] {
let limit = Int64::max(0L, maximum_width)
driven_intervals(trace, signal_id).filter_map(fn(interval) {
let width = interval.end - interval.start
match interval.value {
Scalar(value) if width > 0L && width <= limit =>
Some({
signal_id,
start: interval.start,
end: interval.end,
width,
value,
})
_ => None
}
})
}
///|
/// Return the shortest known pulse for a signal.
pub fn shortest_pulse(trace : Trace, signal_id : String) -> (Char, Int64)? {
let mut result : (Char, Int64)? = None
for pulse in pulse_widths(trace, signal_id) {
match result {
Some((_, width)) if width <= pulse.1 => ()
_ => result = Some(pulse)
}
}
result
}
///|
/// Return the longest known pulse for a signal.
pub fn longest_pulse(trace : Trace, signal_id : String) -> (Char, Int64)? {
let mut result : (Char, Int64)? = None
for pulse in pulse_widths(trace, signal_id) {
match result {
Some((_, width)) if width >= pulse.1 => ()
_ => result = Some(pulse)
}
}
result
}
///|
/// Compute period jitter from consecutive rising edges.
pub fn jitter_report(trace : Trace, signal_id : String) -> JitterReport {
let estimate = frequency_estimate(trace, signal_id)
let times = edges(trace, signal_id).filter_map(fn(event) {
if event.kind is Rising {
Some(event.time)
} else {
None
}
})
let periods : Array[Int64] = []
if times.length() > 1 {
for i in 1.. 0 =>
Some(
periods.fold(init=0.0, fn(total, period) {
total + (period.to_double() - mean).abs()
}) /
periods.length().to_double(),
)
_ => None
}
{
signal_id,
edges: times.length(),
periods: periods.length(),
mean_period: estimate.mean_period,
minimum_period: estimate.minimum_period,
maximum_period: estimate.maximum_period,
mean_absolute_deviation: deviation,
}
}
///|
/// Return whether all periods lie inside an inclusive tolerance.
pub fn periods_within_tolerance(
trace : Trace,
signal_id : String,
tolerance : Int64,
) -> Bool {
let report = jitter_report(trace, signal_id)
match report.mean_period {
Some(mean) => {
for period in period_values(trace, signal_id) {
if (period.to_double() - mean).abs() > tolerance.to_double() {
return false
}
}
true
}
None => true
}
}
///|
/// Return scalar pulse durations in source order.
pub fn period_values(trace : Trace, signal_id : String) -> Array[Int64] {
let times = edges(trace, signal_id).filter_map(fn(event) {
if event.kind is Rising {
Some(event.time)
} else {
None
}
})
let result : Array[Int64] = []
if times.length() > 1 {
for i in 1.. String {
let out : Array[String] = ["signal,start,end,width,value"]
for glitch in glitches {
out.push(
glitch.signal_id +
"," +
glitch.start.to_string() +
"," +
glitch.end.to_string() +
"," +
glitch.width.to_string() +
"," +
glitch.value.to_string(),
)
}
out.join("\n")
}
///|
/// Estimate the fraction of driven intervals that are short pulses.
pub fn glitch_ratio(
trace : Trace,
signal_id : String,
maximum_width : Int64,
) -> Double {
let all = driven_intervals(trace, signal_id).length()
if all == 0 {
0.0
} else {
detect_glitches(trace, signal_id, maximum_width).length().to_double() /
all.to_double()
}
}