Electronic engineers were proud to have helped their team to higher CDD resolution with a trick they published:
they would use a median filter to filter out the photon arriving from the CDD from the thermal noise. They were even very proud of seeing a very signal far smaller than the noise.
I thought to myself. Such a scam, this is to stupid it cannot work.
So I decided to play with matplotlib to test it.
First we generate white noise, then given a probability of 1/n a photon is seen, and its amplitude is 1/A of the signal.
Then, I used #python-fr to have advices on the best way to do a moving average (which is the usual filter used for filtering out signals), then I tried the median filter.
A median filter is just as silly as taking the median in a moving window on a time serie.
It cant work, can't it?
Well, it does.
Is it not hard to code, but beware your eyes will cry from PEP8 insanity and mapltotlib style mixed with pythonic style (call it art):
(https://gist.github.com/3794506)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# -*- coding: utf-8 -*- | |
import matplotlib.pyplot as plt | |
from scipy.signal import medfilt | |
import numpy as np | |
NOISE_AMPLITUDE = 4.0 | |
PERIOD = 200 | |
PRESENCE_PROBABILITY = .4 | |
OPT_MA_WINDOW = 75 | |
OPT_MEDIAN_WINDOW = 41 | |
# size of sample | |
S_O_S = 4000 | |
def canonical_ma( a_signal, size_of_window): | |
""" method used canoncically in signal processing to obtain | |
moving average the fastest way possible. | |
In plain english: it is the average of the nterms around a | |
point in a serie | |
""" | |
window = np.ones( size_of_window ) | |
return np.roll( | |
np.convolve( window / size_of_window , a_signal, 'valid' ) , | |
size_of_window / 2 | |
) | |
#contributed by generic_genus (reddit) | |
signal = np.where(np.arange(4000) % PERIOD > PERIOD / 2, 0.5, -0.5) | |
noise = np.random.uniform( | |
low=-0.5*NOISE_AMPLITUDE, | |
high=0.5*NOISE_AMPLITUDE, | |
size=S_O_S | |
) | |
noise = np.where( | |
np.random.uniform(size=len(noise)) < PRESENCE_PROBABILITY, | |
signal, noise | |
) | |
fig=plt.figure(figsize=(12,10)) | |
plt.suptitle( | |
"Square signal mixed in white noise filtering with P=%f proba and NS ratio=%f" % | |
( PRESENCE_PROBABILITY,NOISE_AMPLITUDE) | |
) | |
tint = 255 | |
ax=plt.subplot(211) | |
ax.plot( noise, color='blue', label="signal mixed in random noise") | |
# amer45 (reddit) made me realize I should use existing available best way to do it | |
ax.plot( | |
medfilt(noise,[OPT_MEDIAN_WINDOW]), | |
color ='#%x0000' % tint, label="median filter" | |
) | |
ax.plot( signal , color ='yellow', label="initial signal" ) | |
ax.legend() | |
ax=plt.subplot(212) | |
ax.plot( noise, color='blue', label="signal mixed in random noise") | |
ax.plot( signal , color ='yellow', label="initial signal" ) | |
ax.plot( | |
canonical_ma(noise,OPT_MA_WINDOW), | |
color ='#00%x00' % tint, label="moving average" | |
) | |
ax.legend() | |
plt.show() |
Moral of the story?
Well, I should have to publish the code that shows why 40 for median and 75 for movering average are the optimal settings, and how dazzling fast matplotlib is.
But the real point is python + matplotlib are amazing. And that sometimes simple ideas give amazing results.
Have fun!
No comments:
Post a Comment