added load tsv format feature
This commit is contained in:
@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"preset": {
|
|
||||||
"filename": "s11_start100_stop8800_points1000_bw1khz.bin",
|
|
||||||
"mode": "s11",
|
|
||||||
"start_freq": 100000000.0,
|
|
||||||
"stop_freq": 8800000000.0,
|
|
||||||
"points": 1000,
|
|
||||||
"bandwidth": 1000.0
|
|
||||||
},
|
|
||||||
"calibration_name": "вфыввф",
|
|
||||||
"standards": [
|
|
||||||
"open",
|
|
||||||
"short",
|
|
||||||
"load"
|
|
||||||
],
|
|
||||||
"created_timestamp": "2025-09-30T18:55:17.618157",
|
|
||||||
"is_complete": true
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"preset": {
|
|
||||||
"filename": "s11_start100_stop8800_points1000_bw1khz.bin",
|
|
||||||
"mode": "s11",
|
|
||||||
"start_freq": 100000000.0,
|
|
||||||
"stop_freq": 8800000000.0,
|
|
||||||
"points": 1000,
|
|
||||||
"bandwidth": 1000.0
|
|
||||||
},
|
|
||||||
"calibration_name": "вфыввф",
|
|
||||||
"standard": "load",
|
|
||||||
"sweep_number": 33,
|
|
||||||
"sweep_timestamp": 1759247715.1613321,
|
|
||||||
"created_timestamp": "2025-09-30T18:55:17.618031",
|
|
||||||
"total_points": 1000
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"preset": {
|
|
||||||
"filename": "s11_start100_stop8800_points1000_bw1khz.bin",
|
|
||||||
"mode": "s11",
|
|
||||||
"start_freq": 100000000.0,
|
|
||||||
"stop_freq": 8800000000.0,
|
|
||||||
"points": 1000,
|
|
||||||
"bandwidth": 1000.0
|
|
||||||
},
|
|
||||||
"calibration_name": "вфыввф",
|
|
||||||
"standard": "open",
|
|
||||||
"sweep_number": 18,
|
|
||||||
"sweep_timestamp": 1759247683.8793015,
|
|
||||||
"created_timestamp": "2025-09-30T18:55:17.612864",
|
|
||||||
"total_points": 1000
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"preset": {
|
|
||||||
"filename": "s11_start100_stop8800_points1000_bw1khz.bin",
|
|
||||||
"mode": "s11",
|
|
||||||
"start_freq": 100000000.0,
|
|
||||||
"stop_freq": 8800000000.0,
|
|
||||||
"points": 1000,
|
|
||||||
"bandwidth": 1000.0
|
|
||||||
},
|
|
||||||
"calibration_name": "вфыввф",
|
|
||||||
"standard": "short",
|
|
||||||
"sweep_number": 19,
|
|
||||||
"sweep_timestamp": 1759247685.9623504,
|
|
||||||
"created_timestamp": "2025-09-30T18:55:17.615536",
|
|
||||||
"total_points": 1000
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"preset": {
|
|
||||||
"filename": "s11_start100_stop8800_points1000_bw1khz.bin",
|
|
||||||
"mode": "s11",
|
|
||||||
"start_freq": 100000000.0,
|
|
||||||
"stop_freq": 8800000000.0,
|
|
||||||
"points": 1000,
|
|
||||||
"bandwidth": 1000.0
|
|
||||||
},
|
|
||||||
"calibration_name": "еуыеуые",
|
|
||||||
"standards": [
|
|
||||||
"open",
|
|
||||||
"load",
|
|
||||||
"short"
|
|
||||||
],
|
|
||||||
"created_timestamp": "2025-09-26T17:19:50.019248",
|
|
||||||
"is_complete": true
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"preset": {
|
|
||||||
"filename": "s11_start100_stop8800_points1000_bw1khz.bin",
|
|
||||||
"mode": "s11",
|
|
||||||
"start_freq": 100000000.0,
|
|
||||||
"stop_freq": 8800000000.0,
|
|
||||||
"points": 1000,
|
|
||||||
"bandwidth": 1000.0
|
|
||||||
},
|
|
||||||
"calibration_name": "еуыеуые",
|
|
||||||
"standard": "load",
|
|
||||||
"sweep_number": 12,
|
|
||||||
"sweep_timestamp": 1758896376.33808,
|
|
||||||
"created_timestamp": "2025-09-26T17:19:50.017201",
|
|
||||||
"total_points": 1000
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"preset": {
|
|
||||||
"filename": "s11_start100_stop8800_points1000_bw1khz.bin",
|
|
||||||
"mode": "s11",
|
|
||||||
"start_freq": 100000000.0,
|
|
||||||
"stop_freq": 8800000000.0,
|
|
||||||
"points": 1000,
|
|
||||||
"bandwidth": 1000.0
|
|
||||||
},
|
|
||||||
"calibration_name": "еуыеуые",
|
|
||||||
"standard": "open",
|
|
||||||
"sweep_number": 10,
|
|
||||||
"sweep_timestamp": 1758896372.20023,
|
|
||||||
"created_timestamp": "2025-09-26T17:19:50.015286",
|
|
||||||
"total_points": 1000
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"preset": {
|
|
||||||
"filename": "s11_start100_stop8800_points1000_bw1khz.bin",
|
|
||||||
"mode": "s11",
|
|
||||||
"start_freq": 100000000.0,
|
|
||||||
"stop_freq": 8800000000.0,
|
|
||||||
"points": 1000,
|
|
||||||
"bandwidth": 1000.0
|
|
||||||
},
|
|
||||||
"calibration_name": "еуыеуые",
|
|
||||||
"standard": "short",
|
|
||||||
"sweep_number": 13,
|
|
||||||
"sweep_timestamp": 1758896378.4093437,
|
|
||||||
"created_timestamp": "2025-09-26T17:19:50.019159",
|
|
||||||
"total_points": 1000
|
|
||||||
}
|
|
||||||
@ -299,7 +299,7 @@ class BaseProcessor:
|
|||||||
# --------------------------------------------------------------------- #
|
# --------------------------------------------------------------------- #
|
||||||
# Data path: accept new sweep, recompute, produce result
|
# Data path: accept new sweep, recompute, produce result
|
||||||
# --------------------------------------------------------------------- #
|
# --------------------------------------------------------------------- #
|
||||||
def add_sweep_data(self, sweep_data: Any, calibrated_data: Any, vna_config: ConfigPreset | None, reference_data: Any = None, reference_info: Any = None):
|
def add_sweep_data(self, sweep_data: Any, calibrated_data: Any, vna_config: ConfigPreset | None, reference_data: Any = None, reference_info: Any = None, raw_reference_data: Any = None, calibration_standards: dict | None = None):
|
||||||
"""
|
"""
|
||||||
Add the latest sweep to the in-memory history and trigger recalculation.
|
Add the latest sweep to the in-memory history and trigger recalculation.
|
||||||
|
|
||||||
@ -312,9 +312,13 @@ class BaseProcessor:
|
|||||||
vna_config:
|
vna_config:
|
||||||
Snapshot of VNA settings (dataclass or pydantic model supported).
|
Snapshot of VNA settings (dataclass or pydantic model supported).
|
||||||
reference_data:
|
reference_data:
|
||||||
Open air reference sweep data for background subtraction/normalization.
|
Calibrated open air reference sweep data for background subtraction/normalization.
|
||||||
reference_info:
|
reference_info:
|
||||||
ReferenceInfo object with metadata (name, description, etc.) about the reference.
|
ReferenceInfo object with metadata (name, description, etc.) about the reference.
|
||||||
|
raw_reference_data:
|
||||||
|
Raw (uncalibrated) reference sweep data for export purposes.
|
||||||
|
calibration_standards:
|
||||||
|
Dictionary of calibration standard sweep data (e.g., {"open": SweepData, "short": SweepData, ...}).
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
@ -329,6 +333,8 @@ class BaseProcessor:
|
|||||||
"vna_config": self._snapshot_vna_config(vna_config),
|
"vna_config": self._snapshot_vna_config(vna_config),
|
||||||
"reference_data": reference_data,
|
"reference_data": reference_data,
|
||||||
"reference_info": reference_info,
|
"reference_info": reference_info,
|
||||||
|
"raw_reference_data": raw_reference_data,
|
||||||
|
"calibration_standards": calibration_standards,
|
||||||
"timestamp": datetime.now().timestamp(),
|
"timestamp": datetime.now().timestamp(),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -615,12 +621,32 @@ class BaseProcessor:
|
|||||||
sweep_data = entry["sweep_data"]
|
sweep_data = entry["sweep_data"]
|
||||||
calibrated_data = entry["calibrated_data"]
|
calibrated_data = entry["calibrated_data"]
|
||||||
reference_data = entry.get("reference_data")
|
reference_data = entry.get("reference_data")
|
||||||
|
raw_reference_data = entry.get("raw_reference_data")
|
||||||
|
calibration_standards = entry.get("calibration_standards")
|
||||||
|
reference_info = entry.get("reference_info")
|
||||||
|
|
||||||
|
# Export calibration standards if present
|
||||||
|
cal_standards_export = None
|
||||||
|
if calibration_standards:
|
||||||
|
cal_standards_export = {}
|
||||||
|
for std_name, std_data in calibration_standards.items():
|
||||||
|
cal_standards_export[std_name] = {
|
||||||
|
"points": self._points_to_list(getattr(std_data, "points", [])),
|
||||||
|
"sweep_number": getattr(std_data, "sweep_number", None),
|
||||||
|
"timestamp": getattr(std_data, "timestamp", None),
|
||||||
|
}
|
||||||
|
|
||||||
exported.append({
|
exported.append({
|
||||||
"timestamp": float(entry.get("timestamp")) if entry.get("timestamp") is not None else None,
|
"timestamp": float(entry.get("timestamp")) if entry.get("timestamp") is not None else None,
|
||||||
"sweep_points": self._points_to_list(getattr(sweep_data, "points", [])),
|
"sweep_points": self._points_to_list(getattr(sweep_data, "points", [])),
|
||||||
"calibrated_points": self._points_to_list(getattr(calibrated_data, "points", [])),
|
"calibrated_points": self._points_to_list(getattr(calibrated_data, "points", [])),
|
||||||
"reference_points": self._points_to_list(getattr(reference_data, "points", [])),
|
"reference_points": self._points_to_list(getattr(reference_data, "points", [])),
|
||||||
|
"raw_reference_points": self._points_to_list(getattr(raw_reference_data, "points", [])),
|
||||||
|
"calibration_standards": cal_standards_export,
|
||||||
|
"reference_info": {
|
||||||
|
"name": getattr(reference_info, "name", None),
|
||||||
|
"description": getattr(reference_info, "description", None),
|
||||||
|
} if reference_info else None,
|
||||||
"vna_config": self._snapshot_vna_config(entry.get("vna_config")),
|
"vna_config": self._snapshot_vna_config(entry.get("vna_config")),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"open_air": true,
|
"open_air": false,
|
||||||
"axis": "real",
|
"axis": "phase",
|
||||||
"cut": 0.727,
|
"cut": 0.279,
|
||||||
"max": 2.3,
|
"max": 4.0,
|
||||||
"gain": 0.5,
|
"gain": 0.5,
|
||||||
"start_freq": 100.0,
|
"start_freq": 100.0,
|
||||||
"stop_freq": 8800.0,
|
"stop_freq": 8230.0,
|
||||||
"clear_history": false,
|
"clear_history": false,
|
||||||
"data_limit": 500
|
"data_limit": 500
|
||||||
}
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"y_min": -45,
|
"y_min": -50,
|
||||||
"y_max": 40,
|
"y_max": 40,
|
||||||
"autoscale": true,
|
"autoscale": true,
|
||||||
"show_magnitude": true,
|
"show_magnitude": true,
|
||||||
|
|||||||
@ -100,7 +100,7 @@ class ProcessorManager:
|
|||||||
# --------------------------------------------------------------------- #
|
# --------------------------------------------------------------------- #
|
||||||
# Main processing actions
|
# Main processing actions
|
||||||
# --------------------------------------------------------------------- #
|
# --------------------------------------------------------------------- #
|
||||||
def process_sweep(self, sweep_data: SweepData, calibrated_data: Any, vna_config: ConfigPreset | None, reference_data: Any = None, reference_info: Any = None) -> dict[str, ProcessedResult]:
|
def process_sweep(self, sweep_data: SweepData, calibrated_data: Any, vna_config: ConfigPreset | None, reference_data: Any = None, reference_info: Any = None, raw_reference_data: Any = None, calibration_standards: dict | None = None) -> dict[str, ProcessedResult]:
|
||||||
"""
|
"""
|
||||||
Feed a sweep into all processors and dispatch results to callbacks.
|
Feed a sweep into all processors and dispatch results to callbacks.
|
||||||
|
|
||||||
@ -114,7 +114,7 @@ class ProcessorManager:
|
|||||||
|
|
||||||
for processor_id, processor in processors_items:
|
for processor_id, processor in processors_items:
|
||||||
try:
|
try:
|
||||||
result = processor.add_sweep_data(sweep_data, calibrated_data, vna_config, reference_data, reference_info)
|
result = processor.add_sweep_data(sweep_data, calibrated_data, vna_config, reference_data, reference_info, raw_reference_data, calibration_standards)
|
||||||
if result:
|
if result:
|
||||||
results[processor_id] = result
|
results[processor_id] = result
|
||||||
for cb in callbacks:
|
for cb in callbacks:
|
||||||
@ -288,9 +288,19 @@ class ProcessorManager:
|
|||||||
if latest and latest.sweep_number > self._last_processed_sweep:
|
if latest and latest.sweep_number > self._last_processed_sweep:
|
||||||
calibrated = self._apply_calibration(latest)
|
calibrated = self._apply_calibration(latest)
|
||||||
vna_cfg = self.settings_manager.get_current_preset()
|
vna_cfg = self.settings_manager.get_current_preset()
|
||||||
reference_data = self._apply_calibration(self.settings_manager.get_current_reference_sweep(vna_cfg))
|
|
||||||
|
# Get raw and calibrated reference data
|
||||||
|
raw_reference_sweep = self.settings_manager.get_current_reference_sweep(vna_cfg)
|
||||||
|
calibrated_reference_data = self._apply_calibration(raw_reference_sweep)
|
||||||
reference_info = self.settings_manager.get_current_reference(vna_cfg)
|
reference_info = self.settings_manager.get_current_reference(vna_cfg)
|
||||||
self.process_sweep(latest, calibrated, vna_cfg, reference_data, reference_info)
|
|
||||||
|
# Get calibration standards for export
|
||||||
|
calib_set = self.settings_manager.get_current_calibration()
|
||||||
|
calibration_standards = None
|
||||||
|
if calib_set and calib_set.standards:
|
||||||
|
calibration_standards = {std.value: sweep_data for std, sweep_data in calib_set.standards.items()}
|
||||||
|
|
||||||
|
self.process_sweep(latest, calibrated, vna_cfg, calibrated_reference_data, reference_info, raw_reference_sweep, calibration_standards)
|
||||||
self._last_processed_sweep = latest.sweep_number
|
self._last_processed_sweep = latest.sweep_number
|
||||||
|
|
||||||
# Light-duty polling to reduce wakeups
|
# Light-duty polling to reduce wakeups
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"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
@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "ыфвф",
|
|
||||||
"timestamp": "2025-09-30T19:05:27.995476",
|
|
||||||
"preset_filename": "s11_start100_stop8800_points1000_bw1khz.bin",
|
|
||||||
"description": "фывфф",
|
|
||||||
"metadata": {}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -178,9 +178,12 @@ export class ChartManager {
|
|||||||
<button class="chart-card__action" data-action="upload" title="Load History">
|
<button class="chart-card__action" data-action="upload" title="Load History">
|
||||||
<span data-icon="upload"></span>
|
<span data-icon="upload"></span>
|
||||||
</button>
|
</button>
|
||||||
<button class="chart-card__action" data-action="download" title="Download">
|
<button class="chart-card__action" data-action="download" title="Download JSON">
|
||||||
<span data-icon="download"></span>
|
<span data-icon="download"></span>
|
||||||
</button>
|
</button>
|
||||||
|
<button class="chart-card__action" data-action="export-sweeps" title="Export Sweeps (TSV)">
|
||||||
|
<span data-icon="database"></span>
|
||||||
|
</button>
|
||||||
<button class="chart-card__action" data-action="hide" title="Hide">
|
<button class="chart-card__action" data-action="hide" title="Hide">
|
||||||
<span data-icon="eye-off"></span>
|
<span data-icon="eye-off"></span>
|
||||||
</button>
|
</button>
|
||||||
@ -220,6 +223,7 @@ export class ChartManager {
|
|||||||
case 'fullscreen': this.toggleFullscreen(processorId); break;
|
case 'fullscreen': this.toggleFullscreen(processorId); break;
|
||||||
case 'upload': this.uploadHistory(processorId); break;
|
case 'upload': this.uploadHistory(processorId); break;
|
||||||
case 'download': this.downloadChart(processorId); break;
|
case 'download': this.downloadChart(processorId); break;
|
||||||
|
case 'export-sweeps': this.exportSweeps(processorId); break;
|
||||||
case 'hide':
|
case 'hide':
|
||||||
this.hideChart(processorId);
|
this.hideChart(processorId);
|
||||||
if (window.vnaDashboard?.ui) window.vnaDashboard.ui.setProcessorEnabled(processorId, false);
|
if (window.vnaDashboard?.ui) window.vnaDashboard.ui.setProcessorEnabled(processorId, false);
|
||||||
@ -413,6 +417,187 @@ export class ChartManager {
|
|||||||
await togglePlotlyFullscreen(c.element, c.plotContainer);
|
await togglePlotlyFullscreen(c.element, c.plotContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async exportSweeps(id) {
|
||||||
|
const c = this.charts.get(id);
|
||||||
|
if (!c?.plotContainer) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Request full processor state from backend via WebSocket
|
||||||
|
const websocket = window.vnaDashboard?.websocket;
|
||||||
|
if (!websocket || !websocket.getProcessorState) {
|
||||||
|
this.notifications?.show?.({
|
||||||
|
type: 'error',
|
||||||
|
title: 'Ошибка экспорта',
|
||||||
|
message: 'WebSocket не доступен'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up one-time listener for processor_state response
|
||||||
|
const stateHandler = async (payload) => {
|
||||||
|
if (payload.processor_id === id) {
|
||||||
|
websocket.off('processor_state', stateHandler);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.performSweepExport(id, payload);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Sweep export failed:', e);
|
||||||
|
this.notifications?.show?.({
|
||||||
|
type: 'error',
|
||||||
|
title: 'Ошибка экспорта',
|
||||||
|
message: `Не удалось экспортировать свипы: ${e.message}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
websocket.on('processor_state', stateHandler);
|
||||||
|
websocket.getProcessorState(id);
|
||||||
|
|
||||||
|
// Timeout fallback
|
||||||
|
setTimeout(() => {
|
||||||
|
websocket.off('processor_state', stateHandler);
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Export sweeps failed:', e);
|
||||||
|
this.notifications?.show?.({
|
||||||
|
type: 'error',
|
||||||
|
title: 'Ошибка экспорта',
|
||||||
|
message: 'Не удалось экспортировать данные свипов'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async performSweepExport(processorId, statePayload) {
|
||||||
|
if (!statePayload || !statePayload.state) {
|
||||||
|
throw new Error('Нет данных состояния процессора');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { state } = statePayload;
|
||||||
|
const sweepHistory = state.sweep_history || [];
|
||||||
|
|
||||||
|
console.log('performSweepExport: sweepHistory length:', sweepHistory.length);
|
||||||
|
|
||||||
|
if (sweepHistory.length === 0) {
|
||||||
|
throw new Error('Нет данных свипов для экспорта');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get latest sweep (most recent)
|
||||||
|
const latestSweep = sweepHistory[sweepHistory.length - 1];
|
||||||
|
console.log('performSweepExport: latestSweep keys:', Object.keys(latestSweep));
|
||||||
|
console.log('performSweepExport: sweep_points length:', latestSweep.sweep_points?.length);
|
||||||
|
console.log('performSweepExport: calibrated_points length:', latestSweep.calibrated_points?.length);
|
||||||
|
console.log('performSweepExport: calibration_standards:', latestSweep.calibration_standards);
|
||||||
|
console.log('performSweepExport: raw_reference_points length:', latestSweep.raw_reference_points?.length);
|
||||||
|
console.log('performSweepExport: reference_points length:', latestSweep.reference_points?.length);
|
||||||
|
|
||||||
|
// Prepare filename with timestamp and preset info
|
||||||
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||||
|
const presetMode = latestSweep.vna_config?.mode || 'unknown';
|
||||||
|
const baseFilename = `${processorId}_${presetMode}_${timestamp}`;
|
||||||
|
|
||||||
|
let exportedCount = 0;
|
||||||
|
|
||||||
|
// Export raw sweep
|
||||||
|
if (latestSweep.sweep_points && latestSweep.sweep_points.length > 0) {
|
||||||
|
console.log('Exporting raw sweep, points:', latestSweep.sweep_points.length);
|
||||||
|
this.exportPointsToTSV(latestSweep.sweep_points, latestSweep.vna_config, `${baseFilename}_raw`);
|
||||||
|
exportedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export calibrated sweep
|
||||||
|
if (latestSweep.calibrated_points && latestSweep.calibrated_points.length > 0) {
|
||||||
|
console.log('Exporting calibrated sweep, points:', latestSweep.calibrated_points.length);
|
||||||
|
this.exportPointsToTSV(latestSweep.calibrated_points, latestSweep.vna_config, `${baseFilename}_calibrated`);
|
||||||
|
exportedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export calibration standards if present
|
||||||
|
if (latestSweep.calibration_standards) {
|
||||||
|
console.log('Exporting calibration standards:', Object.keys(latestSweep.calibration_standards));
|
||||||
|
for (const [standardName, standardData] of Object.entries(latestSweep.calibration_standards)) {
|
||||||
|
if (standardData && standardData.points && standardData.points.length > 0) {
|
||||||
|
this.exportPointsToTSV(standardData.points, latestSweep.vna_config, `${baseFilename}_cal_${standardName}`);
|
||||||
|
exportedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export raw reference if present
|
||||||
|
if (latestSweep.raw_reference_points && latestSweep.raw_reference_points.length > 0) {
|
||||||
|
const refName = latestSweep.reference_info?.name || 'reference';
|
||||||
|
console.log('Exporting raw reference:', refName);
|
||||||
|
this.exportPointsToTSV(latestSweep.raw_reference_points, latestSweep.vna_config, `${baseFilename}_ref_${refName.replace(/\s/g, '_')}_raw`);
|
||||||
|
exportedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export calibrated reference if present (for comparison)
|
||||||
|
if (latestSweep.reference_points && latestSweep.reference_points.length > 0) {
|
||||||
|
const refName = latestSweep.reference_info?.name || 'reference';
|
||||||
|
console.log('Exporting calibrated reference:', refName);
|
||||||
|
this.exportPointsToTSV(latestSweep.reference_points, latestSweep.vna_config, `${baseFilename}_ref_${refName.replace(/\s/g, '_')}_calibrated`);
|
||||||
|
exportedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Total exported files:', exportedCount);
|
||||||
|
|
||||||
|
this.notifications?.show?.({
|
||||||
|
type: 'success',
|
||||||
|
title: 'Экспорт завершён',
|
||||||
|
message: `Данные свипов экспортированы для ${processorId}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
exportPointsToTSV(points, vnaConfig, filename) {
|
||||||
|
console.log('exportPointsToTSV called with filename:', filename);
|
||||||
|
console.log('exportPointsToTSV points length:', points?.length);
|
||||||
|
|
||||||
|
if (!points || points.length === 0) {
|
||||||
|
console.warn('No points to export');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const numPoints = points.length;
|
||||||
|
console.log('exportPointsToTSV: numPoints =', numPoints);
|
||||||
|
|
||||||
|
// Generate frequency array
|
||||||
|
const startFreq = vnaConfig?.start_freq || 100e6;
|
||||||
|
const stopFreq = vnaConfig?.stop_freq || 8.8e9;
|
||||||
|
|
||||||
|
let frequencies;
|
||||||
|
if (numPoints === 1) {
|
||||||
|
frequencies = [startFreq];
|
||||||
|
} else {
|
||||||
|
const step = (stopFreq - startFreq) / (numPoints - 1);
|
||||||
|
frequencies = Array.from({ length: numPoints }, (_, i) => startFreq + i * step);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build TSV content
|
||||||
|
let tsv = 'Frequency(Hz)\tReal\tImaginary\n';
|
||||||
|
for (let i = 0; i < numPoints; i++) {
|
||||||
|
const point = points[i];
|
||||||
|
const freq = frequencies[i];
|
||||||
|
const real = point[0];
|
||||||
|
const imag = point[1];
|
||||||
|
tsv += `${freq}\t${real}\t${imag}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('exportPointsToTSV: TSV size =', tsv.length, 'chars');
|
||||||
|
|
||||||
|
// Download file
|
||||||
|
const blob = new Blob([tsv], { type: 'text/tab-separated-values;charset=utf-8' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
link.download = `${filename}.tsv`;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
console.log('exportPointsToTSV: Clicking download link for', filename);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
hideEmptyState() {
|
hideEmptyState() {
|
||||||
if (this.emptyState) this.emptyState.classList.add('empty-state--hidden');
|
if (this.emptyState) this.emptyState.classList.add('empty-state--hidden');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -178,6 +178,14 @@ const ICONS = {
|
|||||||
{ type: 'circle', attrs: { cx: 12, cy: 12, r: 6 } },
|
{ type: 'circle', attrs: { cx: 12, cy: 12, r: 6 } },
|
||||||
{ type: 'circle', attrs: { cx: 12, cy: 12, r: 10 } }
|
{ type: 'circle', attrs: { cx: 12, cy: 12, r: 10 } }
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
database: {
|
||||||
|
viewBox: '0 0 24 24',
|
||||||
|
elements: [
|
||||||
|
{ type: 'ellipse', attrs: { cx: 12, cy: 5, rx: 9, ry: 3 } },
|
||||||
|
{ type: 'path', attrs: { d: 'M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5' } },
|
||||||
|
{ type: 'path', attrs: { d: 'M3 12c0 1.66 4 3 9 3s9-1.34 9-3' } }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user