diff --git a/static/css/modes/sstv-general.css b/static/css/modes/sstv-general.css
index 9ebad84..1bbec01 100644
--- a/static/css/modes/sstv-general.css
+++ b/static/css/modes/sstv-general.css
@@ -465,6 +465,20 @@
text-align: center;
}
+.sstv-general-signal-vis-state {
+ font-family: var(--font-mono);
+ font-size: 9px;
+ color: var(--text-dim);
+ text-align: center;
+ margin-top: 6px;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+
+.sstv-general-signal-vis-state.active {
+ color: var(--accent-cyan);
+}
+
/* ============================================
IMAGE MODAL
============================================ */
diff --git a/static/css/modes/sstv.css b/static/css/modes/sstv.css
index 588a25b..896a740 100644
--- a/static/css/modes/sstv.css
+++ b/static/css/modes/sstv.css
@@ -812,6 +812,20 @@
text-align: center;
}
+.sstv-signal-vis-state {
+ font-family: var(--font-mono);
+ font-size: 9px;
+ color: var(--text-dim);
+ text-align: center;
+ margin-top: 6px;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+
+.sstv-signal-vis-state.active {
+ color: var(--accent-cyan);
+}
+
/* ============================================
IMAGE MODAL
============================================ */
diff --git a/static/js/modes/sstv-general.js b/static/js/modes/sstv-general.js
index f023f0a..c0856e4 100644
--- a/static/js/modes/sstv-general.js
+++ b/static/js/modes/sstv-general.js
@@ -308,6 +308,7 @@ const SSTVGeneral = (function() {
0
No signal
+ VIS: idle
`;
monitor = container.querySelector('.sstv-general-signal-monitor');
}
@@ -317,6 +318,24 @@ const SSTVGeneral = (function() {
fill.style.background = barColor;
monitor.querySelector('.sstv-general-signal-status-text').textContent = statusText;
monitor.querySelector('.sstv-general-signal-level-value').textContent = level;
+
+ const visStateEl = monitor.querySelector('.sstv-general-signal-vis-state');
+ if (visStateEl && data.vis_state) {
+ const stateLabels = {
+ 'idle': 'Idle',
+ 'leader_1': 'Leader',
+ 'break': 'Break',
+ 'leader_2': 'Leader 2',
+ 'start_bit': 'Start bit',
+ 'data_bits': 'Data bits',
+ 'parity': 'Parity',
+ 'stop_bit': 'Stop bit',
+ };
+ const label = stateLabels[data.vis_state] || data.vis_state;
+ visStateEl.textContent = 'VIS: ' + label;
+ visStateEl.className = 'sstv-general-signal-vis-state' +
+ (data.vis_state !== 'idle' ? ' active' : '');
+ }
}
/**
diff --git a/static/js/modes/sstv.js b/static/js/modes/sstv.js
index 2d02a24..227a7b6 100644
--- a/static/js/modes/sstv.js
+++ b/static/js/modes/sstv.js
@@ -743,6 +743,7 @@ const SSTV = (function() {
0
No signal
+ VIS: idle
`;
monitor = container.querySelector('.sstv-signal-monitor');
}
@@ -752,6 +753,24 @@ const SSTV = (function() {
fill.style.background = barColor;
monitor.querySelector('.sstv-signal-status-text').textContent = statusText;
monitor.querySelector('.sstv-signal-level-value').textContent = level;
+
+ const visStateEl = monitor.querySelector('.sstv-signal-vis-state');
+ if (visStateEl && data.vis_state) {
+ const stateLabels = {
+ 'idle': 'Idle',
+ 'leader_1': 'Leader',
+ 'break': 'Break',
+ 'leader_2': 'Leader 2',
+ 'start_bit': 'Start bit',
+ 'data_bits': 'Data bits',
+ 'parity': 'Parity',
+ 'stop_bit': 'Stop bit',
+ };
+ const label = stateLabels[data.vis_state] || data.vis_state;
+ visStateEl.textContent = 'VIS: ' + label;
+ visStateEl.className = 'sstv-signal-vis-state' +
+ (data.vis_state !== 'idle' ? ' active' : '');
+ }
}
/**
diff --git a/utils/sstv/sstv_decoder.py b/utils/sstv/sstv_decoder.py
index 3035adc..f73c639 100644
--- a/utils/sstv/sstv_decoder.py
+++ b/utils/sstv/sstv_decoder.py
@@ -94,6 +94,7 @@ class DecodeProgress:
image: SSTVImage | None = None
signal_level: int | None = None # 0-100 RMS audio level, None = not measured
sstv_tone: str | None = None # 'leader', 'sync', 'noise', None
+ vis_state: str | None = None # VIS detector state name
def to_dict(self) -> dict:
result: dict = {
@@ -111,6 +112,8 @@ class DecodeProgress:
result['signal_level'] = self.signal_level
if self.sstv_tone:
result['sstv_tone'] = self.sstv_tone
+ if self.vis_state:
+ result['vis_state'] = self.vis_state
return result
@@ -476,6 +479,7 @@ class SSTVDecoder:
message='Listening...',
signal_level=signal_level,
sstv_tone=sstv_tone,
+ vis_state=vis_detector.state.value,
))
except Exception as e: