Unplugged-cable behavior fixed end to end: heat grid rolls on the slot boundary (view computes the newest slot from the draw clock; history sampled every frame so slots color within a frame; noise phase displayed as commanded straight from the shared clock — radiating and the per-slot vote deleted), SNR ghost panic disambiguated via the PMA 1.1 latch (link dropped mid-read discards the batch, ghost on a proven-up link still dies), measure skips the 25s relink wait when the ECD verdict says faulted and the header says MEASURING instead of GOOD while a diag runs, RollBall mailbox deadline resized to 10s from bench measurement (<25ms linked, ~220ms no-link, >3s during a live plug's training; docs/modules/wiitek updated)

This commit is contained in:
flamingcow
2026-08-17 21:29:02 -07:00
parent 916c0150ef
commit be2bf56fa7
7 changed files with 81 additions and 40 deletions
+13 -6
View File
@@ -65,6 +65,16 @@ type noisePort struct {
frame []byte
}
// The cycle is a pure function of the shared slot clock: even slots up — ports
// admin-up and the wire loud, training or frames — odd slots down. Everything
// that shows the phase computes it from the clock through here rather than
// sampling the actuator, whose ioctls lag the boundary.
func slotUp(seq int64) bool { return seq%2 == 0 }
func noiseUp(now time.Time) bool {
return slotUp(int64(now.Sub(rateEpochStart) / histSlotSpan))
}
type noiser struct {
eps [2]endpoint
ports [2]noisePort
@@ -73,8 +83,6 @@ type noiser struct {
// phase. Latched across the down phase, where the missing carrier is our
// own doing and says nothing about the cable.
connected atomic.Bool
// The cycle's up phase: ports admin-up and the wire loud, training or frames.
radiating atomic.Bool
}
func newNoiser() (*noiser, error) {
@@ -124,9 +132,9 @@ type noiseView struct {
// missing is zero while the cable was there at the last verdict, one while it
// was not: the shape the error cells already colour by, so absence paints as
// the fault it is. on is the cycle's phase, presence granted.
// the fault it is. on is the commanded phase, straight from the clock.
func (n *noiser) view() noiseView {
v := noiseView{on: n.radiating.Load()}
v := noiseView{on: noiseUp(time.Now())}
if !n.connected.Load() {
v.missing = 1
}
@@ -188,7 +196,7 @@ func (n *noiser) run(done *atomic.Bool) {
applied, wasUp, linked := false, false, false
for !done.Load() {
<-tick.C
up := (time.Since(rateEpochStart)/histSlotSpan)%2 == 0
up := noiseUp(time.Now())
if !applied || up != wasUp {
// A whole up phase with no link is many times the ~1s the wire
// needs to train, so at its end the silence is the cable's answer.
@@ -196,7 +204,6 @@ func (n *noiser) run(done *atomic.Bool) {
n.connected.Store(linked)
}
n.setLinks(fd, up)
n.radiating.Store(up)
applied, wasUp, linked = true, up, false
}
if !up || !n.bothUp() {