Title

Name

Clear["Global`*"]; DateString[]
SetDirectory[NotebookDirectory[]];

Here is the model oscillation function we want to fit:

osc[V_, f_, ϕ_, Voff_, t_] := V  Sin[2 π f t - ϕ] + Voff

files = FileNames["TO-driven-*.csv"]

Look at the first few lines of  file number 1:

FilePrint[files[[1]], 3]

This function will read the input voltages:

getVin[filename_] := Select[Import[filename, "CSV"], Length[#] == 3 && VectorQ[#, NumberQ] &][[All, {1, 2}]]

Check the function by looking at the first few lines it returns for a typical file:

TableForm[
getVin[files[[1]]][[Range[3]]]]

This function will read the output voltages:

getVout[filename_] := Select[Import[filename, "CSV"], Length[#] == 3 && VectorQ[#, NumberQ] &][[All, {1, 3}]]

Make up a handy function to show the data.

showData[data_] := ListPlot[data, PlotStyle -> Red, LabelStyle->Larger, AxesLabel -> {"t (s)", "V (V)"}, ImageSize->Scaled[0.7]]

showData[getVout[files[[3]]]]

For the torsional oscillator, we want to find the amplitude  and phase for each of our  input and output voltages, for each of our trials.  To do the fits, it will be useful to use good guesses for the frequency and phase.  Again, try the commands one by one, and then bundle them all up in a handy function.

Make a list of the frequencies at which the function generator was set .  These will help constrain the fits.

fset = {0.40, 0.78, 0.82, 1.00}

Generate guesses at phases just by looking at the first few seconds of each plot.  For example, Vin for file 2 has a phase close to zero.  Vout for file 2 has a phase closer to π/2.

i =2;
showData[
getVout[files[[i]]][[Range[1500]]]
]

Make a table of phase guesses for all 4 runs by looking at all the  plots.  If the triggering in LoggerPro worked correctly, all the input phases should be close to 0.  You should also be able to guess the output phases based on your theoretical expectations.  These guesses don’t have to be precise, but it’s helpful if they are in the correct quadrant.

ϕinguesses = {0, 0, 0, 0};
ϕoutguesses = {0, π/2, π/2, π};

Generate a function to call NonlinearModelFit, using our initial guesses for the phase and frequency.  Require the amplitude to be positive.  It also turns out to be useful to constrain the frequency rather strictly.  (It should be very close to the set value on the function generator, but we can’t assume the function generator and LoggerPro agree exactly on timing.)   It is easy to guess the amplitude and offset, but that turns out not to make a difference.

fitV[data_, fset_, ϕguess_] := NonlinearModelFit[
data, 
{osc[V, f, ϕ, Voff, t], V > 0, fset - 0.01 <= f <= fset + 0.01},
{
V,
{f, fset},
 {ϕ,ϕguess},
 Voff
}, t]

Try it out on file #2:

i = 2;
vdata= getVout[files[[i]]];
vDataPlot = showData[vdata];
fit = fitV[vdata, fset[[i]], ϕoutguesses[[i]]]
fit["BestFitParameters"]
Show[vDataPlot, Plot[fit[t], {t, 0, 10}], PlotRange -> {{0, 10}, All}]

Once you are confident your fitting scheme is working, here is one way to make a table of all the fit results for the input voltages:

infits = Table[
fitV[getVin[files[[i]]], fset[[i]], ϕinguesses[[i]]]["BestFitParameters"], {i, 1, Length[files]}];
TableForm[infits]

You can then easily extract the input amplitudes:

Vins = V /. infits

Similarly, you can create a table of the output amplitudes, and then use those lists to create the resonance curve.  For the frequencies, just use the values you set on the function generator.