Skip to content

Commit 299ddb3

Browse files
committed
phase computation fix & enhancement
1 parent 09d70ee commit 299ddb3

File tree

4 files changed

+132
-28
lines changed

4 files changed

+132
-28
lines changed

test/definitions/plotsTests.js

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -407,11 +407,11 @@ export const plotsTests = {
407407
characteristicNumbers?.rollOffText,
408408
"0 [dB/dec] (low), 0 [dB/dec] (high)",
409409
],
410-
["Phase at wMin", phaseCurvePoints[0][1], "-180", toleranceTestsMedium],
410+
["Phase at wMin", phaseCurvePoints[0][1], "0", toleranceTestsMedium],
411411
[
412412
"Phase at wMax",
413413
phaseCurvePoints[phaseCurvePoints.length - 1][1],
414-
"0",
414+
"180",
415415
toleranceTestsMedium,
416416
],
417417
],
@@ -479,11 +479,11 @@ export const plotsTests = {
479479
phaseCurvePoints,
480480
characteristicNumbers
481481
) => [
482-
["Phase at wMin", phaseCurvePoints[0][1], "-720", toleranceTestsMedium],
482+
["Phase at wMin", phaseCurvePoints[0][1], "180", toleranceTestsMedium],
483483
[
484484
"Phase at wMax",
485485
phaseCurvePoints[phaseCurvePoints.length - 1][1],
486-
"-720",
486+
"180",
487487
toleranceTestsMedium,
488488
],
489489
],
@@ -542,4 +542,64 @@ export const plotsTests = {
542542
],
543543
],
544544
},
545+
546+
test19: {
547+
description: "test19: tf([1, 2], [1, 5])",
548+
numeratorTermsArray: [1, 2],
549+
denominatorTermsArray: [1, 5],
550+
steps: bodeSteps,
551+
assertions: (
552+
magnitudeCurvePoints,
553+
phaseCurvePoints,
554+
characteristicNumbers
555+
) => [
556+
["Phase at wMin", phaseCurvePoints[0][1], "0", toleranceTestsMedium],
557+
[
558+
"Phase at wMax",
559+
phaseCurvePoints[phaseCurvePoints.length - 1][1],
560+
"0",
561+
toleranceTestsMedium,
562+
],
563+
],
564+
},
565+
566+
test20: {
567+
description: "test20: tf([1, -2], [1, 5])",
568+
numeratorTermsArray: [1, -2],
569+
denominatorTermsArray: [1, 5],
570+
steps: bodeSteps,
571+
assertions: (
572+
magnitudeCurvePoints,
573+
phaseCurvePoints,
574+
characteristicNumbers
575+
) => [
576+
["Phase at wMin", phaseCurvePoints[0][1], "180", toleranceTestsMedium],
577+
[
578+
"Phase at wMax",
579+
phaseCurvePoints[phaseCurvePoints.length - 1][1],
580+
"0",
581+
toleranceTestsMedium,
582+
],
583+
],
584+
},
585+
586+
test21: {
587+
description: "test21: tf([-0.15, -2, -200, -2000], [1, 5, 1000, 2000])",
588+
numeratorTermsArray: [-0.15, -2, -200, -2000],
589+
denominatorTermsArray: [1, 5, 1000, 2000],
590+
steps: bodeSteps,
591+
assertions: (
592+
magnitudeCurvePoints,
593+
phaseCurvePoints,
594+
characteristicNumbers
595+
) => [
596+
["Phase at wMin", phaseCurvePoints[0][1], "180", toleranceTestsMedium],
597+
[
598+
"Phase at wMax",
599+
phaseCurvePoints[phaseCurvePoints.length - 1][1],
600+
"180",
601+
toleranceTestsMedium,
602+
],
603+
],
604+
},
545605
};

util/commons.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,13 @@ export const roundDecimalDigitsNumericalAnalysis = 3;
5555
export const roundDecimalDigitsPrettyPrinting = 3;
5656
export const roundDecimalDigitsTests = 3;
5757

58-
export const toleranceNumericalAnalysisTiny = 10 ** -10;
58+
export const toleranceNumericalAnalysisTiny = 10 ** -8;
5959
export const toleranceNumericalAnalysisSmall = 10 ** -4;
6060
export const toleranceTestsSmall = 10 ** -4;
6161
export const toleranceTestsMedium = 0.2;
6262
export const toleranceTestsLarge = 3;
63+
export const tolerancePhaseUnwrapMedium = 0.05;
64+
export const tolerancePhaseAdjustmentLarge = 10;
6365

