# Wavemeter based on interferometer

This project aims to build a wavemeter which can be used to measure laser in our lab precisely.

Team: Qin Qichen, Zhao Qi, Zhang Zhao.

## Introduction

There are many methods to measure the wavelength of laser. One easy idea is to use an interferometer. The fringes of interference is associated with wavelength of laser. So by comparing it with the result of a reference laser the wavelength can be measured. Now there is something wrong with the wavemeter in our lab. Hence, we aim to build a wavemeter to measure laser in our lab. The range of wavelength our wavemeter can detect aim to be around 1100nm - 1700nm, determined by optics. A precision around 100MHz is expected.

## Theory

Michelson interferometer is a good tool to measure the length if there is a good laser. Similarly, it can also be used to measure the unknown wavelength of a laser by calculating the ratio of wavelengths between the unknown and a known laser. Here we use a 1550nm laser as our reference. By counting zeros in the signal, we can get the ratio between wavelengths.

In Michelson interferometer, an input beam is splitted into two and reflected back by their respective mirror. The two beams with a phase difference caused by travelling different optical path lengths would form an interference pattern after overlapping. If we change the optical path length difference of the two beams, i.e. change the length of one arm by moving the mirror, the interference pattern would change simultaneously.

Consider the planar wave case, the two reflected beams could be written as $E_{1}={\frac {A_{0}}{2}}e^{i(\omega t+knx_{0})}$ and $E_{2}={\frac {A_{0}}{2}}e^{i(\omega t+knx_{0}-2knx(t))}$ , where $A_{0}$ is the amplitude of the input beam, $\omega$ is the angular frequency, $k$ is the wavenumber, $n$ is the refractive index of air, $x_{0}$ is the length of one constant arm and $x(t)$ is the length difference changing with time of the other arm.

Then the total electrical field at the output would be

$E={\frac {A_{0}}{2}}e^{i(\omega t+knx_{0})}(1+e^{-2iknx(t)})$ The intensity would thus be

$I={\frac {1}{2}}\epsilon EE^{*}={\frac {\epsilon A_{0}^{2}}{2}}(1+cos(2knx(t))$ For $x(t)=x$ , the number of zero-crossings of the interference pattern would be $N={\frac {2knx}{\pi }}$ . According to this equation, we can relate the ratio of wavelengths of two lasers $\lambda _{1}$ and $\lambda _{2}$ with their number of zero-crossings as

${\frac {N_{1}}{N_{2}}}={\frac {\frac {2k_{1}nx}{\pi }}{\frac {2k_{2}nx}{\pi }}}={\frac {k_{1}}{k_{2}}}={\frac {\lambda _{2}}{\lambda _{1}}}$ .


## Components

### Optics

List of all the optic components used
BB1-E04 mirror x2 (for 1550nm reference laser)
50/50 Beamsplitter 1100nm-1600nm x1
C220-c Aspherical lens x2 (for fiber input of two lasers)
TS975M-M01B Retroreflector x2
f50mm lens x1 (for unknown-laser detector)
f35mm lens x1 (for 1550nm detector)
PDA10CS-EC detector x1
PDA50B-EC detector x1

### Air track

The whole setup including air track and optics is built on a 750mmx750mm breadboard table. The aluminum linear stage is 550mmx50mm with a 150mmx50mm sledge sliding on its V-shape surface. The friction is reduced by producing an air cushion between the two surfaces using 10 small holes on the bottom of sledge that is provided with compressed air via a silicon tube. Two springs are attached to both ends of the sledge and fixed around two long screws. Besides the force provided by springs when reflected from ends, the kinetic energy loss is also compensated by a small push pull solenoid fixed on one end of the stage. Two optical switches are mounted on board to detect the signal when sledge reaches ends by a piece of paper fixed on sledge, controlling the solenoid.

### Signal Process

The circuit for processing signal. The signals generated by photodetectors are small and dc biased. To enable our counter, we need to transfer the signal to a 0-5V signal. We first use a high pass filter to get rid of the dc part. Then a monolithic amplifier is used to amplify the ac signal before we put it into our comparator. The comparator is used to generate the 5V signal. The output port of LM339AN can be seen as a transistor so use a pullup resistor connected to 5V we can accomplish it.

Comment: Congratulation, obviously it worked ;) However, the MAR amplifiers would need a resistor at the positive supply feeding in DC current for them to actually amplify the signal. Maybe you did not need it in the case here... Also, keep in mind these amplifiers are internally terminated with 50 Ohm to ground: The additional resistors with 150 Ohm at their input only lowers the total input impedance to 1/(1/50+1/150)=37.5Ohm, so you likely have a higher cutoff frequency than what you thought. But hey, it worked!

Components we use
2 Resistors for high pass filter $150\Omega$ 2 Capacitors for high pass filter $0.1\mu F$ 2 Resistors for low pass filter $3.3k\Omega$ 2 Capacitors for low pass filter $1\mu F$ 2 Monolithic Amplifiers MAR-3SM+
1 Voltage Regulator L7805SCV
2 Potentiometer

### Control Circuit

The schematic of the circuit for control is shown below. Arduino UNO board is used to control and count signals and should be connected to a computer to power it and get reading. Two optical switches are used to get rid of signal change when car gets pushed back. The optical switch 0 is also used to turn on/off solenoid. A push pull solenoid is used to push the car and compensate for energy loss in motion.

Components we use
2 Optical switches OPB881T55Z
1 Push-Pull solenoid TAU0730TM-14
2 Optical detectors PDA10CS-EC and PDA50B-EC
2 Resistors for photodiode $240\Omega$ 2 Resistors for phototransistor $6.7k\Omega$ 1 Resistor for transistor base $1k\Omega$ 1 Transistor 2N2222A
1 diode to protect 1N4007G

## Program

The main program consists of two parts.

One is to control the car. When the car passes across the optical switch 0 and move to the edge, the program will receive a signal change from OS0 and then turn on the solenoid. Then the small solenoid will be driven and push the car. So the position of optical switch 0 should be suitable because if the solenoid is on when the car moves towards the edge, the car will be decelerated. The right timing is to push the car when the spring is compressed to the minimum length. This can be done by changing the position of OS0.

The other part is two counters, for reference and measured laser. We make use of Arduino UNO to do this. There are three timers inside Atmega328P(this chip is the core controller of Arduino UNO). Timer0 and Timer2 are 8bit timer and Timer1 is a 16bit timer. Normally when you enable the timer, it will use the internal 16MHz clock and count the clock signal. For example, if you set no prescale(which means the clock frequency is 16/1 = 16Mhz), the timer register will increase every 1/16M second. Then it will be cleared when its value overflows. For 8 bit timer the range of register is from 0 to 255. Then we use these timers to count the rising in the interferometer signal. As tested before, it should be noticed the frequency should be no more than 3MHz and the transition must cross 2.5V and Vpp must be larger than 1V. Otherwise, the timer will lose signals.

As shown here, the initial state of the wavemeter should be like this, the car should be between two optical switches. That's because the setting in code. The circuit does not know where the car is and it only knows whether the car passes across optical switches. So I set the initial state variable as 1 which means the car is between two optical switches and the counters are working. The program will pause the counter by cutting off power for counters when the car passes across optical switches. Because we want to get rid of the signal when the car changes its direction.

## Result

Here we use 1550nm laser as our reference, 701nm laser as a measured laser to test the wavemeter. We need to clarify here that these two lasers are available in our lab now but not locked. So we do not know the real number of the wavelength. There are two methods to count fringes. One way is to use an oscilloscope to save fringes and process data on PC. The other way is to use our Arduino board to count and send counts to the computer automatically. Here we first use an oscilloscope to test our setup. Our result shows the ratios from two methods are in accordance, which shows the counter we made works at least.

### Signal

Here is the signal from detector directly.

As said in electronic section, we use a circuit to amplify and reshape the signal. After processing, the signal is shown below:

### Oscilloscope

Here we save data from an oscilloscope(the signal from detector). Because the sampling time is much shorter than the period of car moving, we can treat the frequency of sine signal unchanged. We can fit the line and get the frequency of signal directly. Then, the ratio gives us the ratio of laser frequency. In a 100 $\mu s$ time duration, two beat signals are sine fitted and the ratio of resulted frequency is the ratio of two lasers' frequency.

