Skip to content

Commit a6ee9de

Browse files
committed
setup of discrete-time & domain transform foundations
1 parent 855e6a9 commit a6ee9de

32 files changed

+1879
-269
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ A User Guide is included in the web app, and can be launched from the main menu
9292

9393
4. Run `npm test` to execute the tests via [Jest](https://jestjs.io/)
9494

95-
> _Current test coverage according to Jest: 66% (statements)_
95+
> _Current test coverage according to Jest: 63% (statements)_
9696
9797
## Code Structure
9898

index.html

Lines changed: 64 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,31 @@ <h5>Delete all</h5>
261261
</button>
262262
</div>
263263
</section>
264+
<section class="ribbon-section">
265+
<header class="ribbon-section-header">
266+
<h2>Discretize</h2>
267+
</header>
268+
<div class="ribbon-section-contents">
269+
<button
270+
class="navbar-button button-with-tooltip"
271+
id="transform-tf-button"
272+
>
273+
<i>s &#x21C4; z</i>
274+
<p>Transform</p>
275+
<div class="tooltip">
276+
<h5>Continuous &#x21C4; Discrete-time transform</h5>
277+
<p>
278+
Create a new tf, which is a transformed copy of the original tf
279+
(discrete or continuous-time respectively) using an
280+
approximation method
281+
</p>
282+
<p class="tooltip-expansion">
283+
After selecting a tf, click this button
284+
</p>
285+
</div>
286+
</button>
287+
</div>
288+
</section>
264289
<section class="ribbon-section">
265290
<header class="ribbon-section-header">
266291
<h2>Optimize</h2>
@@ -422,7 +447,7 @@ <h5>User Guide</h5>
422447
</main>
423448
<section class="element-analysis-window hidden">
424449
<div class="element-analysis-window-header">
425-
<h2>Transfer function</h2>
450+
<h2 id="element-analysis-window-header-text">Transfer function</h2>
426451
<div>
427452
<button
428453
class="element-analysis-window-header-button hidden"
@@ -448,29 +473,45 @@ <h2>Transfer function</h2>
448473
</div>
449474
</div>
450475
<div class="element-analysis-window-contents">
451-
<form class="modify-element-value">
452-
<div>
453-
<p>Numer:</p>
454-
<input
455-
type="text"
456-
placeholder="[1, 2, 3]"
457-
id="update-element-numerator-input"
458-
class="update-element-value-input"
459-
/>
460-
</div>
461-
<div>
462-
<p>Denom:</p>
463-
<input
464-
type="text"
465-
placeholder="[1, 2, 3]"
466-
id="update-element-denominator-input"
467-
class="update-element-value-input"
468-
/>
476+
<section class="element-info-container">
477+
<form>
478+
<div>
479+
<p>Numer:</p>
480+
<input
481+
type="text"
482+
placeholder="[1, 2, 3]"
483+
id="update-element-numerator-input"
484+
class="update-element-value-input"
485+
/>
486+
</div>
487+
<div>
488+
<p>Denom:</p>
489+
<input
490+
type="text"
491+
placeholder="[1, 2, 3]"
492+
id="update-element-denominator-input"
493+
class="update-element-value-input"
494+
/>
495+
</div>
496+
<button class="update-element-value-button" type="submit">
497+
Update value
498+
</button>
499+
</form>
500+
<div class="sampling-t-input-container hidden">
501+
<div>
502+
<p>Sampling T:</p>
503+
</div>
504+
<div>
505+
<p
506+
id="element-sampling-t-input"
507+
class="update-element-value-input"
508+
>
509+
N/A
510+
</p>
511+
<p>[s]</p>
512+
</div>
469513
</div>
470-
<button class="update-element-value-button" type="submit">
471-
Update value
472-
</button>
473-
</form>
514+
</section>
474515
<section class="single-plot-container">
475516
<div
476517
class="element-analysis-window-tab-buttons tab-buttons-container"

math/complexAnalysis/complexAnalysisService.js

Lines changed: 141 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import { Complex } from "../../assets/lib/Complex/Complex.js";
10-
import { isEven, isOdd } from "../../util/commons.js";
10+
import { isEven, isOdd, pascalTriangleLine } from "../../util/commons.js";
1111

1212
/**
1313
* Starting from a polynomial of variable s,
@@ -26,13 +26,11 @@ import { isEven, isOdd } from "../../util/commons.js";
2626
*
2727
* <-> real: [-5, 0, 7], imag: [3, 0]
2828
*
29-
* Formula:
29+
* Thus the rule:
3030
*
31-
* [0, t4, 0, -t2, 0, t0] (real terms)
32-
*
33-
* [t5, 0, -t3, 0, t1, 0] (imag terms)
34-
*
35-
* 5, 4, 3, 2, 1, 0 (term order)
31+
* - [ 0, t4, 0, -t2, 0, t0] (real terms)
32+
* - [t5, 0, -t3, 0, t1, 0] (imag terms)
33+
* - [ 5, 4, 3, 2, 1, 0] (term order)
3634
*/
3735
export const polynomialEvaluatedWithWiRealTermsArray = function (termsArray) {
3836
const length = termsArray.length;
@@ -64,13 +62,11 @@ export const polynomialEvaluatedWithWiRealTermsArray = function (termsArray) {
6462
*
6563
* <-> real: [-5, 0, 7], imag: [3, 0]
6664
*
67-
* Formula:
68-
*
69-
* [0, t4, 0, -t2, 0, t0] (real terms)
65+
* Thus the rule:
7066
*
71-
* [t5, 0, -t3, 0, t1, 0] (imag terms)
72-
*
73-
* 5, 4, 3, 2, 1, 0 (term order)
67+
* - [ 0 t4, 0, -t2, 0, t0] (real terms)
68+
* - [t5, 0, -t3, 0, t1, 0] (imag terms)
69+
* - [ 5, 4, 3, 2, 1, 0] (term order)
7470
*/
7571
export const polynomialEvaluatedWithWiImagTermsArray = function (termsArray) {
7672
const length = termsArray.length;
@@ -85,6 +81,138 @@ export const polynomialEvaluatedWithWiImagTermsArray = function (termsArray) {
8581
});
8682
};
8783

84+
/**
85+
* Starting from a polynomial of variable z,
86+
* after the latter variable is substituted by the complex number z=e^(w*T*i),
87+
* compute the real terms as a function of variable w
88+
*
89+
* (used in discrete-time Bode plot computation)
90+
*
91+
* Formulas used below:
92+
*
93+
* - Euler's: e^(x*i) = cos(x) + i*sin(x)
94+
* - i^1 = i, i^2 =-1, i^3 =-i, i^4 = 1, i^5 = i
95+
*
96+
* Example:
97+
*
98+
* [5, 3, 7] <-> 5*z^2 + 3*z + 7
99+
*
100+
* => 5*e^(2*w*Ti) + 3*e^(w*i*T) + 7
101+
* = 5*(cos(w*T) + i*sin(w*T))^2 + 3*(cos(w*T) + i*sin(w*T)) + 7
102+
*
103+
* In general, with: a=cos(w*T), b=sin(w*T):
104+
*
105+
* - (a+b*i)^2 = a^2 + i*2*a*b - b^2
106+
* - (a+b*i)^3 = a^3 + i*3*a^2*b - 3*a*b^2 - i*b^3
107+
* - (a+b*i)^4 = a^4 + i*4*a^3*b - 6*a^2*b^2 - i*4*a*b^3 + b^4
108+
*
109+
* Thus the rule:
110+
*
111+
* - [t4*(a^4 - 6*a^2*b^2 + b^4), t3*(a^3 - 3*a*b^2), t2*(a^2 - b^2), t1*a, t0] (real terms)
112+
* - [t4*(4*a^3*b -4*a*b^3), t3*(3*a^2*b - 3*b^3), t2*(2*a*b), t1*b, 0] (imag terms)
113+
* - [4, 3, 2, 1, 0] (term order)
114+
*/
115+
export const discreteTimePolynomialEvaluatedWithWiRealTermsFunction = function (
116+
termsArray,
117+
samplingT
118+
) {
119+
const a = (w) => Math.cos(w * samplingT);
120+
const b = (w) => Math.sin(w * samplingT);
121+
122+
const realTerms = termsArray
123+
.map((term, i) => {
124+
//computation, filtering & addition of all real terms
125+
//stemming from a single binomial
126+
const order = termsArray.length - i - 1;
127+
128+
const line = pascalTriangleLine(order, false);
129+
const mapped = line.map((pascalTerm, j) => {
130+
const sign = j % 4 === 2 || j % 4 === 3 ? -1 : 1;
131+
return (w) =>
132+
term * sign * pascalTerm * a(w) ** (line.length - j - 1) * b(w) ** j;
133+
});
134+
return mapped
135+
.filter((_, i) => isEven(i))
136+
.reduce(
137+
(acc, x) => (w) => acc(w) + x(w),
138+
(w) => 0
139+
);
140+
})
141+
.reduce(
142+
//addition of all real terms stemming from all binomials
143+
(acc, x) => (w) => acc(w) + x(w),
144+
(w) => 0
145+
);
146+
147+
return realTerms;
148+
};
149+
150+
/**
151+
* Starting from a polynomial of variable z,
152+
* after the latter variable is substituted by the complex number z=e^(w*T*i),
153+
* compute the imag terms as a function of variable w
154+
*
155+
* (used in discrete-time Bode plot computation)
156+
*
157+
* Formulas used below:
158+
*
159+
* - Euler's: e^(x*i) = cos(x) + i*sin(x)
160+
* - i^1 = i, i^2 =-1, i^3 =-i, i^4 = 1, i^5 = i
161+
*
162+
* Example:
163+
*
164+
* [5, 3, 7] <-> 5*z^2 + 3*z + 7
165+
*
166+
* => 5*e^(2*w*Ti) + 3*e^(w*i*T) + 7
167+
* = 5*(cos(w*T) + i*sin(w*T))^2 + 3*(cos(w*T) + i*sin(w*T)) + 7
168+
*
169+
* In general, with: a=cos(w*T), b=sin(w*T):
170+
*
171+
* - (a+b*i)^2 = a^2 + i*2*a*b - b^2
172+
* - (a+b*i)^3 = a^3 + i*3*a^2*b - 3*a*b^2 - i*b^3
173+
* - (a+b*i)^4 = a^4 + i*4*a^3*b - 6*a^2*b^2 - i*4*a*b^3 + b^4
174+
*
175+
* Thus the rule:
176+
*
177+
* - [t4*(a^4 - 6*a^2*b^2 + b^4), t3*(a^3 - 3*a*b^2), t2*(a^2 - b^2), t1*a, t0] (real terms)
178+
* - [t4*(4*a^3*b -4*a*b^3), t3*(3*a^2*b - 3*b^3), t2*(2*a*b), t1*b, 0] (imag terms)
179+
* - [4, 3, 2, 1, 0] (term order)
180+
*/
181+
export const discreteTimePolynomialEvaluatedWithWiImagTermsFunction = function (
182+
termsArray,
183+
samplingT
184+
) {
185+
const a = (w) => Math.cos(w * samplingT);
186+
const b = (w) => Math.sin(w * samplingT);
187+
188+
const imagTerms = termsArray
189+
.map((term, i) => {
190+
//computation, filtering & addition of all imag terms
191+
//stemming from a single binomial
192+
const order = termsArray.length - i - 1;
193+
194+
const line = pascalTriangleLine(order, false);
195+
const mapped = line.map((pascalTerm, j) => {
196+
const sign = j % 4 === 2 || j % 4 === 3 ? -1 : 1;
197+
return (w) =>
198+
term * sign * pascalTerm * a(w) ** (line.length - j - 1) * b(w) ** j;
199+
});
200+
return mapped
201+
.filter((_, i) => isOdd(i))
202+
.reduce(
203+
(acc, x) => (w) => acc(w) + x(w),
204+
(w) => 0
205+
);
206+
})
207+
.reduce(
208+
//addition of all imag terms stemming from all binomials
209+
(acc, x) => (w) => acc(w) + x(w),
210+
(w) => 0
211+
);
212+
213+
return imagTerms;
214+
};
215+
88216
//
89217
// Methods using the 'Complex' library
90218
//

math/computerAlgebra/algebraicOperations.js

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
primitiveOperationsSymbols,
2525
isSymbol,
2626
isReal,
27+
containsSymbols,
2728
} from "../../util/commons.js";
2829

2930
/**
@@ -53,19 +54,9 @@ export const addTypeTag = (typeTag, content) =>
5354
* Invocation of the appropriate operation for arguments of the respective data types
5455
*/
5556
const invokeOperation = function (operation, ...args) {
56-
//helper functions
57-
const containsSymbols = function (tree) {
58-
const traverseTreeForSymbols = (result, t) => {
59-
if (t === null) return result;
60-
return Array.isArray(t[0])
61-
? traverseTreeForSymbols(result, t[0]) ||
62-
traverseTreeForSymbols(result, t[1])
63-
: (isSymbol(t[0]) && !primitiveOperationsSymbols.includes(t[0])) ||
64-
traverseTreeForSymbols(result, t.slice(1));
65-
};
66-
return traverseTreeForSymbols(false, tree);
67-
};
68-
57+
//
58+
// Helper functions
59+
//
6960
const getTypeTag = function (obj) {
7061
if (Array.isArray(obj)) {
7162
return containsSymbols(obj) && primitiveOperationsSymbols.includes(obj[0])
@@ -127,6 +118,7 @@ export const reduce = (p1, p2) => invokeOperation("reduce", p1, p2); // polynomi
127118

128119
export const getNumerator = (r) => invokeOperation("getNumerator", r); // ratios
129120
export const getDenominator = (r) => invokeOperation("getDenominator", r); // ratios
121+
export const getParam = (p) => invokeOperation("getParam", p); // polynomials
130122
export const getTermsArray = (p) => invokeOperation("getTermsArray", p); // polynomials
131123

132124
export const round = (x, d) => invokeOperation("round", x, d);

math/computerAlgebra/dataTypes/polynomials.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@ export const loadPolynomialsOperations = function () {
342342
simplifyPolynomials(p1, p2).map(tag)
343343
);
344344

345+
set(["getParam", "polynomial"], param);
345346
set(["getTermsArray", "polynomial"], termsArray);
346347

347348
set(["round", "polynomial", "real"], (p, d) => tag(roundPolynomial(p, d)));

0 commit comments

Comments
 (0)