6466
export const isZeroWithinTolerance = function (x) {
6567
return Math.abs(x) < toleranceNumericalAnalysisTiny;

view/plots/bodePlot.js

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import {
1515
functionFromPolynomialTermsArray,
1616
areAllTfTermsNumbers,
1717
roundDecimal,
18+
toleranceNumericalAnalysisSmall,
19+
tolerancePhaseAdjustmentLarge,
1820
} from "../../util/commons.js";
1921
import { logMessages } from "../../util/loggingService.js";
2022
import {
@@ -159,8 +161,16 @@ export default class BodePlot {
159161

160162
//compute expected steep phase shifts due to polynomial zeros & poles at y-axis
161163
const expectedSteepPhaseShiftsMap = new Map();
162-
const zerosAtYAxis = this.#zeros.filter((x) => x[0] === 0 && x[1] !== 0);
163-
const polesAtYAxis = this.#poles.filter((x) => x[0] === 0 && x[1] !== 0);
164+
const zerosAtYAxis = this.#zeros.filter(
165+
(x) =>
166+
(x[0] === 0 && x[1] !== 0) ||
167+
(Math.abs(x[0]) < 1 && Math.abs(x[1] / x[0]) > 15)
168+
);
169+
const polesAtYAxis = this.#poles.filter(
170+
(x) =>
171+
(x[0] === 0 && x[1] !== 0) ||
172+
(Math.abs(x[0]) < 1 && Math.abs(x[1] / x[0]) > 15)
173+
);
164174

165175
zerosAtYAxis.forEach((x) => {
166176
const value = expectedSteepPhaseShiftsMap.get(Math.abs(x[1]));
@@ -186,7 +196,8 @@ export default class BodePlot {
186196
let newMagnitudeValue;
187197
let newPhaseValue;
188198
let lastMagnitudeValue = magnitude(this.#wMin);
189-
let lastPhaseValue = (180 / Math.PI) * phase(this.#wMin);
199+
const firstPhaseValue = (180 / Math.PI) * phase(this.#wMin);
200+
let lastPhaseValue = firstPhaseValue;
190201

191202
const variableStep = new VariableStep();
192203
const phaseUnwrapper = new PhaseUnwrapper(expectedSteepPhaseShiftsMap);
@@ -256,22 +267,49 @@ export default class BodePlot {
256267
? Math.max(...magnitudeCurvePoints)
257268
: undefined;
258269

259-
//adjust phase based on expected phase value at wMax according to the transfer function polynomials
260-
const zerosAtPositiveHalfplane = this.#zeros.filter((x) => x[0] > 0).length;
261-
const zerosAtNegativeHalfplaneOrYAxis = this.#zeros.filter(
262-
(x) => x[0] <= 0
263-
).length;
270+
//calculate pre-adjusted phase min & max values
271+
const minPhaseTemp =
272+
this.#phaseCurvePoints.length > 0
273+
? Math.min(...this.#phaseCurvePoints.map((x) => x[1]))
274+
: undefined;
275+
const maxPhaseTemp =
276+
this.#phaseCurvePoints.length > 0
277+
? Math.max(...this.#phaseCurvePoints.map((x) => x[1]))
278+
: undefined;
279+
280+
let expectedPhaseValueAtWmax = lastPhaseValue;
281+
const tol = tolerancePhaseAdjustmentLarge;
264282

265-
const expectedPhaseValueAtWmax =
266-
-90 *
267-
(this.#denominatorTermsArray.length -
268-
1 +
269-
zerosAtPositiveHalfplane -
270-
zerosAtNegativeHalfplaneOrYAxis);
283+
//adjust expected phase value at wMax - case 1
284+
if (minPhaseTemp > 180 - tol && maxPhaseTemp > minPhaseTemp + 2 * tol) {
285+
expectedPhaseValueAtWmax = lastPhaseValue - 360;
286+
} else if (
287+
maxPhaseTemp < -180 + tol &&
288+
maxPhaseTemp > minPhaseTemp + 2 * tol
289+
) {
290+
expectedPhaseValueAtWmax = lastPhaseValue + 360;
291+
}
292+
293+
//adjust expected phase value at wMax - case 2
294+
//(display absolute phase values beyond 180, as an exception in this case)
295+
const polesAtYAxisOrOrigin = this.#poles.filter((x) => x[0] === 0).length;
296+
const zerosAtYAxisOrOrigin = this.#zeros.filter((x) => x[0] === 0).length;
297+
if (
298+
this.#poles.length === polesAtYAxisOrOrigin &&
299+
this.#zeros.length === 0
300+
) {
301+
expectedPhaseValueAtWmax = -90 * polesAtYAxisOrOrigin;
302+
} else if (
303+
this.#zeros.length === zerosAtYAxisOrOrigin &&
304+
this.#poles.length === 0
305+
) {
306+
expectedPhaseValueAtWmax = 90 * zerosAtYAxisOrOrigin;
307+
}
271308

272-
if (lastPhaseValue > expectedPhaseValueAtWmax + 10) {
309+
//apply adjustment
310+
if (lastPhaseValue > expectedPhaseValueAtWmax + tol) {
273311
const factor = Math.ceil(
274-
(Math.abs(lastPhaseValue - expectedPhaseValueAtWmax) - 10) / 180
312+
(Math.abs(lastPhaseValue - expectedPhaseValueAtWmax) - tol) / 180
275313
);
276314
logMessages(
277315
[
@@ -287,9 +325,9 @@ export default class BodePlot {
287325
this.#phaseCurvePoints.forEach(
288326
(_, i) => (this.#phaseCurvePoints[i][1] -= factor * 180)
289327
);
290-
} else if (lastPhaseValue + 10 < expectedPhaseValueAtWmax) {
328+
} else if (lastPhaseValue + tol < expectedPhaseValueAtWmax) {
291329
const factor = Math.ceil(
292-
(Math.abs(expectedPhaseValueAtWmax - lastPhaseValue) - 10) / 180
330+
(Math.abs(expectedPhaseValueAtWmax - lastPhaseValue) - tol) / 180
293331
);
294332
logMessages(
295333
[

view/plots/plotService.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
* View / Plots / PlotService
77
*/
88

9-
import { isPowerOfTen, roundDecimal } from "../../util/commons.js";
9+
import {
10+
isPowerOfTen,
11+
roundDecimal,
12+
tolerancePhaseUnwrapMedium,
13+
} from "../../util/commons.js";
1014
import { logMessages } from "../../util/loggingService.js";
1115

1216
// to run with NPM uncomment this:
@@ -191,14 +195,14 @@ export class PhaseUnwrapper {
191195
newPhaseValue += this.#phaseAdjustmentTotal;
192196
this.#newAdjustment = 0;
193197
let diff = newPhaseValue - lastPhaseValue;
194-
if (diff > 300) {
198+
if (diff > 220) {
195199
// console.log("case1", newPhaseValue, lastPhaseValue);
196200
logMessages(
197201
[`[CP-83] Phase unwarp adjustment by -360, at w=${roundDecimal(w, 5)}`],
198202
"checkpoints"
199203
);
200204
this.#newAdjustment = -360;
201-
} else if (diff < -300) {
205+
} else if (diff < -220) {
202206
// console.log("case2", newPhaseValue, lastPhaseValue);
203207
logMessages(
204208
[`[CP-84] Phase unwarp adjustment by +360, at w=${roundDecimal(w, 5)}`],
@@ -208,13 +212,13 @@ export class PhaseUnwrapper {
208212
} else if (
209213
(diff > 70 || diff < -70) &&
210214
[...this.#expectedSteepPhaseShiftsMap].filter(
211-
(x) => Math.abs(x[0] - w) < 0.05
215+
(x) => Math.abs(x[0] - w) < tolerancePhaseUnwrapMedium
212216
).length === 1
213217
) {
214218
// console.log("case3", newPhaseValue, lastPhaseValue);
215219
const expectedSteepPhaseShift = [
216220
...this.#expectedSteepPhaseShiftsMap,
217-
].filter((x) => Math.abs(x[0] - w) < 0.05)[0][1];
221+
].filter((x) => Math.abs(x[0] - w) < tolerancePhaseUnwrapMedium)[0][1];
218222
logMessages(
219223
[
220224
`[CP-85] Steep phase shift by ${

0 commit comments

Comments
 (0)