eyeling 1.16.2 → 1.16.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HANDBOOK.md +4 -0
- package/README.md +0 -1
- package/examples/ershov-mixed-computation.n3 +106 -0
- package/examples/output/ershov-mixed-computation.n3 +15 -0
- package/eyeling.js +510 -263
- package/lib/cli.js +22 -12
- package/lib/engine.js +488 -251
- package/package.json +2 -3
- package/arctifacts/README.md +0 -59
- package/arctifacts/ackermann.html +0 -678
- package/arctifacts/auroracare.html +0 -1297
- package/arctifacts/bike-trip.html +0 -752
- package/arctifacts/binomial-theorem.html +0 -631
- package/arctifacts/bmi.html +0 -511
- package/arctifacts/building-performance.html +0 -750
- package/arctifacts/clinical-care.html +0 -726
- package/arctifacts/collatz.html +0 -403
- package/arctifacts/complex.html +0 -321
- package/arctifacts/control-system.html +0 -482
- package/arctifacts/delfour.html +0 -849
- package/arctifacts/earthquake-epicenter.html +0 -982
- package/arctifacts/eco-route.html +0 -662
- package/arctifacts/euclid-infinitude.html +0 -564
- package/arctifacts/euler-identity.html +0 -667
- package/arctifacts/exoplanet-transit.html +0 -1000
- package/arctifacts/faltings-theorem.html +0 -1046
- package/arctifacts/fibonacci.html +0 -299
- package/arctifacts/fundamental-theorem-arithmetic.html +0 -398
- package/arctifacts/godel-numbering.html +0 -743
- package/arctifacts/gps-bike.html +0 -759
- package/arctifacts/gps-clinical-bench.html +0 -792
- package/arctifacts/graph-french.html +0 -449
- package/arctifacts/grass-molecular.html +0 -592
- package/arctifacts/group-theory.html +0 -740
- package/arctifacts/health-info.html +0 -833
- package/arctifacts/kaprekar-constant.html +0 -576
- package/arctifacts/lee.html +0 -805
- package/arctifacts/linked-lists.html +0 -502
- package/arctifacts/lldm.html +0 -612
- package/arctifacts/matrix-multiplication.html +0 -502
- package/arctifacts/matrix.html +0 -651
- package/arctifacts/newton-raphson.html +0 -944
- package/arctifacts/peano-factorial.html +0 -456
- package/arctifacts/pi.html +0 -363
- package/arctifacts/polynomial.html +0 -646
- package/arctifacts/prime.html +0 -366
- package/arctifacts/pythagorean-theorem.html +0 -468
- package/arctifacts/rest-path.html +0 -469
- package/arctifacts/roots-of-unity.html +0 -363
- package/arctifacts/turing.html +0 -409
- package/arctifacts/wind-turbines.html +0 -726
|
@@ -1,482 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
-
<title>Control System</title>
|
|
7
|
-
<style>
|
|
8
|
-
:root {
|
|
9
|
-
--bg: #ffffff;
|
|
10
|
-
--ink: #111827;
|
|
11
|
-
--muted: #6b7280;
|
|
12
|
-
--panel: #f8fafc;
|
|
13
|
-
--border: #e5e7eb;
|
|
14
|
-
--accent: #0ea5e9;
|
|
15
|
-
--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', monospace;
|
|
16
|
-
--ui:
|
|
17
|
-
system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', 'Apple Color Emoji',
|
|
18
|
-
'Segoe UI Emoji';
|
|
19
|
-
}
|
|
20
|
-
html,
|
|
21
|
-
body {
|
|
22
|
-
height: 100%;
|
|
23
|
-
}
|
|
24
|
-
body {
|
|
25
|
-
margin: 0;
|
|
26
|
-
background: var(--bg);
|
|
27
|
-
color: var(--ink);
|
|
28
|
-
font: 15px/1.55 var(--ui);
|
|
29
|
-
}
|
|
30
|
-
.wrap {
|
|
31
|
-
max-width: 980px;
|
|
32
|
-
margin: 36px auto;
|
|
33
|
-
padding: 0 16px;
|
|
34
|
-
}
|
|
35
|
-
header {
|
|
36
|
-
display: flex;
|
|
37
|
-
align-items: center;
|
|
38
|
-
justify-content: space-between;
|
|
39
|
-
margin-bottom: 16px;
|
|
40
|
-
}
|
|
41
|
-
h1 {
|
|
42
|
-
font-size: 22px;
|
|
43
|
-
margin: 0;
|
|
44
|
-
letter-spacing: 0.2px;
|
|
45
|
-
}
|
|
46
|
-
.pill {
|
|
47
|
-
display: inline-flex;
|
|
48
|
-
align-items: center;
|
|
49
|
-
gap: 8px;
|
|
50
|
-
padding: 6px 10px;
|
|
51
|
-
border: 1px solid var(--border);
|
|
52
|
-
border-radius: 999px;
|
|
53
|
-
color: var(--muted);
|
|
54
|
-
background: #fff;
|
|
55
|
-
}
|
|
56
|
-
.row {
|
|
57
|
-
display: flex;
|
|
58
|
-
flex-direction: column;
|
|
59
|
-
gap: 16px;
|
|
60
|
-
}
|
|
61
|
-
.card {
|
|
62
|
-
background: var(--panel);
|
|
63
|
-
border: 1px solid var(--border);
|
|
64
|
-
border-radius: 14px;
|
|
65
|
-
overflow: hidden;
|
|
66
|
-
}
|
|
67
|
-
.head {
|
|
68
|
-
display: flex;
|
|
69
|
-
align-items: center;
|
|
70
|
-
justify-content: space-between;
|
|
71
|
-
padding: 10px 12px;
|
|
72
|
-
border-bottom: 1px solid var(--border);
|
|
73
|
-
}
|
|
74
|
-
.head h2 {
|
|
75
|
-
font-size: 13px;
|
|
76
|
-
text-transform: uppercase;
|
|
77
|
-
letter-spacing: 0.08em;
|
|
78
|
-
color: var(--muted);
|
|
79
|
-
margin: 0;
|
|
80
|
-
}
|
|
81
|
-
.body {
|
|
82
|
-
padding: 12px;
|
|
83
|
-
}
|
|
84
|
-
pre {
|
|
85
|
-
white-space: pre-wrap;
|
|
86
|
-
background: #fff;
|
|
87
|
-
border: 1px solid var(--border);
|
|
88
|
-
border-radius: 10px;
|
|
89
|
-
padding: 12px;
|
|
90
|
-
overflow: auto;
|
|
91
|
-
font-family: var(--mono);
|
|
92
|
-
font-size: 13px;
|
|
93
|
-
}
|
|
94
|
-
button {
|
|
95
|
-
all: unset;
|
|
96
|
-
background: #fff;
|
|
97
|
-
border: 1px solid var(--border);
|
|
98
|
-
padding: 8px 12px;
|
|
99
|
-
border-radius: 10px;
|
|
100
|
-
cursor: pointer;
|
|
101
|
-
}
|
|
102
|
-
button:hover {
|
|
103
|
-
border-color: #cbd5e1;
|
|
104
|
-
}
|
|
105
|
-
.small {
|
|
106
|
-
color: var(--muted);
|
|
107
|
-
text-decoration: none;
|
|
108
|
-
}
|
|
109
|
-
.accent {
|
|
110
|
-
color: var(--accent);
|
|
111
|
-
}
|
|
112
|
-
/* ARC v2 (Cards) */
|
|
113
|
-
.arc-grid {
|
|
114
|
-
display: flex;
|
|
115
|
-
flex-direction: column;
|
|
116
|
-
gap: 12px;
|
|
117
|
-
}
|
|
118
|
-
.arc-card {
|
|
119
|
-
background: #fff;
|
|
120
|
-
border: 1px solid var(--border);
|
|
121
|
-
border-radius: 14px;
|
|
122
|
-
overflow: hidden;
|
|
123
|
-
}
|
|
124
|
-
.arc-card .ac-head {
|
|
125
|
-
display: flex;
|
|
126
|
-
align-items: center;
|
|
127
|
-
gap: 8px;
|
|
128
|
-
padding: 10px 12px;
|
|
129
|
-
border-bottom: 1px solid var(--border);
|
|
130
|
-
font-size: 12px;
|
|
131
|
-
letter-spacing: 0.08em;
|
|
132
|
-
text-transform: uppercase;
|
|
133
|
-
color: var(--muted);
|
|
134
|
-
}
|
|
135
|
-
.arc-card .ac-body {
|
|
136
|
-
padding: 12px;
|
|
137
|
-
}
|
|
138
|
-
.arc-card pre {
|
|
139
|
-
margin: 0;
|
|
140
|
-
white-space: pre-wrap;
|
|
141
|
-
background: #fff;
|
|
142
|
-
border: 1px solid #eef2f7;
|
|
143
|
-
border-radius: 10px;
|
|
144
|
-
padding: 10px;
|
|
145
|
-
font-family: var(--mono);
|
|
146
|
-
font-size: 13px;
|
|
147
|
-
}
|
|
148
|
-
.arc-card.answer {
|
|
149
|
-
border-left: 4px solid #10b981;
|
|
150
|
-
}
|
|
151
|
-
.arc-card.reason {
|
|
152
|
-
border-left: 4px solid #3b82f6;
|
|
153
|
-
}
|
|
154
|
-
.arc-card.check {
|
|
155
|
-
border-left: 4px solid #f59e0b;
|
|
156
|
-
}
|
|
157
|
-
.toolbar {
|
|
158
|
-
display: flex;
|
|
159
|
-
gap: 8px;
|
|
160
|
-
align-items: center;
|
|
161
|
-
}
|
|
162
|
-
</style>
|
|
163
|
-
</head>
|
|
164
|
-
<body>
|
|
165
|
-
<div class="wrap">
|
|
166
|
-
<header>
|
|
167
|
-
<h1>Control System</h1>
|
|
168
|
-
<div class="pill">Self‑contained • No deps</div>
|
|
169
|
-
</header>
|
|
170
|
-
|
|
171
|
-
<div class="card" style="margin-bottom: 16px">
|
|
172
|
-
<div class="body">
|
|
173
|
-
<p>
|
|
174
|
-
<strong>What this is?</strong> A self‑contained, browser‑only <em>ARC harness</em> for a toy closed‑loop
|
|
175
|
-
control system. It embeds a small set of measurable facts and computes two actuator commands with simple
|
|
176
|
-
control laws. Everything runs on this page—no network and no external libraries.
|
|
177
|
-
</p>
|
|
178
|
-
<p>
|
|
179
|
-
<strong>How to use it:</strong> Review the embedded facts, press <em>Run</em> to generate the analysis, and
|
|
180
|
-
optionally export the ARC report. The output is organized as <em>Answer</em> (final values),
|
|
181
|
-
<em>Reason why</em> (explanations), <em>Trace</em> (step‑by‑step derivations), and <em>Check</em> (a small
|
|
182
|
-
harness that re‑derives the results and verifies identities).
|
|
183
|
-
</p>
|
|
184
|
-
<p class="small">
|
|
185
|
-
This page is provided for reproducibility and documentation; it is not guidance for real‑world control
|
|
186
|
-
design.
|
|
187
|
-
</p>
|
|
188
|
-
</div>
|
|
189
|
-
</div>
|
|
190
|
-
|
|
191
|
-
<div class="row">
|
|
192
|
-
<div class="card">
|
|
193
|
-
<div class="head">
|
|
194
|
-
<h2>Facts (embedded)</h2>
|
|
195
|
-
<a
|
|
196
|
-
href="#"
|
|
197
|
-
class="small"
|
|
198
|
-
onclick="
|
|
199
|
-
copyFacts();
|
|
200
|
-
return false;
|
|
201
|
-
"
|
|
202
|
-
>Copy facts</a
|
|
203
|
-
>
|
|
204
|
-
</div>
|
|
205
|
-
<div class="body">
|
|
206
|
-
<div
|
|
207
|
-
id="facts"
|
|
208
|
-
style="
|
|
209
|
-
font-family: var(--mono);
|
|
210
|
-
font-size: 12px;
|
|
211
|
-
background: #fff;
|
|
212
|
-
border: 1px dashed var(--border);
|
|
213
|
-
border-radius: 10px;
|
|
214
|
-
padding: 10px;
|
|
215
|
-
white-space: pre-wrap;
|
|
216
|
-
"></div>
|
|
217
|
-
<div style="margin-top: 10px; display: flex; gap: 8px; flex-wrap: wrap">
|
|
218
|
-
<button onclick="run()">▶ Run</button>
|
|
219
|
-
<a
|
|
220
|
-
href="#"
|
|
221
|
-
class="small"
|
|
222
|
-
onclick="
|
|
223
|
-
copyARC();
|
|
224
|
-
return false;
|
|
225
|
-
"
|
|
226
|
-
>Copy ARC</a
|
|
227
|
-
>
|
|
228
|
-
</div>
|
|
229
|
-
</div>
|
|
230
|
-
</div>
|
|
231
|
-
|
|
232
|
-
<div class="card">
|
|
233
|
-
<div class="head">
|
|
234
|
-
<h2>Trace</h2>
|
|
235
|
-
<button onclick="downloadText('control_system_trace.txt', document.getElementById('trace').textContent)">
|
|
236
|
-
⬇ Export .txt
|
|
237
|
-
</button>
|
|
238
|
-
</div>
|
|
239
|
-
<div class="body"><pre id="trace">(no run yet)</pre></div>
|
|
240
|
-
</div>
|
|
241
|
-
|
|
242
|
-
<div class="card">
|
|
243
|
-
<div class="head">
|
|
244
|
-
<h2>ARC Output</h2>
|
|
245
|
-
<div class="toolbar">
|
|
246
|
-
<button onclick="toggleArcStyle()" id="styleBtn">Style: Cards</button>
|
|
247
|
-
<button onclick="downloadARC()">⬇ Export .txt</button>
|
|
248
|
-
</div>
|
|
249
|
-
</div>
|
|
250
|
-
<div class="body">
|
|
251
|
-
<div id="arc-cards" class="arc-grid">
|
|
252
|
-
<div class="arc-card answer">
|
|
253
|
-
<div class="ac-head">Answer</div>
|
|
254
|
-
<div class="ac-body"><pre id="arc_ans">(no run yet)</pre></div>
|
|
255
|
-
</div>
|
|
256
|
-
<div class="arc-card reason">
|
|
257
|
-
<div class="ac-head">Reason why</div>
|
|
258
|
-
<div class="ac-body"><pre id="arc_reason">(no run yet)</pre></div>
|
|
259
|
-
</div>
|
|
260
|
-
<div class="arc-card check">
|
|
261
|
-
<div class="ac-head">Check (harness)</div>
|
|
262
|
-
<div class="ac-body"><pre id="arc_check">(no run yet)</pre></div>
|
|
263
|
-
</div>
|
|
264
|
-
</div>
|
|
265
|
-
<pre id="arc" style="display: none">(no run yet)</pre>
|
|
266
|
-
</div>
|
|
267
|
-
</div>
|
|
268
|
-
</div>
|
|
269
|
-
</div>
|
|
270
|
-
|
|
271
|
-
<script>
|
|
272
|
-
// ---------------- Facts (embedded) ----------------
|
|
273
|
-
const measurement1 = { input1: [6, 11], disturbance2: [45, 39] };
|
|
274
|
-
const measurement2 = { input2: true };
|
|
275
|
-
const measurement3 = { disturbance1: 35766 };
|
|
276
|
-
const measurement4 = { output2: 24 };
|
|
277
|
-
const observation1 = { state1: 80 };
|
|
278
|
-
const observation2 = { state2: false };
|
|
279
|
-
const observation3 = { state3: 22 };
|
|
280
|
-
const target2 = { output2: 29 };
|
|
281
|
-
|
|
282
|
-
function measurement10(I) {
|
|
283
|
-
// PND feedback control
|
|
284
|
-
const [M1, M2] = measurement1[I];
|
|
285
|
-
if (M1 < M2) {
|
|
286
|
-
const M3 = M2 - M1;
|
|
287
|
-
return Math.sqrt(M3);
|
|
288
|
-
} else {
|
|
289
|
-
return M1;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// ---------------- Control rules ----------------
|
|
294
|
-
function control1_actuator1() {
|
|
295
|
-
// control1(actuator1, C)
|
|
296
|
-
const steps = [];
|
|
297
|
-
const M1 = measurement10('input1');
|
|
298
|
-
if (measurement2['input2'] !== true) throw new Error('input2 must be true');
|
|
299
|
-
const D1 = Number(measurement3['disturbance1']);
|
|
300
|
-
const C1 = M1 * 19.6;
|
|
301
|
-
const C2 = Math.log(D1) / Math.log(10.0); // ≡ Math.log10(D1)
|
|
302
|
-
const C = C1 - C2;
|
|
303
|
-
|
|
304
|
-
steps.push(`[actuator1] M1 = measurement10(input1) = sqrt(11-6) = ${M1.toFixed(12)} (PND feedback control)`);
|
|
305
|
-
steps.push(`[actuator1] C1 = M1 * 19.6 = ${C1.toFixed(12)} % proportional part`);
|
|
306
|
-
steps.push(`[actuator1] C2 = log(D1)/log(10) = log(35766)/log(10) = ${C2.toFixed(12)} % compensation part`);
|
|
307
|
-
steps.push(`[actuator1] C = C1 - C2 = ${C.toFixed(12)} % simple feedforward control`);
|
|
308
|
-
|
|
309
|
-
return { actuator: 'actuator1', C, steps };
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
function control1_actuator2() {
|
|
313
|
-
// control1(actuator2, C)
|
|
314
|
-
const steps = [];
|
|
315
|
-
const P3 = Number(observation3['state3']);
|
|
316
|
-
const M4 = Number(measurement4['output2']);
|
|
317
|
-
const T2 = Number(target2['output2']);
|
|
318
|
-
const E = T2 - M4;
|
|
319
|
-
const D = P3 - M4;
|
|
320
|
-
const C1 = 5.8 * E;
|
|
321
|
-
const N = 7.3 / E;
|
|
322
|
-
const C2 = N * D;
|
|
323
|
-
const C = C1 + C2;
|
|
324
|
-
|
|
325
|
-
steps.push(`[actuator2] E = T2 - M4 = ${T2.toFixed(0)} - ${M4.toFixed(0)} = ${E.toFixed(12)} % error`);
|
|
326
|
-
steps.push(
|
|
327
|
-
`[actuator2] D = P3 - M4 = ${P3.toFixed(0)} - ${M4.toFixed(0)} = ${D.toFixed(12)} % differential error`,
|
|
328
|
-
);
|
|
329
|
-
steps.push(`[actuator2] C1 = 5.8 * E = 5.8*${E.toFixed(0)} = ${C1.toFixed(12)} % proportional part`);
|
|
330
|
-
steps.push(`[actuator2] N = 7.3 / E = 7.3/${E.toFixed(0)} = ${N.toFixed(12)} % nonlinear factor`);
|
|
331
|
-
steps.push(
|
|
332
|
-
`[actuator2] C2 = N * D = ${N.toFixed(12)}*${D.toFixed(0)} = ${C2.toFixed(12)} % nonlinear differential part`,
|
|
333
|
-
);
|
|
334
|
-
steps.push(`[actuator2] C = C1 + C2 = ${C1.toFixed(12)} + ${C2.toFixed(12)} = ${C.toFixed(12)}`);
|
|
335
|
-
|
|
336
|
-
return { actuator: 'actuator2', C, steps };
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
function control1_all() {
|
|
340
|
-
return [control1_actuator1(), control1_actuator2()];
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// ---------------- ARC printing ----------------
|
|
344
|
-
function run() {
|
|
345
|
-
const sols = control1_all();
|
|
346
|
-
|
|
347
|
-
// ----- Answer / Reason / Check -----
|
|
348
|
-
const A = [],
|
|
349
|
-
R = [],
|
|
350
|
-
C = [],
|
|
351
|
-
T = [];
|
|
352
|
-
|
|
353
|
-
// Answer
|
|
354
|
-
for (const s of sols) {
|
|
355
|
-
A.push(`control1(${s.actuator}, C) → C = ${s.C.toFixed(12)}`);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// Reason why (conceptual, short; details live in Trace)
|
|
359
|
-
R.push('Two simple control laws produce actuator setpoints:');
|
|
360
|
-
R.push(
|
|
361
|
-
' • actuator1 uses feedback (sqrt of difference) plus a proportional term and a log-based compensation, then subtracts the compensation.',
|
|
362
|
-
);
|
|
363
|
-
R.push(
|
|
364
|
-
' • actuator2 uses proportional-on-error plus a nonlinear differential part (7.3/E)×(P3−M4), then adds both.',
|
|
365
|
-
);
|
|
366
|
-
R.push('Values are computed from the embedded facts shown above.');
|
|
367
|
-
|
|
368
|
-
// Trace: stitch both step lists
|
|
369
|
-
for (const s of sols) for (const line of s.steps) T.push(line);
|
|
370
|
-
|
|
371
|
-
// Check (harness)
|
|
372
|
-
try {
|
|
373
|
-
check_harness(sols);
|
|
374
|
-
C.push('OK: derivations match the rules; identities and PND logic verified.');
|
|
375
|
-
} catch (e) {
|
|
376
|
-
C.push('FAILED: ' + e.message);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// Populate Trace
|
|
380
|
-
document.getElementById('trace').textContent = T.join('\n');
|
|
381
|
-
|
|
382
|
-
// Populate Cards
|
|
383
|
-
document.getElementById('arc_ans').textContent = A.join('\n');
|
|
384
|
-
document.getElementById('arc_reason').textContent = R.join('\n');
|
|
385
|
-
document.getElementById('arc_check').textContent = C.join('\n');
|
|
386
|
-
|
|
387
|
-
// Keep a combined plain-text for copy/export
|
|
388
|
-
const combined = [
|
|
389
|
-
'Answer',
|
|
390
|
-
'------',
|
|
391
|
-
...A,
|
|
392
|
-
'',
|
|
393
|
-
'Reason why',
|
|
394
|
-
'----------',
|
|
395
|
-
...R,
|
|
396
|
-
'',
|
|
397
|
-
'Check (harness)',
|
|
398
|
-
'---------------',
|
|
399
|
-
...C,
|
|
400
|
-
].join('\n');
|
|
401
|
-
document.getElementById('arc').textContent = combined;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// ---------------- Harness ----------------
|
|
405
|
-
function check_harness(solutions) {
|
|
406
|
-
const eps = 1e-12;
|
|
407
|
-
// PND feedback control checks
|
|
408
|
-
if (Math.abs(measurement10('input1') - Math.sqrt(11 - 6)) >= eps)
|
|
409
|
-
throw new Error("measurement10('input1') mismatch");
|
|
410
|
-
if (Math.abs(measurement10('disturbance2') - 45.0) >= eps)
|
|
411
|
-
throw new Error("measurement10('disturbance2') mismatch");
|
|
412
|
-
// log identity
|
|
413
|
-
const D1 = Number(measurement3['disturbance1']);
|
|
414
|
-
if (Math.abs(Math.log(D1) / Math.log(10.0) - Math.log10(D1)) >= eps) throw new Error('log identity mismatch');
|
|
415
|
-
// recompute C values
|
|
416
|
-
const recomputed = {};
|
|
417
|
-
recomputed['actuator1'] = measurement10('input1') * 19.6 - Math.log(D1) / Math.log(10.0);
|
|
418
|
-
const E = target2['output2'] - measurement4['output2']; // 29-24 = 5
|
|
419
|
-
const D = observation3['state3'] - measurement4['output2']; // 22-24 = -2
|
|
420
|
-
recomputed['actuator2'] = 5.8 * E + (7.3 / E) * D;
|
|
421
|
-
for (const s of solutions) {
|
|
422
|
-
if (Math.abs(s.C - recomputed[s.actuator]) >= eps) throw new Error(`Mismatch for ${s.actuator}`);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// ---------------- Pretty facts ----------------
|
|
427
|
-
function showFacts() {
|
|
428
|
-
const obj = {
|
|
429
|
-
measurement1,
|
|
430
|
-
measurement2,
|
|
431
|
-
measurement3,
|
|
432
|
-
measurement4,
|
|
433
|
-
observation1,
|
|
434
|
-
observation2,
|
|
435
|
-
observation3,
|
|
436
|
-
target2,
|
|
437
|
-
};
|
|
438
|
-
document.getElementById('facts').textContent = JSON.stringify(obj, null, 2);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
// Utilities
|
|
442
|
-
function downloadText(name, text) {
|
|
443
|
-
const blob = new Blob([text], { type: 'text/plain' });
|
|
444
|
-
const a = document.createElement('a');
|
|
445
|
-
a.href = URL.createObjectURL(blob);
|
|
446
|
-
a.download = name;
|
|
447
|
-
document.body.appendChild(a);
|
|
448
|
-
a.click();
|
|
449
|
-
a.remove();
|
|
450
|
-
}
|
|
451
|
-
function copyARC() {
|
|
452
|
-
navigator.clipboard.writeText(document.getElementById('arc').textContent);
|
|
453
|
-
}
|
|
454
|
-
function copyFacts() {
|
|
455
|
-
navigator.clipboard.writeText(document.getElementById('facts').textContent);
|
|
456
|
-
}
|
|
457
|
-
function toggleArcStyle() {
|
|
458
|
-
const pre = document.getElementById('arc');
|
|
459
|
-
const cards = document.getElementById('arc-cards');
|
|
460
|
-
const btn = document.getElementById('styleBtn');
|
|
461
|
-
const showingCards = cards.style.display !== 'none';
|
|
462
|
-
if (showingCards) {
|
|
463
|
-
cards.style.display = 'none';
|
|
464
|
-
pre.style.display = 'block';
|
|
465
|
-
btn.textContent = 'Style: Plain';
|
|
466
|
-
} else {
|
|
467
|
-
cards.style.display = 'block';
|
|
468
|
-
pre.style.display = 'none';
|
|
469
|
-
btn.textContent = 'Style: Cards';
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
function downloadARC() {
|
|
473
|
-
const text = document.getElementById('arc').textContent;
|
|
474
|
-
downloadText('control_system_arc.txt', text);
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
// bootstrap
|
|
478
|
-
showFacts();
|
|
479
|
-
run();
|
|
480
|
-
</script>
|
|
481
|
-
</body>
|
|
482
|
-
</html>
|