${\frac {1550}{701}}=2.2111$ and from fitted line we get ratio ${\frac {4.698}{2.12}}=2.2160$ Comment: Whenever possible, try to specify an uncertainty of your measurement result. In this method of determining the wavelength, it would likely be the uncertainty of +/-1 in the zero transitions during a fixed periode of the test beam. Not sure how your code exactly works, but you may or may not have to consider an uncertainty of +/-1 in the reference counts as well. The latter uncertainty you could remove if you count a fixed number of reference events, and use it to control a gate window for the test counts....

### Arduino counter

For Arduino counter, because we do not limit the start point of counting(do not make sure two signals have the same phase), counts may be different. There is also fluctuation in counts. So we read 20000 samples and plot the histogram here.

The average result shows a ratio of wavelength $2.2157$ , the same as the result from the fitting method.

## Future work

There exist several problems to be solved here.

First, the wavelength of our laser is unknown. So we can only get a ratio and can not get an absolute value. We plan to use a 804nm laser as reference in the future because this laser is locked and we can read the wavelength from the wavemeter in our lab.

Second, in the result from Arduino board, the result shows discrete distribution. The space between boxes is limited by max counts set in Arduino code. If we want to get a higher resolution, we need higher max counts, which means a longer sampling time.

Third, It is necessary to write an interface window to make this system easier to use. This window should allow people choose max counts and average numbers.

Our project gives a preliminary result here. It can be improved by solving these problems.

## History

### Mar 9

We find the optics for our wavemeter and order the retroreflectors. The design files of our air track and car are sent to Bob.

We test our pull-push solenoids and optical switch. We aim to use Arduino UNO as our controller. Today we test using an optical switch to generate the voltage signal.

### Mar 16

Arduino UNO is tested to do the counter. We try to use the simple functions provided by Arduino compiler environment to do this. The idea is to detect the rising of the signal. So I make the comparison between two samplings with a given time interval to see if there is a rising. The problem is this method is too slow. The function provided by the compiler is actually a bunch of pieces of the atomical operations. The time interval set by function is also limited by an internal timer of the board.

There are two types of timers in this Arduino UNO board, which are actually provided by the microcontroller chip Atmega328P. The chip has an internal clock with 16MHz frequency. Theoretically, our signal is around 1MHz if assuming the speed of our car is around 1m/s. So it seems feasible if we use the internal clock to do this counter. Need to read documents and search to see how other people do this.

### Mar 29

Sampling and comparing the signal is not good enough to do the counter. This method is useful only for very slow signals such as 1kHz. If the frequency of our signal is 1MHz, if we want to do the counting by comparing the sampling, our sampling should be at least 2MHz to capture all rising. But when we compare the executing of those codes can waste a lot of time, then we will miss a lot of rising signal. So it is not a good idea.

I check some useful documents. It seems use port manipulation will minimize the time used to do the read and write. I can also use the interrupt which is associated with a timer to do some tasks. A good instruction about this can be seen on the sites below.

### Apr 5

Bob finished the mechanical work. We get our air track and car. We are beginning to set up the air track.

### Apr 6

We use compressed air in our lab as our air source. we put the car on the air track. But their are two problems here.

First, the air port of the car is on one side so the pipe may touch some optics. So we need an L shape adapter to make the pipe vertical.

Then, the pipe connected to the car is hard, which means when we move the car it will be forced by the pipe. We need to find a softer pipe,

Murray suggests using a 90 curved copper pipe to connect the car.

### Apr 7

Tried the method in https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/. Use the signal to trigger the interrupt. I use two signals from the function generator to test. The result is it can detect a signal no more than 100kHz. So this method is not good and we still need to find a way to do the counting.

Make one 90 curved copper pipe and try it. But it is so heavy that the car tilts and touches one side of the air track. It is better not to use this. We find a curved plastic pipe which is better to do this. We also borrow a soft pipe and the compression spring from Kai's Lab(the spring we bought before is too hard, the k is too high).

### Apr 8

The clock frequency is designed to be 16MHz so theoretically signal with a lower frequency than 16MHz can all be counted. It also reminds me that the timer inside the chip can be chosen as the counter. Choosing the timer inside needs an understanding of the register inside the microcontroller. Basically, registers can store some bits and the value of these bits can determine how the chip works. Details about this can be found in https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf

Use this method as our counter. It is found the board can work pretty well when we use two MHz signals as input from a function generator. Now the control part can be added in.

