Surface NMR processing and inversion GUI
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

adapt.py 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. import numpy as np
  2. from numpy.linalg import lstsq
  3. from numpy.linalg import norm
  4. from numpy import fft
  5. import pylab
  6. from scipy.signal import correlate
  7. def autocorr(x):
  8. #result = np.correlate(x, x, mode='full')
  9. result = correlate(x, x, mode='full')
  10. return result[result.size/2:]
  11. class AdaptiveFilter:
  12. def __init__(self, mu):
  13. self.mu = mu
  14. def adapt_filt_Ref(self, x, R, M, mu, PCA, lambda2=0.95, H0=0):
  15. """ Taken from .m file
  16. This function is written to allow the user to filter a input signal
  17. with an adaptive filter that utilizes 2 reference signals instead of
  18. the standard method which allows for only 1 reference signal.
  19. Author: Rob Clemens Date: 3/16/06
  20. Modified and ported to Python, now takes arbitray number of reference points
  21. """
  22. #from akvo.tressel import pca
  23. import akvo.tressel.pca as pca
  24. if np.shape(x) != np.shape(R[0]): # or np.shape(x) != np.shape(rx1):
  25. print ("Error, non aligned")
  26. exit(1)
  27. if PCA == "Yes":
  28. #print("Performing PCA calculation in noise cancellation")
  29. # PCA decomposition on ref channels so signals are less related
  30. R, K, means = pca.pca( R )
  31. # test for in loop reference
  32. #print("Cull nearly zero terms?", np.shape(x), np.shape(R))
  33. #R = R[0:3,:]
  34. #R = R[2:4,:]
  35. #print(" removed zero terms?", np.shape(x), np.shape(R))
  36. #H0 = H0[0:3*np.shape(x)[0]]
  37. #H0 = H0[0:2*np.shape(x)[0]]
  38. if all(H0) == 0:
  39. H = np.zeros( (len(R)*M))
  40. #print ("resetting filter")
  41. else:
  42. H = H0
  43. #H = np.zeros( (len(R)*M))
  44. Rn = np.ones(len(R)*M) / mu
  45. r_ = np.zeros( (len(R), M) )
  46. e = np.zeros(len(x)) # error, desired output
  47. ilambda = lambda2**-1
  48. for z in range(0, len(x)):
  49. # Only look forwards, to avoid distorting the lates times
  50. # (run backwards, if opposite and you don't care about distorting very late time.)
  51. for ir in range(len(R)):
  52. if z < M:
  53. r_[ir,0:z] = R[ir][0:z]
  54. r_[ir,z:M] = 0
  55. else:
  56. # TODO, use np.delete and np.append to speed this up
  57. r_[ir,:] = R[ir][z-M:z]
  58. # reshape
  59. r_n = np.reshape(r_, -1) #concatenate((r_v, r_h ))
  60. #K = np.dot( np.diag(Rn,0), r_n) / (lambda2 + np.dot(r_n*Rn, r_n)) # Create/update K
  61. K = (Rn* r_n) / (lambda2 + np.dot(r_n*Rn, r_n)) # Create/update K
  62. e[z] = x[z] - np.dot(r_n.T, H) # e is the filtered signal, input - r(n) * Filter Coefs
  63. H += K*e[z]; # Update Filter Coefficients
  64. Rn = ilambda*Rn - ilambda*np.dot(np.dot(K, r_n.T), Rn) # Update R(n)
  65. return e, H
  66. def transferFunctionFFT(self, D, R, reg=1e-2):
  67. from akvo.tressel import pca
  68. """
  69. Computes the transfer function (H) between a Data channel and
  70. a number of Reference channels. The Matrices D and R are
  71. expected to be in the frequency domain on input.
  72. | R1'R1 R1'R2 R1'R3| |h1| |R1'D|
  73. | R2'R1 R2'R2 R2'R3| * |h2| = |R2'D|
  74. | R3'R1 R3'R2 R3'R3| |h3| |R3'D|
  75. Returns the corrected array
  76. """
  77. # PCA decomposition on ref channels so signals are less related
  78. #transMatrix, K, means = pca.pca( np.array([rx0, rx1]))
  79. #RR = np.zeros(( np.shape(R[0])[0]*np.shape(R[0])[1], len(R)))
  80. # RR = np.zeros(( len(R), np.shape(R[0])[0]*np.shape(R[0])[1] ))
  81. # for ir in range(len(R)):
  82. # RR[ir,:] = np.reshape(R[ir], -1)
  83. # transMatrix, K, means = pca.pca(RR)
  84. # #R rx0 = transMatrix[0,:]
  85. # # rx1 = transMatrix[1,:]
  86. # for ir in range(len(R)):
  87. # R[ir] = transMatrix[ir,0]
  88. import scipy.linalg
  89. import akvo.tressel.pca as pca
  90. # Compute as many transfer functions as len(R)
  91. # A*H = B
  92. nref = len(R)
  93. H = np.zeros( (np.shape(D)[1], len(R)), dtype=complex )
  94. for iw in range(np.shape(D)[1]):
  95. A = np.zeros( (nref, nref), dtype=complex )
  96. B = np.zeros( (nref) , dtype=complex)
  97. for ii in range(nref):
  98. for jj in range(nref):
  99. # build A
  100. A[ii,jj] = np.dot(R[ii][:,iw], R[jj][:,iw])
  101. # build B
  102. B[ii] = np.dot( R[ii][:,iw], D[:,iw] )
  103. # compute H(iw)
  104. #linalg.solve(a,b) if a is square
  105. #print "A", A
  106. #print "B", B
  107. # TODO, regularise this solve step? So as to not fit the spurious noise
  108. #print np.shape(B), np.shape(A)
  109. #H[iw, :] = scipy.linalg.solve(A,B)
  110. H[iw, :] = scipy.linalg.lstsq(A,B,cond=reg)[0]
  111. #print "lstqt", np.shape(scipy.linalg.lstsq(A,B))
  112. #print "solve", scipy.linalg.solve(A,B)
  113. #H[iw,:] = scipy.linalg.lstsq(A,B) # otherwise
  114. #H = np.zeros( (np.shape(D)[1], ) )
  115. #print H #A, B
  116. Error = np.zeros(np.shape(D), dtype=complex)
  117. for ir in range(nref):
  118. for q in range( np.shape(D)[0] ):
  119. #print "dimcheck", np.shape(H[:,ir]), np.shape(R[ir][q,:] )
  120. Error[q,:] += H[:,ir]*R[ir][q,:]
  121. return D - Error
  122. def adapt_filt_tworefFreq(self, x, rx0, rx1, M, lambda2=0.95):
  123. """ Frequency domain version of above
  124. """
  125. from akvo.tressel import pca
  126. pylab.figure()
  127. pylab.plot(rx0)
  128. pylab.plot(rx1)
  129. # PCA decomposition on ref channels so signals are less related
  130. transMatrix, K, means = pca.pca( np.array([rx0, rx1]))
  131. rx0 = transMatrix[:,0]
  132. rx1 = transMatrix[:,1]
  133. pylab.plot(rx0)
  134. pylab.plot(rx1)
  135. pylab.show()
  136. exit()
  137. if np.shape(x) != np.shape(rx0) or np.shape(x) != np.shape(rx1):
  138. print ("Error, non aligned")
  139. exit(1)
  140. wx = fft.rfft(x)
  141. wr0 = fft.rfft(rx0)
  142. wr1 = fft.rfft(rx1)
  143. H = np.zeros( (2*M), dtype=complex )
  144. ident_mat = np.eye((2*M))
  145. Rn = ident_mat / 0.1
  146. r_v = np.zeros( (M), dtype=complex )
  147. r_h = np.zeros( (M), dtype=complex )
  148. e = np.zeros(len(x), dtype=complex )
  149. ilambda = lambda2**-1
  150. for z in range(0, len(wx)):
  151. # TODO Padd with Zeros or truncate if M >,< arrays
  152. r_v = wr0[::-1][:M]
  153. r_h = wr1[::-1][:M]
  154. r_n = np.concatenate((r_v, r_h ))
  155. K = np.dot(Rn, r_n) / (lambda2 + np.dot(np.dot(r_n.T, Rn), r_n)) # Create/update K
  156. e[z] = wx[z] - np.dot(r_n,H) # e is the filtered signal, input - r(n) * Filter Coefs
  157. H += K * e[z]; # Update Filter Coefficients
  158. Rn = ilambda*Rn - ilambda*K*r_n.T*Rn # Update R(n)
  159. return fft.irfft(e)
  160. def iter_filt_refFreq(self, x, rx0, Ahat=.05, Bhat=.5, k=0.05):
  161. X = np.fft.rfft(x)
  162. X0 = np.copy(X)
  163. RX0 = np.fft.rfft(rx0)
  164. # step 0
  165. Abs2HW = []
  166. alphai = k * (np.abs(Ahat)**2 / np.abs(Bhat)**2)
  167. betai = k * (1. / (np.abs(Bhat)**2) )
  168. Hw = ((1.+alphai) * np.abs(X)**2 ) / (np.abs(X)**2 + betai*(np.abs(RX0)**2))
  169. H = np.abs(Hw)**2
  170. pylab.ion()
  171. pylab.figure()
  172. for i in range(10):
  173. #print "alphai", alphai
  174. #print "betai", betai
  175. #print "Hw", Hw
  176. alphai = k * (np.abs(Ahat)**2 / np.abs(Bhat)**2) * np.product(H, axis=0)
  177. betai = k * (1. / np.abs(Bhat)**2) * np.product(H, axis=0)
  178. # update signal
  179. Hw = ((1.+alphai) * np.abs(X)**2) / (np.abs(X)**2 + betai*np.abs(RX0)**2)
  180. Hw = np.nan_to_num(Hw)
  181. X *= Hw
  182. H = np.vstack( (H, np.abs(Hw)**2) )
  183. #print "Hw", Hw
  184. pylab.cla()
  185. pylab.plot(Hw)
  186. #pylab.plot(np.abs(X))
  187. #pylab.plot(np.abs(RX0))
  188. pylab.draw()
  189. raw_input("wait")
  190. pylab.cla()
  191. pylab.ioff()
  192. #return np.fft.irfft(X0-X)
  193. return np.fft.irfft(X)
  194. def iter_filt_refFreq(self, x, rx0, rx1, Ahat=.1, Bhat=1., k=0.001):
  195. X = np.fft.rfft(x)
  196. X0 = np.copy(X)
  197. RX0 = np.fft.rfft(rx0)
  198. RX1 = np.fft.rfft(rx1)
  199. # step 0
  200. alphai = k * (np.abs(Ahat)**2 / np.abs(Bhat)**2)
  201. betai = k * (1. / (np.abs(Bhat)**2) )
  202. #Hw = ((1.+alphai) * np.abs(X)**2 ) / (np.abs(X)**2 + betai*(np.abs(RX0)**2))
  203. H = np.ones(len(X)) # abs(Hw)**2
  204. #pylab.ion()
  205. #pylab.figure(334)
  206. for i in range(1000):
  207. #print "alphai", alphai
  208. #print "betai", betai
  209. #print "Hw", Hw
  210. alphai = k * (np.abs(Ahat)**2 / np.abs(Bhat)**2) * np.product(H, axis=0)
  211. betai = k * (1. / np.abs(Bhat)**2) * np.product(H, axis=0)
  212. # update signal
  213. Hw = ((1.+alphai) * np.abs(X)**2) / (np.abs(X)**2 + betai*np.abs(RX0)**2)
  214. Hw = np.nan_to_num(Hw)
  215. X *= Hw #.conjugate
  216. #H = np.vstack((H, np.abs(Hw)**2) )
  217. H = np.vstack((H, np.abs(Hw)) )
  218. #print "Hw", Hw
  219. #pylab.cla()
  220. #pylab.plot(Hw)
  221. #pylab.plot(np.abs(X))
  222. #pylab.plot(np.abs(RX0))
  223. #pylab.draw()
  224. #raw_input("wait")
  225. #pylab.cla()
  226. #pylab.ioff()
  227. return np.fft.irfft(X0-X)
  228. #return np.fft.irfft(X)
  229. def Tdomain_DFT(self, desired, input, S):
  230. """ Lifted from Adaptive filtering toolbox. Modefied to accept more than one input
  231. vector
  232. """
  233. # Initialisation Procedure
  234. nCoefficients = S["filterOrderNo"]/2+1
  235. nIterations = len(desired)
  236. # Pre Allocations
  237. errorVector = np.zeros(nIterations, dtype='complex')
  238. outputVector = np.zeros(nIterations, dtype='complex')
  239. # Initial State
  240. coefficientVectorDFT = np.fft.rfft(S["initialCoefficients"])/np.sqrt(float(nCoefficients))
  241. desiredDFT = np.fft.rfft(desired)
  242. powerVector = S["initialPower"]*np.ones(nCoefficients)
  243. # Improve source code regularity, pad with zeros
  244. # TODO, confirm zeros(nCoeffics) not nCoeffics-1
  245. prefixedInput = np.concatenate([np.zeros(nCoefficients-1), np.array(input)])
  246. # Body
  247. pylab.ion()
  248. pylab.figure(11)
  249. for it in range(nIterations): # = 1:nIterations,
  250. regressorDFT = np.fft.rfft(prefixedInput[it:it+nCoefficients][::-1]) /\
  251. np.sqrt(float(nCoefficients))
  252. # Summing two column vectors
  253. powerVector = S["alpha"] * (regressorDFT*np.conjugate(regressorDFT)) + \
  254. (1.-S["alpha"])*(powerVector)
  255. pylab.cla()
  256. #pylab.plot(prefixedInput[::-1], 'b')
  257. #pylab.plot(prefixedInput[it:it+nCoefficients][::-1], 'g', linewidth=3)
  258. #pylab.plot(regressorDFT.real)
  259. #pylab.plot(regressorDFT.imag)
  260. pylab.plot(powerVector.real)
  261. pylab.plot(powerVector.imag)
  262. #pylab.plot(outputVector)
  263. #pylab.plot(errorVector.real)
  264. #pylab.plot(errorVector.imag)
  265. pylab.draw()
  266. #raw_input("wait")
  267. outputVector[it] = np.dot(coefficientVectorDFT.T, regressorDFT)
  268. #errorVector[it] = desired[it] - outputVector[it]
  269. errorVector[it] = desiredDFT[it] - outputVector[it]
  270. #print errorVector[it], desired[it], outputVector[it]
  271. # Vectorized
  272. coefficientVectorDFT += (S["step"]*np.conjugate(errorVector[it])*regressorDFT) /\
  273. (S['gamma']+powerVector)
  274. return np.real(np.fft.irfft(errorVector))
  275. #coefficientVector = ifft(coefficientVectorDFT)*sqrt(nCoefficients);
  276. def Tdomain_DCT(self, desired, input, S):
  277. """ Lifted from Adaptive filtering toolbox. Modefied to accept more than one input
  278. vector. Uses cosine transform
  279. """
  280. from scipy.fftpack import dct
  281. # Initialisation Procedure
  282. nCoefficients = S["filterOrderNo"]+1
  283. nIterations = len(desired)
  284. # Pre Allocations
  285. errorVector = np.zeros(nIterations)
  286. outputVector = np.zeros(nIterations)
  287. # Initial State
  288. coefficientVectorDCT = dct(S["initialCoefficients"]) #/np.sqrt(float(nCoefficients))
  289. desiredDCT = dct(desired)
  290. powerVector = S["initialPower"]*np.ones(nCoefficients)
  291. # Improve source code regularity, pad with zeros
  292. prefixedInput = np.concatenate([np.zeros(nCoefficients-1), np.array(input)])
  293. # Body
  294. #pylab.figure(11)
  295. #pylab.ion()
  296. for it in range(0, nIterations): # = 1:nIterations,
  297. regressorDCT = dct(prefixedInput[it:it+nCoefficients][::-1], type=2)
  298. #regressorDCT = dct(prefixedInput[it+nCoefficients:it+nCoefficients*2+1])#[::-1])
  299. # Summing two column vectors
  300. powerVector = S["alpha"]*(regressorDCT) + (1.-S["alpha"])*(powerVector)
  301. #pylab.cla()
  302. #pylab.plot(powerVector)
  303. #pylab.draw()
  304. outputVector[it] = np.dot(coefficientVectorDCT.T, regressorDCT)
  305. #errorVector[it] = desired[it] - outputVector[it]
  306. errorVector[it] = desiredDCT[it] - outputVector[it]
  307. # Vectorized
  308. coefficientVectorDCT += (S["step"]*errorVector[it]*regressorDCT) #/\
  309. #(S['gamma']+powerVector)
  310. #pylab.plot(errorVector)
  311. #pylab.show()
  312. return dct(errorVector, type=3)
  313. #coefficientVector = ifft(coefficientVectorDCT)*sqrt(nCoefficients);
  314. def Tdomain_CORR(self, desired, input, S):
  315. from scipy.linalg import toeplitz
  316. from scipy.signal import correlate
  317. # Autocorrelation
  318. ac = np.correlate(input, input, mode='full')
  319. ac = ac[ac.size/2:]
  320. R = toeplitz(ac)
  321. # cross correllation
  322. r = np.correlate(desired, input, mode='full')
  323. r = r[r.size/2:]
  324. #r = np.correlate(desired, input, mode='valid')
  325. print ("R", np.shape(R))
  326. print ("r", np.shape(r))
  327. print ("solving")
  328. #H = np.linalg.solve(R,r)
  329. H = np.linalg.lstsq(R,r,rcond=.01)[0]
  330. #return desired - np.dot(H,input)
  331. print ("done solving")
  332. pylab.figure()
  333. pylab.plot(H)
  334. pylab.title("H")
  335. #return desired - np.convolve(H, input, mode='valid')
  336. #return desired - np.convolve(H, input, mode='same')
  337. #return np.convolve(H, input, mode='same')
  338. return desired - np.dot(toeplitz(H), input)
  339. #return np.dot(R, H)
  340. # T = toeplitz(input)
  341. # print "shapes", np.shape(T), np.shape(desired)
  342. # h = np.linalg.lstsq(T, desired)[0]
  343. # print "shapes", np.shape(h), np.shape(input)
  344. # #return np.convolve(h, input, mode='same')
  345. # return desired - np.dot(T,h)
  346. def Fdomain_CORR(self, desired, input, dt, freq):
  347. from scipy.linalg import toeplitz
  348. # Fourier domain
  349. Input = np.fft.rfft(input)
  350. Desired = np.fft.rfft(desired)
  351. T = toeplitz(Input)
  352. #H = np.linalg.solve(T, Desired)
  353. H = np.linalg.lstsq(T, Desired)[0]
  354. # ac = np.correlate(Input, Input, mode='full')
  355. # ac = ac[ac.size/2:]
  356. # R = toeplitz(ac)
  357. #
  358. # r = np.correlate(Desired, Input, mode='full')
  359. # r = r[r.size/2:]
  360. #
  361. # #r = np.correlate(desired, input, mode='valid')
  362. # print "R", np.shape(R)
  363. # print "r", np.shape(r)
  364. # print "solving"
  365. # H = np.linalg.solve(R,r)
  366. # #H = np.linalg.lstsq(R,r)
  367. # #return desired - np.dot(H,input)
  368. # print "done solving"
  369. pylab.figure()
  370. pylab.plot(H.real)
  371. pylab.plot(H.imag)
  372. pylab.plot(Input.real)
  373. pylab.plot(Input.imag)
  374. pylab.plot(Desired.real)
  375. pylab.plot(Desired.imag)
  376. pylab.legend(["hr","hi","ir","ii","dr","di"])
  377. pylab.title("H")
  378. #return desired - np.fft.irfft(Input*H)
  379. return np.fft.irfft(H*Input)
  380. def Tdomain_RLS(self, desired, input, S):
  381. """
  382. A DFT is first performed on the data. Than a RLS algorithm is carried out
  383. for noise cancellation. Related to the RLS_Alt Algoritm 5.3 in Diniz book.
  384. The desired and input signals are assummed to be real time series data.
  385. """
  386. # Transform data into frequency domain
  387. Input = np.fft.rfft(input)
  388. Desired = np.fft.rfft(desired)
  389. # Initialisation Procedure
  390. nCoefficients = S["filterOrderNo"]+1
  391. nIterations = len(Desired)
  392. # Pre Allocations
  393. errorVector = np.zeros(nIterations, dtype="complex")
  394. outputVector = np.zeros(nIterations, dtype="complex")
  395. errorVectorPost = np.zeros(nIterations, dtype="complex")
  396. outputVectorPost = np.zeros(nIterations, dtype="complex")
  397. coefficientVector = np.zeros( (nCoefficients, nIterations+1), dtype="complex" )
  398. # Initial State
  399. coefficientVector[:,1] = S["initialCoefficients"]
  400. S_d = S["delta"]*np.eye(nCoefficients)
  401. # Improve source code regularity, pad with zeros
  402. prefixedInput = np.concatenate([np.zeros(nCoefficients-1, dtype="complex"),
  403. np.array(Input)])
  404. invLambda = 1./S["lambda"]
  405. # Body
  406. pylab.ion()
  407. pylab.figure(11)
  408. for it in range(nIterations):
  409. regressor = prefixedInput[it:it+nCoefficients][::-1]
  410. # a priori estimated output
  411. outputVector[it] = np.dot(coefficientVector[:,it].T, regressor)
  412. # a priori error
  413. errorVector[it] = Desired[it] - outputVector[it]
  414. psi = np.dot(S_d, regressor)
  415. if np.isnan(psi).any():
  416. print ("psi", psi)
  417. exit(1)
  418. pylab.cla()
  419. #pylab.plot(psi)
  420. pylab.plot(regressor.real)
  421. pylab.plot(regressor.imag)
  422. pylab.plot(coefficientVector[:,it].real)
  423. pylab.plot(coefficientVector[:,it].imag)
  424. pylab.legend(["rr","ri", "cr", "ci"])
  425. pylab.draw()
  426. raw_input("paws")
  427. S_d = invLambda * (S_d - np.dot(psi, psi.T) /\
  428. S["lambda"] + np.dot(psi.T, regressor))
  429. coefficientVector[:,it+1] = coefficientVector[:,it] + \
  430. np.conjugate(errorVector[it])*np.dot(S_d, regressor)
  431. # A posteriori estimated output
  432. outputVectorPost[it] = np.dot(coefficientVector[:,it+1].T, regressor)
  433. # A posteriori error
  434. errorVectorPost[it] = Desired[it] - outputVectorPost[it]
  435. errorVectorPost = np.nan_to_num(errorVectorPost)
  436. pylab.figure(11)
  437. print (np.shape(errorVectorPost))
  438. pylab.plot(errorVectorPost.real)
  439. pylab.plot(errorVectorPost.imag)
  440. pylab.show()
  441. print(errorVectorPost)
  442. #return np.fft.irfft(Desired)
  443. return np.fft.irfft(errorVectorPost)
  444. if __name__ == "__main__":
  445. def noise(nu, t, phi):
  446. return np.sin(nu*2.*np.pi*t + phi)
  447. import matplotlib.pyplot as plt
  448. print("Test driver for adaptive filtering")
  449. Filt = AdaptiveFilter(.1)
  450. t = np.arange(0, .5, 1e-4)
  451. omega = 2000 * 2.*np.pi
  452. T2 = .100
  453. n1 = noise(60, t, .2 )
  454. n2 = noise(61, t, .514 )
  455. x = np.sin(omega*t)* np.exp(-t/T2) + 2.3*noise(60, t, .34) + 1.783*noise(31, t, 2.1)
  456. e = Filt.adapt_filt_tworef(x, n1, n2, 200, .98)
  457. plt.plot(t, x)
  458. plt.plot(t, n1)
  459. plt.plot(t, n2)
  460. plt.plot(t, e)
  461. plt.show()