何かあれば GitHub のリポジトリに issue を作るか ryukau@gmail.com までお気軽にどうぞ。
Update: 2024-08-06
数式上は \(\phi\) と \(n\) が循環参照になっていますが、実装では \(\phi\) から \(n\) を計算できます。
class PTROscillator:
def __init__(self, samplerate, frequency):
self.phi = 0.0
self.T = frequency / samplerate
self.h = 1.0
def process(self):
self.phi += self.T
if self.phi >= 1.0:
self.phi -= 1.0
return self.PTRSaw3(self.phi, self.T, self.h)
def PTRSaw3(self, phi, T, h):
= phi / T
n if n >= 3.0:
return 2*T*n-3*T-1
if 0.0 <= n and n < 1.0:
return -(h*n**3)/3+2*T*n+2*h-3*T-1
if 1.0 <= n and n < 2.0:
return (2*h*n**3)/3-3*h*n**2+3*h*n+2*T*n+h-3*T-1
if 2.0 <= n and n < 3.0:
return -(h*n**3)/3+3*h*n**2-9*h*n+2*T*n+9*h-3*T-1
return 0 # 念のため。
以下のコードを wxMaxima にコピペすれば式が得られます。
DPWSaw(N) := block([result: [], eq: x = 0],
integration_constant: 'C,
reset(integration_constant_counter),
for i: 1 thru N do block([a, c],
eq: integrate(eq, x),
a: rhs(solve(eq, concat('C, i))[1]),
if i > 1 then (
c: rhs(solve(subst(1, x, a) = subst(-1, x, a), concat('C, i - 1))[1]),
a: subst(c, concat('C, i - 1), a),
eq: subst(c, concat('C, i - 1), eq)
),a: num(a),
a: a / coeff(a, x, hipow(a, x)),
result: endcons(expand(a), result)
),push(x, result)
);
PTRSaw(dpw, N) := block([ptr],
removeTinverse(eq) := block([r: 0],
if atom(eq) then r: eq
else for term in args(eq) do block([den],
den: denom(term),
if atom(den) then
if den # T then r: r + term
),r
),
phi(n, boundary) :=
if boundary = 'n or n > boundary then n * T
else n * T + h,
s(n, boundary) := 2 * phi(n, boundary) - 1,
y(dpw, N) := block([M: N - 1, result: []],
for b: 0 thru M do block([eq: 0],
for k: 0 thru M do
eq: eq + (-1)^k * binomial(M, k) * subst(s(n - k, n - b), x, dpw[N]),
result: endcons(eq, result)
),result / (2 * T)^(N - 1) / N!
),
ptr: expand(ratsimp(y(dpw, N))),
for i: 1 thru length(ptr) do ptr[i]: removeTinverse(ptr[i]),
ptr
);
dispptrsaw(dpwsaw) := block([N: length(dpwsaw)],
for i: 1 thru N do block([eq],
eq: PTRSaw(dpwsaw, i),
disp(concat('PTRSaw, i - 1)),
disp([(length(eq) - 1) * T <= phi, eq[1]]),
for j: 2 thru length(eq) do
disp([(j - 2) * T <= phi and phi < (j - 1) * T, eq[j]])
)
);
dpwsaw: DPWSaw(10);
dispptrsaw(dpwsaw);
/* format.py で利用。 */
/*
dispptrlist(dpw, ptrfunc) := block([L: []],
for i: 1 thru length(dpw) do L: endcons(ptrfunc(dpw, i), L),
L
);
dispptrlist(dpwsaw, PTRSaw);
*/
位相が \([0, 1)\) で一周するとき、 \([0, 0.5)\) の範囲の計算式だけを求めています。
導出中の遷移領域の位置 k
と、波形の不連続点
b
の関係に応じて絶対値 abs(x)
を
x
か -x
に置き換えています。位相が \([0, 1)\) で一周するとき、 \([0, 0.5)\)
の範囲の計算式だけを求めています。
Saw とは \(\phi\) と \(s\) が異なります。スケーリング係数は Saw の 2 倍です。
DPWTri(N) := block([result: []],
tri(N) := block([poly: 0, poly_diff: [], A: [], D],
for i: 1 thru N do (
if oddp(i) then
poly: poly + if i = N then x^i else concat(a, i) * x^i
elseif i = N then
poly: poly + x^(i - 1) * abs(x)
),
D: subst(x, abs(x), poly),
D: diff(D, x),
poly_diff: endcons(D, poly_diff),
while hipow(D, x) > 1 do (
D: diff(D, x, 2),
poly_diff: endcons(D, poly_diff)
),
for i: length(poly_diff) step -1 thru 1 do (
D: solve(subst(1 / 2, x, poly_diff[i]) = 0, concat(a, i * 2 - 1)),
A: append(A, subst(A, D))
),
subst(A, poly)
),
for i: 1 thru N do result: endcons(tri(i), result),
result
);
PTRTri(dpw, N) := block([ptr: []],
phi(n, boundary) :=
if boundary = 'n or n > boundary then n * T
else n * T + 1,
s_odd(n, boundary) := 1 / 2 - abs(1 - 2 * phi(n, boundary)),
s_even(n, boundary) := 1 - 2 * phi(n, boundary),
replaceabs(eq, sign) := block(
f(x) := if not atom(x) and op(x) = abs then sign * part(x, 1) else x,
if atom(eq) then eq else scanmap(f, eq)
),
y(dpw, N, s) := block([M: N - 1, result: []],
for b: 0 thru M do block([eq: 0, boundary],
boundary: 'n - b,
for k: 0 thru M do block([abs_x],
abs_x: if b = 0 then x elseif k < b then x else -x,
eq: eq + (-1)^k * binomial(M, k) * replaceabs(
subst(s(n - k, boundary), x, dpw[N]),
if b = 0 then -1 elseif k < b then -1 else 1
)
),result: endcons(eq, result)
),result * 2 / (2 * T)^(N - 1) / N!
),
ptr: y(dpw, N, if oddp(N) then s_odd else s_even),
ptr: expand(ratsimp(ptr)),
ptr
);
dispptrtri(dpwtri) := block([N: length(dpwtri)],
for i: 1 thru N do block([eq],
eq: PTRTri(dpwtri, i),
disp(concat('PTRTri, i - 1)),
for j: 1 thru length(eq) do disp([eq[j]])
)
);
dispptrlist(dpw) := block([L: []],
for i: 1 thru length(dpw) do L: endcons(PTRTri(dpw, i), L),
L
);
dpwtri: DPWTri(11);
dispptrtri(dpwtri);
Saw を元にして境界の向こう側を 0 に置き直しました。
DPWRamp(N) := block([result: [], eq: x + 1 = 0],
integration_constant: 'C,
reset(integration_constant_counter),
for i: 1 thru N do block([a, c],
eq: integrate(eq, x),
a: rhs(solve(eq, concat('C, i))[1]),
if i > 1 then (
/* solve の式を変更。 */
c: rhs(solve(subst(-1, x, a) = 0, concat('C, i - 1))[1]),
a: subst(c, concat('C, i - 1), a),
eq: subst(c, concat('C, i - 1), eq)
),a: num(a),
a: a / coeff(a, x, hipow(a, x)),
result: endcons(expand(a), result)
),push(x, result)
);
PTRRamp(dpw, N) := block([ptr],
removeTinverse(eq) := block([r: 0],
if atom(eq) then r: eq
else for term in args(eq) do block([den],
den: denom(term),
if atom(den) then
if den # T then r: r + term
),r
),
phi(n, boundary) :=
if boundary = 'n or n > boundary then n * T
else 0, /* 境界を超えると 0 。 */
s(n, boundary) := 2 * phi(n, boundary) - 1,
y(dpw, N) := block([M: N - 1, result: []],
for b: 0 thru M do block([eq: 0],
for k: 0 thru M do
eq: eq + (-1)^k * binomial(M, k) * subst(s(n - k, n - b), x, dpw[N]),
result: endcons(eq, result)
),result / (2 * T)^(N - 1) / N!
),
ptr: expand(ratsimp(y(dpw, N))),
for i: 1 thru length(ptr) do ptr[i]: removeTinverse(ptr[i]),
ptr
);
dispptrramp(dpwramp) := block([N: length(dpwramp)],
for i: 1 thru N do block([eq],
eq: PTRRamp(dpwramp, i),
disp(concat('PTRRamp, i - 1)),
disp([(length(eq) - 1) * T <= phi, eq[1]]),
for j: 2 thru length(eq) do
disp([(j - 2) * T <= phi and phi < (j - 1) * T, eq[j]])
)
);
dispptrlist(dpw) := block([L: []],
for i: 1 thru length(dpw) do L: endcons(PTRRamp(dpw, i), L),
L
);
dpwramp: DPWRamp(10);
dispptrramp(dpwramp);
Ramp 関数の微分です。
DPWStep(N) := block([result: [], eq: x + 1 = 0],
integration_constant: 'C,
reset(integration_constant_counter),
for i: 1 thru N do block([a, c],
eq: integrate(eq, x),
a: rhs(solve(eq, concat('C, i))[1]),
if i > 1 then (
c: rhs(solve(subst(-1, x, a) = 0, concat('C, i - 1))[1]),
a: subst(c, concat('C, i - 1), a),
eq: subst(c, concat('C, i - 1), eq)
),a: num(a),
a: a / coeff(a, x, hipow(a, x)),
result: endcons(diff(expand(a), x), result) /* diff を追加。 */
),push(x, result)
);
PTRStep(dpw, N) := block([ptr],
removeTinverse(eq) := block([r: 0],
if atom(eq) then r: eq
else for term in args(eq) do block([den],
den: denom(term),
if atom(den) then
if den # T then r: r + term
),r
),
phi(n, boundary) :=
if boundary = 'n or n > boundary then n * T
else h, /* 0 でも h でも結果が同じになる。 */
s(n, boundary) := 2 * phi(n, boundary) - 1,
y(dpw, N) := block([M: N - 1, result: []],
for b: 0 thru M do block([eq: 0],
for k: 0 thru M do
eq: eq + (-1)^k * binomial(M, k) * subst(s(n - k, n - b), x, dpw[N]),
result: endcons(eq, result)
),result / (2 * T)^(N - 1) / N!
),
ptr: expand(ratsimp(y(dpw, N))),
for i: 1 thru length(ptr) do ptr[i]: removeTinverse(ptr[i]),
ptr
);
dpwstep: DPWStep(10);
dispptrramp(dpwstep);