### Apr 9

The picture shows the basic idea about our circuit.

Two optical switches and one solenoid are connected to our Arduino UNO board. We want to make sure the board stops counting when the car moves to the edge. So the optical switch 1 is connected to the INT1 pin of Atmega328p which is used to request an interrupt to pause counting when the signal changes. The optical switch 0 is connected to INT0 and can also pause counting. INT0 is also used to control solenoid. This pull push solenoid is on when the car reaches the edge and pushes the car back. We use a simple transistor to control it.

The result shows our board can count the risings in signal and when one counting reaches a maximum value we set before it will show and then clear the counting.

Use two signals from a function generator as input. One is 1.2MHz and the other one is 1MHz. The counting from our board is 5120000 and 4266671. The ratio is 1.19999878125. The maximum value of counting, 5120000, can be changed to get different precision.

### Apr 12

Test the circuit which controls the car. Change the position of optical switch 0 a little to make the car keep moving. The result shows the car can move at least several hours if the air pressure is suitable.

### Apr 14

Use a PCB board found in the workshop and make a board instead of using the breadboard in case someone messes up our circuit.

### Apr 16

Finish setting up for optics preliminarily. Now we use 701 laser to test our wavemeter.

### Apr 19

Check the signal from detectors. The signal from 1550nm reference laser is not stable while the signal from 701nm laser is stable. That is because we set up 1550 first and now the optics is optimized for 701 laser. When the car moves, the reference signal shows an oscillating. It is also a problem that the signals are too small for our counter to work. We need to process the signal first.

### Apr 21

Because the interference is not ideal so there is a DC voltage in our signal. We test a high pass filter to do this.

### Apr 23

Make a board to process the signal. The board is designed to filter dc signal and amplified the sine signal. The output is designed to be a 0-5V square wave which is more suitable for our counters.

### Apr 26

Test the wavemeter today. The circuit and counter work well but the result is wrong. The problem is from 1550 reference laser. Because this signal is not stable. When the signal is very small, the counter will miss counts. Then the counts for 1550 will be smaller than the actual number which means our result will be smaller than the real value. So need to check our setup.

### Apr 28

Check the setup and set smaller max counts. Now the board can work well.

## Code

Here is the code for Arduino IDE.

//these two lines are used to disable the timer0 defination used in Arduino board. wiring.c should be changed.
//signal <3MHz 2.5V crossing Vpp>1V
#define _DISABLE_ARDUINO_TIMER0_INTERRUPT_HANDLER_
#include <wiring.c>

volatile int counter_0_ovf = 0; //use timer 0 8bit count 256*ovf     pin4 detector 0
volatile int counter_1_ovf = 0; //use timer 1 16bit count 65536*ovf  pin5 detector 1
volatile long counter0_ovf_result = 0;//store ovf value
volatile long counter1_ovf_result = 0;//store ovf value
volatile int counter0 = 0;//timer0 value
volatile int counter1 = 0;//timer1 value

bool car_state = true; //state of car, initial value true
bool counter0_state = false;//state flag for counter0, initial value false
bool counter1_state = false;//state flag for counter1, initial value false

int state = 0;

long MaxCount0_ovf = 10240; //define max ovf value for counter0
long MaxCount1_ovf = MaxCount0_ovf*256/65536; //define max ovf value for counter1

void counter_reset(){
//clear counter
TCNT0 = 0;
TCNT1 = 0;
//clear ovf_value
counter_0_ovf = 0;
counter_1_ovf = 0;
}

