-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmergen_scope.html
More file actions
207 lines (198 loc) · 11.3 KB
/
Copy pathmergen_scope.html
File metadata and controls
207 lines (198 loc) · 11.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
<!--
Mergen Scope
Recent changes:
- Phase 1: marker selection, minima search, marker-driven IP3, import normalization, saved-result cleanup, local vendored runtime imports.
- Phase 2: raw/derived trace model, Trace Ops, smoothing methods, trace math, interpolation choices, unit-aware warnings.
- Phase 3: multi-pane layout, pane-local lines, pane-targeted import, and shared or pane-local X navigation.
- Phase 4: expanded analysis toolkit including peak/spur tables, range stats, bandwidth, crossings, ripple, OBW, and guarded channel power.
Manual regression checklist:
1. Normal single trace: import one regular trace, verify startup, chart render, hover, X/Y zoom, markers, Peak, Next Peak, Min, Next Min.
2. Amplitude-only trace: import a file that relies on Start/Stop reconstruction and verify frequencies are reconstructed, sorted, and still chart correctly.
3. Overlapping two-trace pair: import two overlapping traces, verify visibility toggles, marker trace targeting, and crosshair readout across both.
4. Non-overlapping two-trace pair: import traces with separated spans and verify marker fallback, X zoom, and saved results still bind to the correct trace.
5. Sparse vs dense pair: import one sparse and one dense trace, verify nearest-point marker placement plus Next Peak / Next Min sequence behavior.
6. Noisy PSD trace: run Noise PSD, save a result, remove the source trace, and confirm the saved-result panel stays consistent.
7. Two-tone IP3 trace: create/select markers, assign F1/F2/IM3L/IM3U, try auto-pick, move a labeled marker, save IP3, and verify values update from marker positions.
8. Multi-pane pass: switch panes, drag traces between panes, test locked lines, and verify Zoom All on/off behavior.
9. Phase 4 pass: open Peak / Spur Table, Range Statistics, and Threshold Crossings, then confirm results follow the active pane and visible range.
Phase 1 signoff:
- Startup works from repo-hosted files only.
- Marker add/select/move/edit works on single-trace and two-trace views.
- Next Peak / Next Min behave correctly with selected marker and Auto trace mode.
- V/H reference lines place, edit, select, and move correctly.
- Noise PSD saves and stays consistent after source-trace changes.
- IP3 works from visible marker roles and saves normalized results.
- Zoom, pan, crosshair, raw table, and saved-result panels still work after the above checks.
Multi-trace spectrum analyzer file viewer with noise PSD, IP3, crosshair, Y-zoom, and reference lines.
Dependencies: React 18, React DOM 18, PropTypes, and Recharts 2 are vendored locally for GitHub Pages.
-->
<!DOCTYPE html>
<html lang="en"><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<!-- SEO Meta Tags -->
<title>Spectrum Analyzer .DAT File Viewer | Mergen Scope</title>
<meta name="description" content="Open and visualize .dat files from spectrum analyzers. Multi-trace support, markers, noise PSD, IP3/TOI measurements. Free, browser-based, no dependencies.">
<meta name="keywords" content="spectrum analyzer, .dat viewer, Rohde and Schwarz .dat, RF measurements, open source, browser waveform viewer">
<meta name="author" content="Alp Gökalp">
<meta name="robots" content="noindex, nofollow">
<meta name="language" content="English">
<link rel="icon" type="image/svg+xml" href="mergen-scope-icon.svg?v=2">
<link rel="icon" type="image/png" sizes="48x48" href="mergen-scope-icon.png?v=1">
<link rel="shortcut icon" href="mergen-scope-icon.png?v=1">
<link rel="apple-touch-icon" href="mergen-scope-icon.png?v=1">
<!-- Open Graph / Social Media -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://alpgoxd.github.io/mergen-scope/mergen_scope.html">
<meta property="og:title" content="Spectrum Analyzer .DAT File Viewer">
<meta property="og:description" content="Visualize spectrum analyzer measurements in your browser. Multi-trace, markers, IP3/TOI tools included.">
<meta property="og:image" content="https://alpgoxd.github.io/mergen-scope/og-image.png">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Spectrum Analyzer .DAT File Viewer">
<meta name="twitter:description" content="Open-source .dat file viewer for RF/microwave measurements">
<!-- Canonical URL -->
<link rel="canonical" href="https://alpgoxd.github.io/mergen-scope/mergen_scope.html">
<!-- Structured Data (JSON-LD) -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebApplication",
"name": "Spectrum Analyzer .DAT File Viewer",
"description": "Browser-based viewer for spectrum analyzer measurement files with advanced analysis tools",
"url": "https://alpgoxd.github.io/mergen-scope/",
"author": {
"@type": "Person",
"name": "Alp Gökalp"
},
"applicationCategory": "Utility",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
},
"softwareVersion": "1.0",
"isAccessibleForFree": true,
"license": "https://www.gnu.org/licenses/gpl-3.0.html",
"operatingSystem": "Web Browser",
"browserRequirements": "ES6+",
"featureList": [
"Multi-trace support",
"Interactive markers",
"Noise PSD panel",
"IP3/TOI measurements",
"Zoom and pan",
"Import/export functionality"
]
}
</script>
<style>
:root{
color-scheme:light dark;
--bg:#fff;--card:#f6f8fa;--border:#d1d9e0;--accent:#0078d4;
--text:#1f2328;--muted:#656d76;--dim:#8b949e;
--grid:rgba(0,0,0,0.2);
--trace1:#00994d;--trace2:#d4380d;--trace3:#0066cc;--trace4:#b8860b;--trace5:#8b3dbb;--trace6:#008577;
--derived1:#5b8def;--derived2:#ef7d57;--derived3:#44b88b;--derived4:#b07cf7;--derived5:#d4a72c;--derived6:#e56aa6;
--mkr-n:#c2185b;--mkr-d:#6d28d9;--mkr-p:#d12f2f;
--tip-bg:#fff;--tip-bd:#d1d9e0;
--da:#f6f8fa;--damp:#996600;
--err-bg:#fff3cd;--err-bd:#ffc107;--err-tx:#664d03;
--drop-h:rgba(0,120,212,0.04);
--mb:rgba(194,24,91,0.06);--mbb:rgba(194,24,91,0.18);
--pb:rgba(209,47,47,0.06);--pbb:rgba(209,47,47,0.18);
--db:rgba(109,40,217,0.06);--dbb:rgba(109,40,217,0.18);
--ft:rgba(0,120,212,0.08);--ftb:rgba(0,120,212,0.2);
--noise-tr:#9966cc;--ip3-color:#ff6699;
--refline:#d97706;--refline-h:#0f8f9a;
--crosshair:rgba(0,120,212,0.4);
}
@media(prefers-color-scheme:dark){:root{
--bg:#0d1117;--card:#161b22;--border:#30363d;--accent:#58a6ff;
--text:#e6edf3;--muted:#8b949e;--dim:#484f58;
--grid:rgba(255,255,255,0.06);
--trace1:#00ff88;--trace2:#ff6644;--trace3:#44aaff;--trace4:#ffcc00;--trace5:#cc66ff;--trace6:#00ffcc;
--derived1:#7fb3ff;--derived2:#ff9b73;--derived3:#62d3a3;--derived4:#c39bff;--derived5:#ffd766;--derived6:#ff96c2;
--mkr-n:#ff5fa2;--mkr-d:#a78bfa;--mkr-p:#ff6b6b;
--tip-bg:#1c2128;--tip-bd:#30363d;
--da:rgba(13,17,23,0.5);--damp:#ffcc00;
--err-bg:rgba(246,68,10,0.1);--err-bd:#f64;--err-tx:#ffa;
--drop-h:rgba(0,180,255,0.04);
--mb:rgba(255,95,162,0.06);--mbb:rgba(255,95,162,0.18);
--pb:rgba(255,107,107,0.06);--pbb:rgba(255,107,107,0.18);
--db:rgba(167,139,250,0.06);--dbb:rgba(167,139,250,0.18);
--ft:rgba(88,166,255,0.08);--ftb:rgba(88,166,255,0.2);
--noise-tr:#cc99ff;--ip3-color:#ff88aa;
--refline:#f59e0b;--refline-h:#22b8c7;
--crosshair:rgba(88,166,255,0.4);
}}
*{margin:0;padding:0;box-sizing:border-box;}
body{background:var(--bg);color:var(--text);font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;}
#root{height:100vh;}
::-webkit-scrollbar{width:6px;}
::-webkit-scrollbar-track{background:transparent;}
::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px;}
.recharts-cartesian-grid-horizontal line,.recharts-cartesian-grid-vertical line{stroke:var(--grid)!important;}
.recharts-default-tooltip{background:var(--tip-bg)!important;border-color:var(--tip-bd)!important;color:var(--text)!important;}
.recharts-tooltip-item{color:var(--text)!important;}
.recharts-legend-item-text{color:var(--text)!important;}
.crosshair-readout{position:absolute;top:8px;right:8px;background:var(--tip-bg);border:1px solid var(--tip-bd);border-radius:6px;padding:6px 10px;font-size:11px;font-family:monospace;z-index:11;pointer-events:none;max-width:280px;}
.crosshair-readout .cr-row{display:flex;justify-content:space-between;gap:12px;padding:1px 0;}
.crosshair-readout .cr-dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:4px;}
.clr-btn:hover{color:#f55!important;border-color:#f55!important;}
.io-glow{transition:box-shadow .16s ease,border-color .16s ease,transform .16s ease,color .16s ease,background-color .16s ease;}
.io-glow:hover{box-shadow:0 0 0 1px rgba(82,168,255,0.22),0 0 16px rgba(82,168,255,0.18);border-color:#52a8ff!important;color:#d7ebff!important;transform:translateY(-1px);}
</style>
</head>
<body>
<div id="root"></div>
<script src="./vendor/react/react.production.min.js"></script>
<script src="./vendor/react/react-is.production.min.js"></script>
<script src="./vendor/react-dom/react-dom.production.min.js"></script>
<script src="./vendor/prop-types/prop-types.min.js"></script>
<script src="./vendor/recharts/Recharts.min.js"></script>
<script src="./app-modules/trace-model.js?v=20260402"></script>
<script src="./app-modules/trace-helpers.js?v=20260402"></script>
<script src="./app-modules/trace-ops-helpers.js?v=20260402"></script>
<script src="./app-modules/analysis-helpers.js?v=20260402"></script>
<script src="./app-modules/analysis-target-helpers.js?v=20260402"></script>
<script src="./app-modules/range-analysis-helpers.js?v=20260402"></script>
<script src="./app-modules/file-store-helpers.js?v=20260402"></script>
<script src="./app-modules/touchstone-math-helpers.js?v=20260402"></script>
<script src="./app-modules/parser-helpers.js?v=20260402"></script>
<script src="./app-modules/marker-helpers.js?v=20260402"></script>
<script src="./app-modules/derived-state-helpers.js?v=20260402"></script>
<script src="./app-modules/ui-helpers.js?v=20260402"></script>
<script src="./app-modules/pane-helpers.js?v=20260402"></script>
<script src="./app-modules/demo-workspaces.js?v=20260402"></script>
<script src="./app-modules/workspace-helpers.js?v=20260402"></script>
<script src="./app-modules/export-helpers.js?v=20260402"></script>
<script src="./app-modules/app-hooks.js?v=20260402"></script>
<script src="./app-modules/app-shell-components.js?v=20260402"></script>
<script src="./app-modules/app-analysis-components.js?v=20260402"></script>
<script src="./app-modules/app-chart-components.js?v=20260402"></script>
<script src="./app-modules/app-controller.js?v=20260402"></script>
<script type="module">
var APP_BUILD_VERSION="20260402";
try{
var currentUrl=new URL(window.location.href);
var versionParam=currentUrl.searchParams.get("v");
if(versionParam!==APP_BUILD_VERSION){
currentUrl.searchParams.set("v",APP_BUILD_VERSION);
window.location.replace(currentUrl.toString());
}
}catch(_){ }
var React=window.React;
var h=React.createElement;
var createRoot=window.ReactDOM.createRoot;
var AppController=window.AppController||{};
if(!AppController.AppRoot){
throw new Error("AppController.AppRoot is not available.");
}
createRoot(document.getElementById("root")).render(h(AppController.AppRoot));
</script>
</body></html>