Lab 4: Signal and Noise

PHYS 37100 - Fall 2021

Due: November 29th, 4 pm

Goal: Use some hardware based averaging to detect an artificial signal that you can't see but you know should be there. This is a standard part of experimental design. Inject a fake 'signal' into your experiment before you try to measure nature to make sure your experiment can actually detect what you are tying to detect.

Introductory Bits

The basic idea for this lab is to create an artifical "signal" and then see what we can do experimentally to make it easier or harder to detect. The fake signal will be an oscillating brightness of an LED. This can be accomplished by using the PWM output and changing the brightness value like so: \begin{equation} f(t) = A+B\sin(\omega t) \end{equation} where $A$ is the baseline brightness, and the oscillating term is added to it. $B$ will indicate the strength of the added signal and the frequency is determined by the argument of the $\sin()$ function.

The following simple sketch will create an oscillating ($f = 1 \; \textrm{Hz}$) brightness on the LED. Try it out before going any further. (Don't forget to include a 220 $\Omega$ resister in series with your LED!)

  const int ledPin = 9;       // pin that the LED is attached to
  const int TWOPI = 2*3.1415926;

  int brightness;

  void setup() {
    pinMode(ledPin, OUTPUT);

  void loop() {
    brightness = 100+50*sin(1.0*TWOPI*millis()/1000);

Next, set up a voltage divider circuit with the PhotoResistor so that you can measure the brightness of the LED.

You should be able to reporoduce this effect, shown in the animation. The LED is changing brightness periodically. Play with the parameters of the $\sin()$ function to make sure you understand what's going on there.

A basic Python script to view your data would look like this:

import pandas as pd
import matplotlib.pyplot as plt

path = "/content/oscillating-brightness.csv"
thedata = pd.read_csv(path, names = ['time','brightness'], skiprows=10)
thedata["time_seconds"] = .001 * thedata["time"]

ax = thedata.plot('time_seconds','brightness',xlabel = "Time [s]", ylabel="Brightness [AU]",style='r',label="Brightness")


And you should be able to obtain a plot like this of the 'faked' signal.

Make sure you can do all this before moving forward. (This is just the introduction - the actual experiment hasn't started.)

If you look closely at your data (try adding the line: ax.set_xlim([1,3]) right after your plot command to restrict the range of the horizontal axis), you should a not very pretty sin function. The amplitude is certainly oscillating, but there is a lot of 'noise'. This noise is an artifact arising from the fact that in addition to our added oscillations at frequence $f$, there is also a blinking occuring at a much faster frequency due to the PWM that controls the brightness. Remember, the LED is only changing it's apparent brightness to our eyes becuase the duty cycle is being changed.

You can review this concept here: if it's still unclear.

The experiment

  1. Impliment an averaging function into your sketch so that the data recorded is averaged over a short time. This will effectively 'smooth out' the noise. You can use this tutorial as a starting point: Arduino Smoothing
  2. You should be able to obtain a plot that shows a much smoother sinusoidal function.

  1. Now, Prepare three such plots, for three different averaging widths: 1, 5, and 10. (see bottom for python code to plot multiple time series)

  1. Now, go back to your 'fake' signal function:
        brightness = 100+50*sin(1.0*TWOPI*millis()/1000);
    The 50 is effectively the amplitude of the fake signal that we are trying to detect. Try lowering that to say, 5 or so, and adjust your averaging routine so that you can detect it (and look with your eyes at the LED - you probably can't detect any change in brightness visually). Perpare a plot of that very small signal. Here's a plot where the amplitude is just 2. Notice how we can see the resolution of the Analog-Digital converter here.

If we didn't include any averaging, then our small signal measurement would look like this. It's impossible to see the 1 Hertz oscillations now, since they are dominated by the PWM fluctuations that are occuring at much faster times scales.


If you want to dig a little deeper, then try playing with the Reference resistor of your voltage divider. In all the examples above, I chose a resister that is roughly the same as the PhotoResistor, about $10 \; \textrm{k}\Omega$. If you put a much smaller resister in there, like $100 \; \Omega$, then you will notice that the resolution of your measurement suffers.

Also, the above lab used what could be called hardware averaging, since we modified the measurement instrumentation to do the averaging. Another tactic can be software averaging which would involve something the noisy data using code, after the measurement is taken. This is just another part of the big toolbox of experimental physics, which will be explored further in PHYS 471.

Report Instructions:

Your report should be formatted in a single document (pdf) that contains the following:

  1. Introductory text describing the experiment
  2. Circuit Diagram of your LED & PhotoResistor measurement setup
  3. Photograph of your board in operation.
  4. Plots showing the oscillating LED brightness you detected.
  5. Plots showing the affects of averaging parameters.
  6. Plots showing the small signal you detected
  7. Discussion of the results
  8. Any references used
  9. Links to csv files online.
  10. Code for your sketch.

More Notes

For all the data recorded above, I recorded the serial out put for 10 seconds, then copied that data into a texteditor, cleaned it up a bit by deleating any rows after 10,000 milliseconds, and any thing in the beginning that wasn't 'good' data (which sometimes happens with the serial out put when restarting your arduino sketch), and then saved it as a CSV file and uploaded to the session storage of my colab notebook. At this point in the course, this process should be understood and not a mystery.

Example code for plotting multiple time series. It essentially loads three different datasets and then plots them all on the same axis. (not the best way, but perhaps the simplest. If you were doing this for 100 data sets, then you would certainly want to have a more automated script.)

    path1 = "/content/avg_5.csv"
    thedata1 = pd.read_csv(path1, names = ['time','brightness'], skiprows=10)
    thedata1["time_seconds"] = .001 * thedata1["time"]

    path2 = "/content/avg_10.csv"
    thedata2 = pd.read_csv(path2, names = ['time','brightness'], skiprows=10)
    thedata2["time_seconds"] = .001 * thedata2["time"]

    path3 = "/content/avg_20.csv"
    thedata3 = pd.read_csv(path3, names = ['time','brightness'], skiprows=10)
    thedata3["time_seconds"] = .001 * thedata3["time"]

    axx = thedata1.plot('time_seconds','brightness',label="AVG = 5")
    thedata2.plot('time_seconds','brightness',ax=axx,label="AVG = 10")
    thedata3.plot('time_seconds','brightness',ax=axx,label="AVG = 20")