void setup() {
//initila serial setting
Serial.begin(9600);

cli();//disable all interrupt

//enable timer 0 and timer 1 by clearing PRTIM
PRR&=~((1<<PRTIM0)|(1<<PRTIM1));

//set timer 0
TCCR0B|=(1<<CS00)|(1<<CS01)|(1<<CS02); //External clock source on T0 pin4. Clock on rising edge.
//clear WGM to set normal mode
TCCR0A&=~((1<<WGM01)|(1<<WGM00));
TCCR0B&=~(1<<WGM02);
TIMSK0|= (1<<TOIE0); //overflow interrupt is enabled

//set timer 1
TCCR1B|=(1<<CS10)|(1<<CS11)|(1<<CS12); //External clock source on T1 pin5. Clock on rising edge.
//clear WGM to set normal mode
TCCR1A&=~((1<<WGM11)|(1<<WGM10));
TCCR1B&=~((1<<WGM13)|(1<<WGM12));
TIMSK1|= (1<<TOIE1); //overflow interrupt is enabled

//set external interrupt for coil control and pause counting, signal source: optical switch0. use INT0
EIMSK &=~ (1<<INT0);//disable
EICRA |= (1<<ISC00)|(1<<ISC01);//The rising edge of INT0 generates an interrupt request
EIFR |= (1<<INTF0);//clear the flag
EIMSK |= (1<<INT0);//enable

//set external interrupt pause counting, signal source: optical switch1. use INT1
EIMSK &=~ (1<<INT1);//disable
EICRA |= (1<<ISC10)|(1<<ISC11);//The rising edge of INT1 generates an interrupt request
EIFR |= (1<<INTF1);//clear the flag
EIMSK |= (1<<INT1);//enable

//initial the coil(default: off)
DDRB |= (1<<DDB0);//pin set as output to control coil
PORTB&=~(1<<PORTB0);//off

sei();//enable interrupt
}

ISR(INT0_vect){
//toggle the state of car
car_state = !car_state;
//turn on/off the counter
PRR^=((1<<PRTIM0)|(1<<PRTIM1));
//toggle the coil
PORTB^=(1<<PINB0);
}

ISR(INT1_vect){
//toggle the state of car
car_state = !car_state;
//toggle the switch to control counter
PRR^=((1<<PRTIM0)|(1<<PRTIM1));
}

ISR(TIMER0_OVF_vect){
counter_0_ovf++;
if (counter_0_ovf<MaxCount0_ovf){
}
else{
counter0_ovf_result = counter_0_ovf;
counter1_ovf_result = counter_1_ovf;
counter0 = 0;
counter1 = TCNT1;//restore counter1 value
counter_reset();
}
}

ISR(TIMER1_OVF_vect){
counter_1_ovf++;
if (counter_1_ovf<MaxCount1_ovf){
}
else{
counter1_ovf_result = counter_1_ovf;
counter0_ovf_result = counter_0_ovf;
counter1 = 0;
counter0 = TCNT0;//restore counter0 value
counter_reset();
}
}

void loop() {
state = (car_state&&(counter0_state||counter1_state));
switch(state){
case 0:
break;
case 1:
//send counts to computer
Serial.println("counts pin, pin4 and pin5");
Serial.println((counter0_ovf_result)*256+counter0);
Serial.println((counter1_ovf_result)*65536+counter1);
//clear state in case the board repeats output
counter0_state = false;
counter1_state = false;

break;
}
}


Here is the jupyter notebook Python2 code for getting result:



import serial
import numpy as np
from matplotlib import pyplot as plt

arduino = serial.Serial('COM5', 9600, timeout=.1)

def group_sampling(lines):
counter0=[]
counter1=[]
i = 0
while i+2 < len(lines):
if lines[i] == 'counts pin, pin4 and pin5':
counter0.append(float(lines[i+1]))
counter1.append(float(lines[i+2]))
i += 1
return [np.array(counter0),np.array(counter1)]

w0 = 1550e-9 #wavelength of reference laser

Maxsampling = 20000 #groups of sampling
lines=[]
i = 0
for i in list(range(3*Maxsampling)):

[counter0,counter1] = group_sampling(lines)

ratio = counter0/counter1
ratio.sort()
ratio = ratio[10000:-1000]

result = w0*ratio
f = 299792458/result/1e6

sample = result

fig, ax = plt.subplots()

num_bins = 50

# the histogram of the data
n, bins, patches = ax.hist(sample, num_bins, density = True)

# add a 'best fit' line
#y = ((1 / (np.sqrt(2 * np.pi) * sigma)) *
#     np.exp(-0.5 * (1 / sigma * (bins - mu))**2))
#ax.plot(bins, y, '--')
ax.set_xlabel('Wavelength')
ax.set_ylabel('Probability density')
ax.set_title(r'Histogram of sampling: $\mu={:.2f}$, $\sigma={:.2f}$'.format(np.mean(sample),np.std(sample)))

# Tweak spacing to prevent clipping of ylabel
fig.tight_layout()
plt.show()