some refactoring

This commit is contained in:
Ayzen
2025-09-30 16:25:35 +03:00
parent 39bd56c19a
commit b0c2d48c2b
17 changed files with 4324 additions and 114 deletions

Binary file not shown.

View File

@ -1 +1 @@
config_inputs/s11_start100_stop8800_points1000_bw1khz.bin
config_inputs/s21_start100_stop8800_points1000_bw1khz.bin

View File

@ -0,0 +1 @@
s21_start100_stop8800_points1000_bw1khz/bambambum

View File

@ -1,10 +1,10 @@
{
"open_air": true,
"axis": "phase",
"data_limitation": "ph_only_1",
"cut": 0.988,
"max": 2.4,
"gain": 1.2,
"open_air": false,
"axis": "abs",
"data_limitation": "ph_only_2",
"cut": 0.417,
"max": 0.6,
"gain": 0.0,
"start_freq": 100.0,
"stop_freq": 8800.0,
"clear_history": false,

View File

@ -1,5 +1,5 @@
{
"y_min": -80,
"y_max": 40,
"show_phase": true
"show_phase": false
}

View File

@ -166,8 +166,8 @@ class BScanProcessor(BaseProcessor):
"""
try:
# Skip B-scan processing for S21 mode - only works with S11
if vna_config and vna_config.get("mode") == "s21":
return {"error": "B-scan only available in S11 mode"}
# if vna_config and vna_config.get("mode") == "s21":
# return {"error": "B-scan only available in S11 mode"}
# Choose calibrated data when provided
data_to_process = calibrated_data or sweep_data

View File

@ -1,7 +1,8 @@
import numpy as np
from pathlib import Path
from typing import Any
from dataclasses import asdict
from vna_system.core.acquisition.sweep_buffer import SweepData
from vna_system.core.logging.logger import get_component_logger
from vna_system.core.processors.base_processor import BaseProcessor, UIParameter
@ -30,7 +31,7 @@ class MagnitudeProcessor(BaseProcessor):
# ------------------------------------------------------------------ #
# Core processing
# ------------------------------------------------------------------ #
def process_sweep(self, sweep_data: Any, calibrated_data: Any, vna_config: dict[str, Any]) -> dict[str, Any]:
def process_sweep(self, sweep_data: SweepData, calibrated_data: SweepData, vna_config: dict[str, Any]) -> dict[str, Any]:
"""
Produce magnitude trace (dB) and ancillary info from a calibrated sweep.
@ -70,15 +71,21 @@ class MagnitudeProcessor(BaseProcessor):
# Magnitude in dB (clamp zero magnitude to -120 dB)
mags_db: list[float] = []
phases_deg: list[float] = []
# real_points: list[str] = []
# imag_points: list[str] = []
for real, imag in points:
complex_val = complex(real, imag)
# real_points.append(str(real))
# imag_points.append(str(imag))
mag = abs(complex_val)
mags_db.append(20.0 * np.log10(mag) if mag > 0.0 else -120.0)
phases_deg.append(np.degrees(np.angle(complex_val)))
result = {
"frequencies": freqs,
# "real_points" : real_points,
# "imag_points" : imag_points,
"magnitudes_db": mags_db,
"phases_deg": phases_deg,
"y_min": float(self._config.get("y_min", -80)),

View File

@ -270,9 +270,10 @@ class ProcessorManager:
from .implementations.magnitude_processor import MagnitudeProcessor
from .implementations.bscan_processor import BScanProcessor
self.register_processor(MagnitudeProcessor(self.config_dir))
# self.register_processor(PhaseProcessor(self.config_dir))
self.register_processor(BScanProcessor(self.config_dir))
self.register_processor(MagnitudeProcessor(self.config_dir))
# self.register_processor(SmithChartProcessor(self.config_dir))
logger.info("Default processors registered", count=len(self._processors))

View File

@ -1 +1 @@
s11_start100_stop8800_points1000_bw1khz/test1
s21_start100_stop8800_points1000_bw1khz/testet

View File

@ -0,0 +1,7 @@
{
"name": "asd",
"timestamp": "2025-09-30T15:20:21.546972",
"preset_filename": "s11_start100_stop8800_points1000_bw1khz.bin",
"description": "asd",
"metadata": {}
}

File diff suppressed because it is too large Load Diff

View File

@ -38,6 +38,7 @@ body {
display: flex;
align-items: center;
gap: var(--space-3);
flex-wrap: wrap;
}
.header__icon {
@ -52,12 +53,57 @@ body {
color: var(--text-primary);
}
.header__subtitle {
font-size: var(--font-size-sm);
color: var(--text-tertiary);
padding: var(--space-1) var(--space-2);
.header__summary {
display: flex;
align-items: center;
gap: var(--space-3);
padding: var(--space-2) var(--space-4);
background-color: var(--bg-tertiary);
border-radius: var(--radius-default);
border: 1px solid var(--border-primary);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
}
.header-summary__item {
display: flex;
flex-direction: column;
gap: var(--space-1);
}
.header-summary__label {
font-size: var(--font-size-xs);
color: var(--text-tertiary);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.header-summary__value {
font-size: var(--font-size-sm);
color: var(--text-primary);
font-family: var(--font-mono);
}
.header-summary__divider {
width: 1px;
height: 32px;
background-color: var(--border-primary);
}
@media (max-width: 768px) {
.header__summary {
width: 100%;
justify-content: space-between;
flex-wrap: wrap;
row-gap: var(--space-2);
}
.header-summary__divider {
display: none;
}
.header-summary__item {
flex: 1 1 45%;
}
}
.header__status {
@ -374,4 +420,4 @@ body {
grid-template-columns: 1fr;
gap: var(--space-4);
}
}
}

View File

@ -131,40 +131,43 @@
align-items: stretch;
}
.preset-dropdown,
.calibration-dropdown {
:is(.settings-select, .preset-dropdown, .calibration-dropdown, .reference-dropdown) {
flex: 1;
min-width: 220px;
padding: var(--space-3) var(--space-4);
padding-right: var(--space-10);
background: var(--bg-primary);
border: 1px solid var(--border-primary);
border-radius: var(--radius-lg);
color: var(--text-primary);
font-size: var(--font-size-sm);
min-height: 3rem;
font-weight: var(--font-weight-medium);
transition: all var(--transition-fast);
box-shadow: var(--shadow-sm);
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' stroke-width='1.5' stroke='%2394a3b8' fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right var(--space-4) center;
background-size: 14px 14px;
}
.preset-dropdown:focus,
.calibration-dropdown:focus {
:is(.settings-select, .preset-dropdown, .calibration-dropdown, .reference-dropdown):focus {
outline: none;
border-color: var(--color-primary-500);
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
background: var(--bg-surface);
}
.preset-dropdown:hover,
.calibration-dropdown:hover {
:is(.settings-select, .preset-dropdown, .calibration-dropdown, .reference-dropdown):hover:not(:disabled) {
border-color: var(--color-primary-500);
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
.preset-dropdown:disabled,
.calibration-dropdown:disabled {
:is(.settings-select, .preset-dropdown, .calibration-dropdown, .reference-dropdown):disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.preset-info {
@ -301,14 +304,18 @@
}
/* Calibration Actions */
.calibration-actions {
:is(.settings-action-group, .calibration-actions, .reference-actions) {
display: flex;
gap: var(--space-3);
align-items: stretch;
flex-wrap: wrap;
}
.calibration-actions {
margin-top: var(--space-4);
}
.calibration-name-input {
:is(.settings-input, .calibration-name-input, .reference-name-input, .reference-description-input) {
flex: 1;
padding: var(--space-3) var(--space-4);
background: var(--bg-primary);
@ -316,30 +323,33 @@
border-radius: var(--radius-lg);
color: var(--text-primary);
font-size: var(--font-size-sm);
min-height: 2.5rem;
min-height: 2.75rem;
transition: all var(--transition-fast);
box-shadow: var(--shadow-sm);
}
.calibration-name-input:focus {
:is(.settings-input, .calibration-name-input, .reference-name-input, .reference-description-input):focus {
outline: none;
border-color: var(--color-primary-500);
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.calibration-name-input:disabled {
:is(.settings-input, .calibration-name-input, .reference-name-input, .reference-description-input):disabled {
opacity: 0.6;
cursor: not-allowed;
}
.calibration-name-input::placeholder {
:is(.settings-input, .calibration-name-input, .reference-name-input, .reference-description-input)::placeholder {
color: var(--text-tertiary);
}
/* Existing Calibrations */
.existing-calibrations {
.existing-calibrations,
.existing-references {
display: flex;
gap: var(--space-3);
align-items: stretch;
flex-wrap: wrap;
}
/* Status Grid */
@ -390,7 +400,8 @@
flex-direction: column;
}
.existing-calibrations {
.existing-calibrations,
.existing-references {
flex-direction: column;
}
@ -503,53 +514,8 @@
margin-bottom: var(--space-4);
}
.reference-name-input,
.reference-description-input {
padding: var(--space-2) var(--space-3);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
font-size: var(--text-sm);
background: var(--color-bg);
color: var(--color-text);
transition: all 0.2s ease;
}
.reference-name-input:focus,
.reference-description-input:focus {
outline: none;
border-color: var(--color-primary-500);
box-shadow: 0 0 0 3px var(--color-primary-100);
}
.reference-name-input::placeholder,
.reference-description-input::placeholder {
color: var(--color-text-secondary);
}
/* Reference Actions */
.reference-actions {
display: flex;
gap: var(--space-3);
align-items: stretch;
margin-top: var(--space-3);
flex-wrap: wrap;
}
.reference-dropdown {
flex: 1;
min-width: 200px;
padding: var(--space-2) var(--space-3);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
font-size: var(--text-sm);
background: var(--color-bg);
color: var(--color-text);
}
.reference-dropdown:disabled {
opacity: 0.6;
cursor: not-allowed;
background: var(--color-gray-50);
}
/* Current Reference Info */
@ -623,4 +589,4 @@
.reference-dropdown {
min-width: unset;
}
}
}

View File

@ -26,6 +26,7 @@ export class SettingsManager {
this.isInitialized = false;
this.elements = {};
this.headerElements = {};
this.debouncer = new Debouncer();
// Sub-managers
@ -99,6 +100,12 @@ export class SettingsManager {
calibrationCount: document.getElementById('calibrationCount'),
systemStatus: document.getElementById('systemStatus')
};
this.headerElements = {
preset: document.getElementById('headerPresetSummary'),
calibration: document.getElementById('headerCalibrationSummary'),
reference: document.getElementById('headerReferenceSummary')
};
}
initSubManagers() {
@ -109,10 +116,9 @@ export class SettingsManager {
// Setup callbacks
this.presetManager.onPresetChanged = async () => {
await this.loadStatus();
const preset = this.presetManager.getCurrentPreset();
this.calibrationManager.setCurrentPreset(preset);
this.calibrationManager.reset();
this.referenceManager.setCurrentPreset(preset);
await this.calibrationManager.loadWorkingCalibration();
this.updateReferenceSummary(this.referenceManager.getCurrentReference());
};
this.calibrationManager.onCalibrationSaved = async () => {
@ -122,6 +128,10 @@ export class SettingsManager {
this.calibrationManager.onCalibrationSet = async () => {
await this.loadStatus();
};
this.referenceManager.onReferenceUpdated = (reference) => {
this.updateReferenceSummary(reference);
};
}
setupEventHandlers() {
@ -130,12 +140,9 @@ export class SettingsManager {
}
async loadInitialData() {
await Promise.all([
this.presetManager.loadPresets(),
this.loadStatus(),
this.calibrationManager.loadWorkingCalibration(),
this.referenceManager.loadReferences()
]);
await this.presetManager.loadPresets();
await this.loadStatus();
await this.calibrationManager.loadWorkingCalibration();
}
async loadStatus() {
@ -158,6 +165,9 @@ export class SettingsManager {
this.elements.presetCount.textContent = status.available_presets || 0;
this.elements.calibrationCount.textContent = status.available_calibrations || 0;
this.elements.systemStatus.textContent = 'Ready';
this.updateHeaderSummary(status);
this.updateReferenceSummary(this.referenceManager.getCurrentReference());
}
async handleViewPlots() {
@ -475,6 +485,108 @@ export class SettingsManager {
console.log('Settings Manager destroyed');
}
formatFrequency(value) {
if (value === null || value === undefined) return null;
const numeric = Number(value);
if (!Number.isFinite(numeric)) return null;
const abs = Math.abs(numeric);
const units = [
{ divider: 1e9, suffix: 'GHz' },
{ divider: 1e6, suffix: 'MHz' },
{ divider: 1e3, suffix: 'kHz' }
];
for (const unit of units) {
if (abs >= unit.divider) {
const scaled = numeric / unit.divider;
const formatted = scaled >= 10 ? scaled.toFixed(0) : scaled.toFixed(2);
return `${formatted} ${unit.suffix}`;
}
}
return `${numeric.toFixed(0)} Hz`;
}
formatPresetSummary(preset) {
if (!preset) return 'Not selected';
const parts = [];
if (preset.mode) {
parts.push(preset.mode.toUpperCase());
}
const start = this.formatFrequency(preset.start_freq);
const stop = this.formatFrequency(preset.stop_freq);
if (start && stop) {
parts.push(`${start} ${stop}`);
} else if (start || stop) {
parts.push(start || stop);
}
if (preset.points) {
parts.push(`${preset.points} pts`);
}
if (preset.bandwidth) {
const bw = this.formatFrequency(preset.bandwidth);
if (bw) parts.push(`BW ${bw}`);
}
return parts.join(' • ') || 'Preset configured';
}
formatCalibrationSummary(status) {
const active = status?.current_calibration;
if (active?.calibration_name) {
return `Active • ${active.calibration_name}`;
}
const working = status?.working_calibration;
if (working?.progress) {
const missingCount = working.missing_standards?.length || 0;
const missingText = missingCount ? `${missingCount} missing` : '';
return `In progress • ${working.progress}${missingText}`;
}
return 'Not set';
}
updateHeaderSummary(status) {
if (!this.headerElements) return;
const { preset, calibration } = this.headerElements;
if (preset) {
preset.textContent = this.formatPresetSummary(status?.current_preset);
}
if (calibration) {
calibration.textContent = this.formatCalibrationSummary(status);
}
}
updateReferenceSummary(reference) {
const target = this.headerElements?.reference;
if (!target) return;
if (!reference) {
target.textContent = 'Not captured';
return;
}
const name = reference.name || 'Reference';
let timestampText = '';
if (reference.timestamp) {
const timestamp = new Date(reference.timestamp);
if (!Number.isNaN(timestamp.getTime())) {
timestampText = timestamp.toLocaleString(undefined, { dateStyle: 'short', timeStyle: 'short' });
}
}
const summaryParts = ['Active', name];
if (timestampText) {
summaryParts.push(timestampText);
}
target.textContent = summaryParts.join(' • ');
}
notify(type, title, message) {
this.notifications?.show?.({ type, title, message });
}

View File

@ -5,7 +5,9 @@
import { Debouncer, RequestGuard, ButtonState } from '../utils.js';
import { apiGet, apiPost } from '../api-client.js';
import { API, TIMING } from '../constants.js';
import { API, TIMING, NOTIFICATION_TYPES } from '../constants.js';
const { SUCCESS, ERROR } = NOTIFICATION_TYPES;
export class PresetManager {
constructor(notifications) {
@ -36,7 +38,7 @@ export class PresetManager {
this.populateDropdown(presets);
} catch (e) {
console.error('Presets load failed:', e);
this.notify('error', 'Load Error', 'Failed to load configuration presets');
this.notify(ERROR, 'Load Error', 'Failed to load configuration presets');
}
}
@ -47,7 +49,9 @@ export class PresetManager {
if (!presets.length) {
dd.innerHTML = '<option value="">No presets available</option>';
dd.disabled = true;
this.elements.setPresetBtn.disabled = true;
if (this.elements.setPresetBtn) {
this.elements.setPresetBtn.disabled = true;
}
return;
}
@ -60,7 +64,7 @@ export class PresetManager {
});
dd.disabled = false;
this.elements.setPresetBtn.disabled = true;
this.syncSelectedPreset();
}
formatDisplay(p) {
@ -75,8 +79,7 @@ export class PresetManager {
}
handlePresetChange() {
const v = this.elements.presetDropdown.value;
this.elements.setPresetBtn.disabled = !v;
this.updateSetButtonState();
}
async handleSetPreset() {
@ -90,17 +93,19 @@ export class PresetManager {
const result = await apiPost(API.SETTINGS.PRESET_SET, { filename });
this.notify('success', 'Preset Set', result.message);
this.notify(SUCCESS, 'Preset Set', result.message);
this.currentPreset = { filename };
if (this.onPresetChanged) {
await this.onPresetChanged();
}
this.syncSelectedPreset();
} catch (e) {
console.error('Set preset failed:', e);
this.notify('error', 'Preset Error', 'Failed to set configuration preset');
this.notify(ERROR, 'Preset Error', 'Failed to set configuration preset');
} finally {
ButtonState.set(this.elements.setPresetBtn, { state: 'normal', icon: 'check', text: 'Set Active' });
this.updateSetButtonState();
}
}), TIMING.DEBOUNCE_PRESET
);
@ -114,13 +119,40 @@ export class PresetManager {
this.currentPreset = null;
this.elements.currentPreset.textContent = 'None';
}
this.syncSelectedPreset();
}
getCurrentPreset() {
return this.currentPreset;
}
syncSelectedPreset() {
const dd = this.elements.presetDropdown;
if (!dd) return;
const active = this.currentPreset?.filename || '';
if (active) {
const hasOption = Array.from(dd.options).some(opt => opt.value === active);
dd.value = hasOption ? active : '';
} else {
dd.value = '';
}
this.updateSetButtonState();
}
updateSetButtonState() {
const dd = this.elements.presetDropdown;
const btn = this.elements.setPresetBtn;
if (!dd || !btn) return;
const selected = dd.value;
const hasSelection = selected !== '';
const isActive = this.currentPreset?.filename === selected;
btn.disabled = !hasSelection || isActive;
}
notify(type, title, message) {
this.notifications?.show?.({ type, title, message });
}
}
}

View File

@ -18,6 +18,7 @@ export class ReferenceManager {
this.elements = {};
this.debouncer = new Debouncer();
this.reqGuard = new RequestGuard();
this.onReferenceUpdated = null;
this.handleCreateReference = this.handleCreateReference.bind(this);
this.handleReferenceChange = this.handleReferenceChange.bind(this);
@ -79,6 +80,10 @@ export class ReferenceManager {
}
}
getCurrentReference() {
return this.currentReference;
}
renderDropdown(references) {
if (!this.elements.referenceDropdown) return;
@ -120,10 +125,16 @@ export class ReferenceManager {
}
updateInfo(reference) {
if (!this.elements.currentReferenceInfo) return;
this.currentReference = reference;
if (!this.elements.currentReferenceInfo) {
this.onReferenceUpdated?.(reference || null);
return;
}
if (!reference) {
this.elements.currentReferenceInfo.style.display = 'none';
this.onReferenceUpdated?.(null);
return;
}
@ -134,12 +145,17 @@ export class ReferenceManager {
}
if (this.elements.currentReferenceTimestamp) {
this.elements.currentReferenceTimestamp.textContent = new Date(reference.timestamp).toLocaleString();
const timestamp = new Date(reference.timestamp);
this.elements.currentReferenceTimestamp.textContent = Number.isNaN(timestamp.getTime())
? '-'
: timestamp.toLocaleString();
}
if (this.elements.currentReferenceDescription) {
this.elements.currentReferenceDescription.textContent = reference.description || '';
}
this.onReferenceUpdated?.(reference);
}
async handleCreateReference() {

View File

@ -41,7 +41,22 @@
<div class="header__brand">
<i data-lucide="activity" class="header__icon"></i>
<h1 class="header__title">VNA System</h1>
<span class="header__subtitle">Real-time Analysis</span>
<div class="header__summary" id="headerSummary">
<div class="header-summary__item">
<span class="header-summary__label">Preset</span>
<span class="header-summary__value" id="headerPresetSummary">Not selected</span>
</div>
<span class="header-summary__divider" aria-hidden="true"></span>
<div class="header-summary__item">
<span class="header-summary__label">Calibration</span>
<span class="header-summary__value" id="headerCalibrationSummary">Not set</span>
</div>
<span class="header-summary__divider" aria-hidden="true"></span>
<div class="header-summary__item">
<span class="header-summary__label">Reference</span>
<span class="header-summary__value" id="headerReferenceSummary">Not captured</span>
</div>
</div>
</div>
<div class="header__status">
@ -149,7 +164,7 @@
<div class="control-group">
<label class="control-label">Available Presets:</label>
<div class="preset-selector" id="presetSelector">
<select class="preset-dropdown" id="presetDropdown" disabled>
<select class="preset-dropdown settings-select" id="presetDropdown" disabled>
<option value="">Loading presets...</option>
</select>
<button class="btn btn--primary" id="setPresetBtn" disabled>
@ -209,12 +224,12 @@
<!-- Standards buttons will be dynamically generated -->
</div>
<div class="calibration-actions">
<div class="calibration-actions settings-action-group">
<button class="btn btn--secondary" id="viewCurrentPlotsBtn" disabled>
<i data-lucide="bar-chart-3"></i>
View Current Plots
</button>
<input type="text" class="calibration-name-input" id="calibrationNameInput" placeholder="Enter calibration name" disabled>
<input type="text" class="calibration-name-input settings-input" id="calibrationNameInput" placeholder="Enter calibration name" disabled>
<button class="btn btn--success" id="saveCalibrationBtn" disabled>
<i data-lucide="save"></i>
Save Calibration
@ -226,10 +241,10 @@
<div class="workflow-section">
<h4 class="workflow-title">Existing Calibrations</h4>
<div class="existing-calibrations" id="existingCalibrations">
<select class="calibration-dropdown" id="calibrationDropdown" disabled>
<select class="calibration-dropdown settings-select" id="calibrationDropdown" disabled>
<option value="">No calibrations available</option>
</select>
<div class="calibration-actions">
<div class="calibration-actions settings-action-group">
<button class="btn btn--primary" id="setCalibrationBtn" disabled>
<i data-lucide="check"></i>
Set Active
@ -253,8 +268,8 @@
<div class="workflow-section">
<h4 class="workflow-title">Create Reference</h4>
<div class="reference-creation">
<input type="text" class="reference-name-input" id="referenceNameInput" placeholder="Enter reference name">
<input type="text" class="reference-description-input" id="referenceDescriptionInput" placeholder="Description (optional)">
<input type="text" class="reference-name-input settings-input" id="referenceNameInput" placeholder="Enter reference name">
<input type="text" class="reference-description-input settings-input" id="referenceDescriptionInput" placeholder="Description (optional)">
<button class="btn btn--primary" id="createReferenceBtn">
<i data-lucide="target"></i>
Capture Reference
@ -266,10 +281,10 @@
<div class="workflow-section">
<h4 class="workflow-title">Existing References</h4>
<div class="existing-references" id="existingReferences">
<select class="reference-dropdown" id="referenceDropdown" disabled>
<select class="reference-dropdown settings-select" id="referenceDropdown" disabled>
<option value="">No references available</option>
</select>
<div class="reference-actions">
<div class="reference-actions settings-action-group">
<button class="btn btn--primary" id="setReferenceBtn" disabled>
<i data-lucide="check"></i>
Set Active
@ -361,4 +376,4 @@
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.js"></script>
<script type="module" src="/static/js/main.js"></script>
</body>
</html>
</html>