From 71236f900c6b4276a7a93cd010f403213f8e2f18 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sun, 21 Dec 2008 00:01:11 -0500 Subject: [PATCH] calibrate.py should now work. A bunch of changes in one commit, sorry. Moved to fledgling splittable_kwargs system to make default argument maintenance easier. I expect the splittable_kwargs system still has some growing to do, but it's already better than the old system. Merged BE database from the calibcant subdir into the main BE database. It was a mistake to create the database there in the first place. --- .../b7152be2-60ea-439f-be64-9ba45ce7e4fa/body | 0 .../values | 0 .../values | 0 .../1d27eecf-4327-4342-bd82-41a859d33a37/body | 0 .../values | 0 .../82052baf-730c-4b42-b2a0-783de0201c2f/body | 0 .../values | 0 .../values | 0 .../285836e5-205d-441c-8b9d-65adea9d486f/body | 30 + .../values | 11 + .../651ebc04-cc59-4996-b4f3-33bfd9c88c23/body | 46 + .../values | 8 + .../96566a23-d674-44cb-8839-dfd962c3ad99/body | 2 + .../values | 11 + .../daf08023-7758-482e-a481-800872e84312/body | 13 + .../values | 11 + .../values | 17 + .../68e5c534-39b4-46ce-85bc-766155f03d6f/body | 2 + .../values | 8 + .../values | 17 + .../7bb7514d-f094-461c-8b96-04590415282c/body | 0 .../values | 0 .../d239edab-0769-496e-8c49-fdd25677afb7/body | 0 .../values | 0 .../d6896169-8e43-4e31-a2c6-53a580ba5ec6/body | 0 .../values | 0 .../values | 0 .be/settings | 7 +- calibcant/.be/settings | 7 - calibcant/.be/version | 1 - calibcant/T_analyze.py | 18 +- calibcant/analyze.py | 84 +- calibcant/bump_analyze.py | 50 +- calibcant/calibrate.py | 393 ++++++++ calibcant/calibrate_cantilever.py | 911 ------------------ calibcant/config.py | 6 + calibcant/vib_analyze.py | 56 +- 37 files changed, 728 insertions(+), 981 deletions(-) rename {calibcant/.be => .be}/bugs/1456fd10-2036-46de-b18e-ddf8cab14ae2/comments/b7152be2-60ea-439f-be64-9ba45ce7e4fa/body (100%) rename {calibcant/.be => .be}/bugs/1456fd10-2036-46de-b18e-ddf8cab14ae2/comments/b7152be2-60ea-439f-be64-9ba45ce7e4fa/values (100%) rename {calibcant/.be => .be}/bugs/1456fd10-2036-46de-b18e-ddf8cab14ae2/values (100%) rename {calibcant/.be => .be}/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/comments/1d27eecf-4327-4342-bd82-41a859d33a37/body (100%) rename {calibcant/.be => .be}/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/comments/1d27eecf-4327-4342-bd82-41a859d33a37/values (100%) rename {calibcant/.be => .be}/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/comments/82052baf-730c-4b42-b2a0-783de0201c2f/body (100%) rename {calibcant/.be => .be}/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/comments/82052baf-730c-4b42-b2a0-783de0201c2f/values (100%) rename {calibcant/.be => .be}/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/values (100%) create mode 100644 .be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/285836e5-205d-441c-8b9d-65adea9d486f/body create mode 100644 .be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/285836e5-205d-441c-8b9d-65adea9d486f/values create mode 100644 .be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/651ebc04-cc59-4996-b4f3-33bfd9c88c23/body create mode 100644 .be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/651ebc04-cc59-4996-b4f3-33bfd9c88c23/values create mode 100644 .be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/96566a23-d674-44cb-8839-dfd962c3ad99/body create mode 100644 .be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/96566a23-d674-44cb-8839-dfd962c3ad99/values create mode 100644 .be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/daf08023-7758-482e-a481-800872e84312/body create mode 100644 .be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/daf08023-7758-482e-a481-800872e84312/values create mode 100644 .be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/values create mode 100644 .be/bugs/4d7fd386-2bf6-4c31-ae68-cdfa364ce2ac/comments/68e5c534-39b4-46ce-85bc-766155f03d6f/body create mode 100644 .be/bugs/4d7fd386-2bf6-4c31-ae68-cdfa364ce2ac/comments/68e5c534-39b4-46ce-85bc-766155f03d6f/values create mode 100644 .be/bugs/4d7fd386-2bf6-4c31-ae68-cdfa364ce2ac/values rename {calibcant/.be => .be}/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/7bb7514d-f094-461c-8b96-04590415282c/body (100%) rename {calibcant/.be => .be}/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/7bb7514d-f094-461c-8b96-04590415282c/values (100%) rename {calibcant/.be => .be}/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/d239edab-0769-496e-8c49-fdd25677afb7/body (100%) rename {calibcant/.be => .be}/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/d239edab-0769-496e-8c49-fdd25677afb7/values (100%) rename {calibcant/.be => .be}/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/d6896169-8e43-4e31-a2c6-53a580ba5ec6/body (100%) rename {calibcant/.be => .be}/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/d6896169-8e43-4e31-a2c6-53a580ba5ec6/values (100%) rename {calibcant/.be => .be}/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/values (100%) delete mode 100644 calibcant/.be/settings delete mode 100644 calibcant/.be/version create mode 100644 calibcant/calibrate.py delete mode 100644 calibcant/calibrate_cantilever.py diff --git a/calibcant/.be/bugs/1456fd10-2036-46de-b18e-ddf8cab14ae2/comments/b7152be2-60ea-439f-be64-9ba45ce7e4fa/body b/.be/bugs/1456fd10-2036-46de-b18e-ddf8cab14ae2/comments/b7152be2-60ea-439f-be64-9ba45ce7e4fa/body similarity index 100% rename from calibcant/.be/bugs/1456fd10-2036-46de-b18e-ddf8cab14ae2/comments/b7152be2-60ea-439f-be64-9ba45ce7e4fa/body rename to .be/bugs/1456fd10-2036-46de-b18e-ddf8cab14ae2/comments/b7152be2-60ea-439f-be64-9ba45ce7e4fa/body diff --git a/calibcant/.be/bugs/1456fd10-2036-46de-b18e-ddf8cab14ae2/comments/b7152be2-60ea-439f-be64-9ba45ce7e4fa/values b/.be/bugs/1456fd10-2036-46de-b18e-ddf8cab14ae2/comments/b7152be2-60ea-439f-be64-9ba45ce7e4fa/values similarity index 100% rename from calibcant/.be/bugs/1456fd10-2036-46de-b18e-ddf8cab14ae2/comments/b7152be2-60ea-439f-be64-9ba45ce7e4fa/values rename to .be/bugs/1456fd10-2036-46de-b18e-ddf8cab14ae2/comments/b7152be2-60ea-439f-be64-9ba45ce7e4fa/values diff --git a/calibcant/.be/bugs/1456fd10-2036-46de-b18e-ddf8cab14ae2/values b/.be/bugs/1456fd10-2036-46de-b18e-ddf8cab14ae2/values similarity index 100% rename from calibcant/.be/bugs/1456fd10-2036-46de-b18e-ddf8cab14ae2/values rename to .be/bugs/1456fd10-2036-46de-b18e-ddf8cab14ae2/values diff --git a/calibcant/.be/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/comments/1d27eecf-4327-4342-bd82-41a859d33a37/body b/.be/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/comments/1d27eecf-4327-4342-bd82-41a859d33a37/body similarity index 100% rename from calibcant/.be/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/comments/1d27eecf-4327-4342-bd82-41a859d33a37/body rename to .be/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/comments/1d27eecf-4327-4342-bd82-41a859d33a37/body diff --git a/calibcant/.be/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/comments/1d27eecf-4327-4342-bd82-41a859d33a37/values b/.be/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/comments/1d27eecf-4327-4342-bd82-41a859d33a37/values similarity index 100% rename from calibcant/.be/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/comments/1d27eecf-4327-4342-bd82-41a859d33a37/values rename to .be/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/comments/1d27eecf-4327-4342-bd82-41a859d33a37/values diff --git a/calibcant/.be/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/comments/82052baf-730c-4b42-b2a0-783de0201c2f/body b/.be/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/comments/82052baf-730c-4b42-b2a0-783de0201c2f/body similarity index 100% rename from calibcant/.be/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/comments/82052baf-730c-4b42-b2a0-783de0201c2f/body rename to .be/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/comments/82052baf-730c-4b42-b2a0-783de0201c2f/body diff --git a/calibcant/.be/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/comments/82052baf-730c-4b42-b2a0-783de0201c2f/values b/.be/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/comments/82052baf-730c-4b42-b2a0-783de0201c2f/values similarity index 100% rename from calibcant/.be/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/comments/82052baf-730c-4b42-b2a0-783de0201c2f/values rename to .be/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/comments/82052baf-730c-4b42-b2a0-783de0201c2f/values diff --git a/calibcant/.be/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/values b/.be/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/values similarity index 100% rename from calibcant/.be/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/values rename to .be/bugs/3059911a-963c-472e-a9d3-bf0a290de1a2/values diff --git a/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/285836e5-205d-441c-8b9d-65adea9d486f/body b/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/285836e5-205d-441c-8b9d-65adea9d486f/body new file mode 100644 index 0000000..883bebe --- /dev/null +++ b/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/285836e5-205d-441c-8b9d-65adea9d486f/body @@ -0,0 +1,30 @@ +z_piezo_calib output confirmed with + +$ cd /home/wking/research/drexel/gyang/data/z_piezo_calib +$ ipython --pylab +In [1]: import z_piezo_calib +In [2]: z_piezo_calib.analyze_calibration_data_from_tweaks(bump_tweaks="20081215_tweak.bump", vib_tweaks="20081215_tweak.vib", T_tweaks="20081215_tweak.temp", plotVerbose=False) +a**2 : 0.000190122 +/- 1.86869e-06 (0.00982893) +T : 295.15 +/- 5.68434e-14 (1.92592e-16) +1/Vp**2 : 61416.8 +/- 17792.9 (0.289708) +Out[2]: +(0.0475823000538, + 0.0137928827301, + 0.00982893194938, + 1.92591627514e-16, + 0.289707547407) + +In [3]: z_piezo_calib.analyze_calibration_data_from_tweaks(bump_tweaks="20081215_tweak_second.bump", vib_tweaks="20081215_tweak_second.vib", T_tweaks="20081215_tweak_second.temp", plotVerbose=False) +a**2 : 0.000217743 +/- 1.08297e-06 (0.00497362) +T : 295.15 +/- 5.68434e-14 (1.92592e-16) +1/Vp**2 : 38507.9 +/- 13142.7 (0.341299) +Out[3]: +(0.034168120631, + 0.0116627940459, + 0.00497362408195, + 1.92591627514e-16, + 0.341299306648) + +However, plotVerbose is broken (missing figure definition), so I'm +gonna go fix that now. + diff --git a/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/285836e5-205d-441c-8b9d-65adea9d486f/values b/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/285836e5-205d-441c-8b9d-65adea9d486f/values new file mode 100644 index 0000000..accc717 --- /dev/null +++ b/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/285836e5-205d-441c-8b9d-65adea9d486f/values @@ -0,0 +1,11 @@ +Content-type: text/plain + + +Date: Fri, 19 Dec 2008 06:17:07 +0000 + + +From: W. Trevor King + + +In-reply-to: daf08023-7758-482e-a481-800872e84312 + diff --git a/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/651ebc04-cc59-4996-b4f3-33bfd9c88c23/body b/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/651ebc04-cc59-4996-b4f3-33bfd9c88c23/body new file mode 100644 index 0000000..5e95cdc --- /dev/null +++ b/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/651ebc04-cc59-4996-b4f3-33bfd9c88c23/body @@ -0,0 +1,46 @@ +I just reran Marissa's calibration. My original output (from the old +gnuplot-fitted ~/.python/calibrate_cantilever.py) had produced + + $ cat 20081215/20081215101516_analysis_text + k (N/m) : 0.0471212 +/- 0.0132663 + a**2 (V/nm)**2 : 0.000191658 +/- 1.36722e-06 + T (K) : 295.15 +/- 5.68434e-14 + 1/Vp**2 (1/V)**2 : 60334.2 +/- 16980.8 + $ cat 20081215/20081215124856_analysis_text + k (N/m) : 0.0346675 +/- 0.0118672 + a**2 (V/nm)**2 : 0.000217743 +/- 1.08297e-06 + T (K) : 295.15 +/- 5.68434e-14 + 1/Vp**2 (1/V)**2 : 39070.7 +/- 13373.1 + +Using all the defaults (and only the second take for the first +calibration). However, once I'd set up the tweakfiles and ran +20081215_calibrate.sh, I got + + $ cat 20081215/20081215101516_analysis_text + Variable (units) : mean +/- std. dev. (relative error) + Cantilever k (N/m) : 0.0656644 +/- 0.00107743 (0.016408) + photoSensitivity**2 (V/nm)**2 : 0.000190122 +/- 1.86869e-06 (0.00982893) + T (K) : 295.15 +/- 5.68434e-14 (1.92592e-16) + 1/Vphoto**2 (1/V)**2 : 84756.3 +/- 1113.56 (0.0131383) + $ cat 20081215/20081215124856_analysis_text + Variable (units) : mean +/- std. dev. (relative error) + Cantilever k (N/m) : 0.0494809 +/- 0.000855265 (0.0172848) + photoSensitivity**2 (V/nm)**2 : 0.000217743 +/- 1.08297e-06 (0.00497362) + T (K) : 295.15 +/- 5.68434e-14 (1.92592e-16) + 1/Vphoto**2 (1/V)**2 : 55765.6 +/- 923.129 (0.0165537) + +Comparing the two, we see: + 101516: + k 0.0471212 -> 0.0656644 (with a 92% drop in error) + photoSensitivity 0.000191658 (little change) + T 295.15 (no change) + 1/Vphoto**2 60334.2 -> 84756.3 (with a 94% drop in error) + 124856: + k 0.0346675 -> 0.0494809 (with a 93% drop in error) + photoSensitivity 0.000217743 (no change) + T 295.15 (no change) + 1/Vphoto**2 39070.7 -> 55765.6 (with a 93% drop in error) + +I don't have hard evidence yet, but I am confident this discrepancy is +due to poor fitting with the older calibration file. + diff --git a/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/651ebc04-cc59-4996-b4f3-33bfd9c88c23/values b/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/651ebc04-cc59-4996-b4f3-33bfd9c88c23/values new file mode 100644 index 0000000..2b97586 --- /dev/null +++ b/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/651ebc04-cc59-4996-b4f3-33bfd9c88c23/values @@ -0,0 +1,8 @@ +Content-type: text/plain + + +Date: Fri, 19 Dec 2008 05:25:39 +0000 + + +From: W. Trevor King + diff --git a/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/96566a23-d674-44cb-8839-dfd962c3ad99/body b/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/96566a23-d674-44cb-8839-dfd962c3ad99/body new file mode 100644 index 0000000..da07afb --- /dev/null +++ b/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/96566a23-d674-44cb-8839-dfd962c3ad99/body @@ -0,0 +1,2 @@ +Yikes, z_piezo_calib uses the actual variance (no fitting)! No wonder +it was so ugly. diff --git a/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/96566a23-d674-44cb-8839-dfd962c3ad99/values b/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/96566a23-d674-44cb-8839-dfd962c3ad99/values new file mode 100644 index 0000000..2b48f42 --- /dev/null +++ b/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/96566a23-d674-44cb-8839-dfd962c3ad99/values @@ -0,0 +1,11 @@ +Content-type: text/plain + + +Date: Fri, 19 Dec 2008 06:22:45 +0000 + + +From: W. Trevor King + + +In-reply-to: 651ebc04-cc59-4996-b4f3-33bfd9c88c23 + diff --git a/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/daf08023-7758-482e-a481-800872e84312/body b/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/daf08023-7758-482e-a481-800872e84312/body new file mode 100644 index 0000000..9cced00 --- /dev/null +++ b/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/daf08023-7758-482e-a481-800872e84312/body @@ -0,0 +1,13 @@ +Oops, I was running ~/.python/z_piezo_calib.calibrate_cantilever() +during the experiment, not ~/.python/calibrate_cantilever.something(). +I was wondering why it was so difficult to get +~./python/calibrate_cantilever.py working. I ended up getting + + Cantilever k : 0.589927 +/- 0.790152 (1.33941) + photoSensitivity**2 : 0.000190122 +/- 1.86869e-06 (0.00982893) + T : 295.15 +/- 0 (0) + 1/Vphoto**2 : 761447 +/- 1.01986e+06 (1.33937) + +out of calibrate_cantilever.py for the first cantilever, but that is +pretty rediculous... Maybe I'll come back and figure out what's going on +after I check out the z_piezo_calib version... diff --git a/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/daf08023-7758-482e-a481-800872e84312/values b/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/daf08023-7758-482e-a481-800872e84312/values new file mode 100644 index 0000000..1a459ea --- /dev/null +++ b/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/comments/daf08023-7758-482e-a481-800872e84312/values @@ -0,0 +1,11 @@ +Content-type: text/plain + + +Date: Fri, 19 Dec 2008 06:09:39 +0000 + + +From: W. Trevor King + + +In-reply-to: 651ebc04-cc59-4996-b4f3-33bfd9c88c23 + diff --git a/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/values b/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/values new file mode 100644 index 0000000..58d8051 --- /dev/null +++ b/.be/bugs/443dd869-a3f5-456c-82af-388f0923bc27/values @@ -0,0 +1,17 @@ +creator: W. Trevor King + + +reporter: W. Trevor King + + +severity: minor + + +status: fixed + + +summary: Suprising difference between old calibration and new calibration + + +time: Fri, 19 Dec 2008 05:08:51 +0000 + diff --git a/.be/bugs/4d7fd386-2bf6-4c31-ae68-cdfa364ce2ac/comments/68e5c534-39b4-46ce-85bc-766155f03d6f/body b/.be/bugs/4d7fd386-2bf6-4c31-ae68-cdfa364ce2ac/comments/68e5c534-39b4-46ce-85bc-766155f03d6f/body new file mode 100644 index 0000000..278309b --- /dev/null +++ b/.be/bugs/4d7fd386-2bf6-4c31-ae68-cdfa364ce2ac/comments/68e5c534-39b4-46ce-85bc-766155f03d6f/body @@ -0,0 +1,2 @@ +Some files use config.TEXT_VERBOSE boolean, others use textVerboseFile option. +Pick one method and go with it. diff --git a/.be/bugs/4d7fd386-2bf6-4c31-ae68-cdfa364ce2ac/comments/68e5c534-39b4-46ce-85bc-766155f03d6f/values b/.be/bugs/4d7fd386-2bf6-4c31-ae68-cdfa364ce2ac/comments/68e5c534-39b4-46ce-85bc-766155f03d6f/values new file mode 100644 index 0000000..5d3e5f1 --- /dev/null +++ b/.be/bugs/4d7fd386-2bf6-4c31-ae68-cdfa364ce2ac/comments/68e5c534-39b4-46ce-85bc-766155f03d6f/values @@ -0,0 +1,8 @@ +Content-type: text/plain + + +Date: Sun, 21 Dec 2008 03:07:58 +0000 + + +From: W. Trevor King + diff --git a/.be/bugs/4d7fd386-2bf6-4c31-ae68-cdfa364ce2ac/values b/.be/bugs/4d7fd386-2bf6-4c31-ae68-cdfa364ce2ac/values new file mode 100644 index 0000000..dcb31dc --- /dev/null +++ b/.be/bugs/4d7fd386-2bf6-4c31-ae68-cdfa364ce2ac/values @@ -0,0 +1,17 @@ +creator: W. Trevor King + + +reporter: W. Trevor King + + +severity: minor + + +status: open + + +summary: unify TEXT_VERBOSE + + +time: Sun, 21 Dec 2008 03:07:08 +0000 + diff --git a/calibcant/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/7bb7514d-f094-461c-8b96-04590415282c/body b/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/7bb7514d-f094-461c-8b96-04590415282c/body similarity index 100% rename from calibcant/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/7bb7514d-f094-461c-8b96-04590415282c/body rename to .be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/7bb7514d-f094-461c-8b96-04590415282c/body diff --git a/calibcant/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/7bb7514d-f094-461c-8b96-04590415282c/values b/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/7bb7514d-f094-461c-8b96-04590415282c/values similarity index 100% rename from calibcant/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/7bb7514d-f094-461c-8b96-04590415282c/values rename to .be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/7bb7514d-f094-461c-8b96-04590415282c/values diff --git a/calibcant/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/d239edab-0769-496e-8c49-fdd25677afb7/body b/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/d239edab-0769-496e-8c49-fdd25677afb7/body similarity index 100% rename from calibcant/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/d239edab-0769-496e-8c49-fdd25677afb7/body rename to .be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/d239edab-0769-496e-8c49-fdd25677afb7/body diff --git a/calibcant/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/d239edab-0769-496e-8c49-fdd25677afb7/values b/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/d239edab-0769-496e-8c49-fdd25677afb7/values similarity index 100% rename from calibcant/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/d239edab-0769-496e-8c49-fdd25677afb7/values rename to .be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/d239edab-0769-496e-8c49-fdd25677afb7/values diff --git a/calibcant/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/d6896169-8e43-4e31-a2c6-53a580ba5ec6/body b/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/d6896169-8e43-4e31-a2c6-53a580ba5ec6/body similarity index 100% rename from calibcant/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/d6896169-8e43-4e31-a2c6-53a580ba5ec6/body rename to .be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/d6896169-8e43-4e31-a2c6-53a580ba5ec6/body diff --git a/calibcant/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/d6896169-8e43-4e31-a2c6-53a580ba5ec6/values b/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/d6896169-8e43-4e31-a2c6-53a580ba5ec6/values similarity index 100% rename from calibcant/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/d6896169-8e43-4e31-a2c6-53a580ba5ec6/values rename to .be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/comments/d6896169-8e43-4e31-a2c6-53a580ba5ec6/values diff --git a/calibcant/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/values b/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/values similarity index 100% rename from calibcant/.be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/values rename to .be/bugs/f3ba76cd-eddd-4520-bb2a-17ca371ac6b6/values diff --git a/.be/settings b/.be/settings index 3fe7e72..739d28c 100644 --- a/.be/settings +++ b/.be/settings @@ -1,7 +1,2 @@ - - - -rcs_name=git - - +rcs_name: git diff --git a/calibcant/.be/settings b/calibcant/.be/settings deleted file mode 100644 index 3fe7e72..0000000 --- a/calibcant/.be/settings +++ /dev/null @@ -1,7 +0,0 @@ - - - -rcs_name=git - - - diff --git a/calibcant/.be/version b/calibcant/.be/version deleted file mode 100644 index 990837e..0000000 --- a/calibcant/.be/version +++ /dev/null @@ -1 +0,0 @@ -Bugs Everywhere Tree 1 0 diff --git a/calibcant/T_analyze.py b/calibcant/T_analyze.py index 7d46993..d52475d 100755 --- a/calibcant/T_analyze.py +++ b/calibcant/T_analyze.py @@ -37,11 +37,18 @@ import common # common module for the calibcant package import config # config module for the calibcant package import data_logger import linfit +from splittable_kwargs import splittableKwargsFunction, \ + make_splittable_kwargs_function def C_to_K(celsius) : "Convert Celsius -> Kelvin." return celsius + 273.15 +def K_to_K(kelvin) : + "Convert Kelvin -> Kelvin." + return kelvin + +@splittableKwargsFunction() def T_analyze(T, convert_to_K=C_to_K) : """ Not much to do here, just convert to Kelvin. @@ -54,6 +61,7 @@ def T_analyze(T, convert_to_K=C_to_K) : T = [convert_to_K(T)] return T +@splittableKwargsFunction() def T_save(T, log_dir=None) : """ Save either a single T (if you are crazy :p), @@ -81,15 +89,20 @@ def T_load(datafile=None) : dl = data_logger.data_load() return dl.read_binary(datafile) -def T_plot(T, plotVerbose) : +@splittableKwargsFunction() +def T_plot(T, plotVerbose=False) : """ Umm, just print the temperature? """ if plotVerbose or PYLAB_VERBOSE or TEXT_VERBOSE : print "Temperature ", T -def T_load_analyze_tweaked(tweak_file, convert_to_K=C_to_K, textVerboseFile=None) : +@splittableKwargsFunction((T_analyze, 'T', 'convert_to_K'), + (T_plot, 'T')) +def T_load_analyze_tweaked(tweak_file, convert_to_K=C_to_K, textVerboseFile=None, **kwargs) : "Load all the T array files from a tweak file and return a single array" + T_analyze_kwargs,T_plot_kwargs = \ + T_load_analyze_tweaked._splitargs(T_load_analyze_tweaked, kwargs) Ts = [] for line in file(tweak_file, 'r') : parsed = line.split() @@ -99,6 +112,7 @@ def T_load_analyze_tweaked(tweak_file, convert_to_K=C_to_K, textVerboseFile=None # read the data data = T_load(path) Ts.extend(data) + T_analyze(Ts, convert_to_K=K_to_K) return numpy.array(Ts, dtype=numpy.float) # commandline interface functions diff --git a/calibcant/analyze.py b/calibcant/analyze.py index 797a78a..2e535ef 100755 --- a/calibcant/analyze.py +++ b/calibcant/analyze.py @@ -47,13 +47,18 @@ Which are related by the parameters : """ import numpy +from splittable_kwargs import splittableKwargsFunction, \ + make_splittable_kwargs_function import common # common module for the calibcant package import config # config module for the calibcant package import T_analyze # T_analyze module for the calibcant package kb = 1.3806504e-23 # Boltzmann's constant in J/K -def calib_analyze(bumps, Ts, vibs) : +#@splittableKwargsFunction((calib_plot, 'bumps', 'Ts', 'vibs')) +# Some of the child functions aren't yet defined, so postpone +# make-splittable until later in the module. +def calib_analyze(bumps, Ts, vibs, **kwargs) : """ Analyze data from get_calibration_data() return (k, k_s, !!!a2_r, T_r, one_o_Vp2_r) @@ -78,6 +83,7 @@ def calib_analyze(bumps, Ts, vibs) : Remember that you need enough samples to have a valid error estimate in the first place, and that none of this addresses any systematic errors. """ + calib_plot_kwargs, = calib_analyze._splitargs(calib_analyze, kwargs) photoSensitivity2 = bumps**2 one_o_Vphoto2 = 1/vibs @@ -105,10 +111,13 @@ def calib_analyze(bumps, Ts, vibs) : k_s = k*(photoSensitivity2_r**2 + T_r**2 + one_o_Vphoto2_r**2)**0.5 + calib_plot(bumps, Ts, vibs, **calib_plot_kwargs) + return (k, k_s, photoSensitivity2_m, photoSensitivity2_s, T_m, T_s, one_o_Vphoto2_m, one_o_Vphoto2_s) +@splittableKwargsFunction() def string_errors(k_m, k_s, photoSensitivity2_m, photoSensitivity2_s, T_m, T_s, @@ -128,19 +137,66 @@ def string_errors(k_m, k_s, % (one_o_Vphoto2_m, one_o_Vphoto2_s, one_o_Vphoto2_r) return string -def calib_plot(bumps, Ts, vibs) : - from pylab import figure, subplot, plot, title, show - figure() - subplot(311) - plot(bumps, 'g.-') - title('Photodiode sensitivity (V/nm)') - subplot(312) - plot(Ts, 'r.-') - title('Temperature (K)') - subplot(313) - plot(vibs, 'b.-') - title('Thermal deflection variance (Volts**2)') - show() +@splittableKwargsFunction() +def calib_save(bumps, Ts, vibs, log_dir=None) : + """ + Save a dictonary with the bump, T, and vib data. + """ + if log_dir != None : + data = {'bump':bumps, 'T':Ts, 'vib':vibs} + log = data_logger.data_log(log_dir, noclobber_logsubdir=False, + log_name="calib") + log.write_dict_of_arrays(data) + +def calib_load(datafile) : + "Load the dictionary data, using data_logger.date_load()" + dl = data_logger.data_load() + data = dl.read_dict_of_arrays(path) + return (data['bump'], data['T'], data['vib']) + +def calib_save_analysis(k, k_s, + photoSensitivity2_m, photoSensitivity2_s, + T_m, T_s, one_o_Vphoto2_m, one_o_Vphoto2_s, + log_dir=None) : + if log_dir != None : + log = data_logger.data_log(log_dir, noclobber_logsubdir=False, + log_name="calib_analysis_text") + log.write_binary(( + "k (N/m) : %g +/- %g\n" % (k, k_s) + + "photoSensitivity**2 (V/nm)**2 : %g +/- %g\n" % \ + (photoSensitivity2_m, photoSensitivity2_s) + + "T (K) : %g +/- %g\n" % (T_m, T_s) + + "1/Vp**2 (1/V)**2 : %g +/- %g\n" % \ + (one_o_Vphoto2_m, one_o_Vphoto2_s) + )) + +@splittableKwargsFunction() +def calib_plot(bumps, Ts, vibs, plotVerbose=False) : + if plotVerbose or config.PYLAB_VERBOSE : + common.import_pylab() + common._pylab.figure(config.BASE_FIGNUM+4) + common._pylab.subplot(311) + common._pylab.plot(bumps, 'g.') + common._pylab.title('Photodiode sensitivity (V/nm)') + common._pylab.subplot(312) + common._pylab.plot(Ts, 'r.') + common._pylab.title('Temperature (K)') + common._pylab.subplot(313) + common._pylab.plot(vibs, 'b.') + common._pylab.title('Thermal deflection variance (Volts**2)') + common._flush_plot() + +make_splittable_kwargs_function(calib_analyze, + (calib_plot, 'bumps', 'Ts', 'vibs')) + +@splittableKwargsFunction((calib_analyze, 'bumps', 'Ts', 'vibs')) +def calib_load_analyze_tweaks(bump_tweaks, vib_tweaks, T_tweaks=None) : + raise NotImplementedError + a = read_tweaked_bumps(bump_tweaks) + vib = V_photo_variance_from_file(vib_tweaks) + if T_tweaks == None: + pass + return analyze_calibration_data(a, T, vib, log_dir=log_dir) # commandline interface functions import scipy.io, sys diff --git a/calibcant/bump_analyze.py b/calibcant/bump_analyze.py index 3d3a891..11a8f51 100755 --- a/calibcant/bump_analyze.py +++ b/calibcant/bump_analyze.py @@ -39,6 +39,10 @@ Which are related by the parameters : zpGain Vzp_out / Vzp zpSensitivity Zp / Vzp photoSensitivity Vphoto / Zcant + +photoSensitivity is measured by bumping the cantilever against the +surface, where Zp = Zcant (see calibrate.bump_aquire()). The measured +slope Vphoto/Vout is converted to photoSensitivity with bump_analyze(). """ import numpy @@ -47,12 +51,17 @@ import config # config module for the calibcant package import data_logger import z_piezo_utils import linfit +from splittable_kwargs import splittableKwargsFunction, \ + make_splittable_kwargs_function +#@splittableKwargsFunction((bump_plot, 'data')) +# Some of the child functions aren't yet defined, so postpone +# make-splittable until later in the module. def bump_analyze(data, zpGain=config.zpGain, zpSensitivity=config.zpSensitivity, Vzp_out2V=config.Vzp_out2V, Vphoto_in2V=config.Vphoto_in2V, - textVerboseFile=None, plotVerbose=False) : + **kwargs) : """ Return the slope of the bump ;). Inputs: @@ -69,19 +78,22 @@ def bump_analyze(data, zpGain=config.zpGain, and THEN converted, so we're assuming that both conversions are LINEAR. if they aren't, rewrite to convert before the regression. """ + bump_plot_kwargs, = bump_analyze._splitargs(bump_analyze, kwargs) scale_Vzp_bits2V = Vzp_out2V(1) - Vzp_out2V(0) scale_Vphoto_bits2V = Vphoto_in2V(1) - Vphoto_in2V(0) Vphoto2Vzp_out_bit, intercept = \ linfit.linregress(x=data["Z piezo output"], y=data["Deflection input"], - plotVerbose=plotVerbose) - Vphoto2Vzp_out = Vphoto2Vzp_out_bit * scale_Vphoto_bits2V / scale_Vzp_bits2V + plotVerbose=False) + Vphoto2Vzp_out = Vphoto2Vzp_out_bit *scale_Vphoto_bits2V / scale_Vzp_bits2V # 1 / (Vzp/Vzp_out * Zp/Vzp * Zcant/Zp ) Vzp_out2Zcant = 1.0/ (zpGain * zpSensitivity) # * 1 + bump_plot(data, **bump_plot_kwargs) return Vphoto2Vzp_out * Vzp_out2Zcant -def bump_save(data, log_dir) : +@splittableKwargsFunction() +def bump_save(data, log_dir=None) : "Save the dictionary data, using data_logger.data_log()" if log_dir != None : log = data_logger.data_log(log_dir, noclobber_logsubdir=False, @@ -94,7 +106,8 @@ def bump_load(datafile) : data = dl.read_dict_of_arrays(datafile) return data -def bump_plot(data, plotVerbose) : +@splittableKwargsFunction() +def bump_plot(data, plotVerbose=False) : "Plot the bump (Vphoto vs Vzp) if plotVerbose or PYLAB_VERBOSE == True" if plotVerbose or config.PYLAB_VERBOSE : common._import_pylab() @@ -105,18 +118,19 @@ def bump_plot(data, plotVerbose) : common._pylab.legend(loc='upper left') common._flush_plot() -def bump_load_analyze_tweaked(tweak_file, zpGain=config.zpGain, - zpSensitivity=config.zpSensitivity, - Vzp_out2V=config.Vzp_out2V, - Vphoto_in2V=config.Vphoto_in2V, - textVerboseFile=None, plotVerbose=False) : +make_splittable_kwargs_function(bump_analyze, (bump_plot, 'data')) + +@splittableKwargsFunction((bump_analyze, 'data')) +def bump_load_analyze_tweaked(tweak_file, **kwargs): "Load the output file of tweak_calib_bump.sh, return an array of slopes" + bump_analyze_kwargs, = \ + bump_load_analyze_tweaked._splitargs(bump_load_analyze_tweaked, kwargs) photoSensitivity = [] for line in file(tweak_file, 'r') : parsed = line.split() path = parsed[0].split('\n')[0] - if textVerboseFile != None : - print >> textVerboseFile, "Reading data from %s with ranges %s" % (path, parsed[1:]) + if config.TEXT_VERBOSE : + print "Reading data from %s with ranges %s" % (path, parsed[1:]) # read the data full_data = bump_load(path) if len(parsed) == 1 : @@ -133,10 +147,8 @@ def bump_load_analyze_tweaked(tweak_file, zpGain=config.zpGain, df.extend(full_data['Deflection input'][starti:stopi]) data = {'Z piezo output': numpy.array(zp), 'Deflection input': numpy.array(df)} - pSi = bump_analyze(data, zpGain, zpSensitivity, - Vzp_out2V, Vphoto_in2V, plotVerbose) + pSi = bump_analyze(data, **bump_analyze_kwargs) photoSensitivity.append(pSi) - bump_plot(data, plotVerbose) return numpy.array(photoSensitivity, dtype=numpy.float) # commandline interface functions @@ -216,10 +228,6 @@ if __name__ == '__main__' : ofile = file(options.ofilename, 'w') else : ofile = sys.stdout - if options.verbose == True : - vfile = sys.stderr - else : - vfile = None config.TEXT_VERBOSE = options.verbose config.PYLAB_INTERACTIVE = False config.PYLAB_VERBOSE = options.pylab @@ -238,11 +246,11 @@ if __name__ == '__main__' : # data not very bilinear, so don't cut anything off. print >> sys.stderr, "use everything" - photoSensitivity = bump_analyze(data, textVerboseFile=vfile) + photoSensitivity = bump_analyze(data) print >> ofile, photoSensitivity else : # tweak file mode - slopes = bump_load_analyze_tweaked(ifilename, textVerboseFile=vfile) + slopes = bump_load_analyze_tweaked(ifilename) if options.comma_out : sep = ',' else : diff --git a/calibcant/calibrate.py b/calibcant/calibrate.py new file mode 100644 index 0000000..64ac942 --- /dev/null +++ b/calibcant/calibrate.py @@ -0,0 +1,393 @@ +#!/usr/bin/python + +""" +Aquire and analyze cantilever calibration data. + +W. Trevor King Dec. 2007-Jan. 2008 + +GPL BOILERPLATE + + +The relevent physical quantities are : + Vzp_out Output z-piezo voltage (what we generate) + Vzp Applied z-piezo voltage (after external ZPGAIN) + Zp The z-piezo position + Zcant The cantilever vertical deflection + Vphoto The photodiode vertical deflection voltage (what we measure) + Fcant The force on the cantilever + T The temperature of the cantilever and surrounding solution + (another thing we measure or guess) + k_b Boltzmann's constant + +Which are related by the parameters : + zpGain Vzp_out / Vzp + zpSensitivity Zp / Vzp + photoSensitivity Vphoto / Zcant + k_cant Fcant / Zcant + +Cantilever calibration assumes a pre-calibrated z-piezo +(zpSensitivity) and a amplifier (zpGain). In our lab, the z-piezo is +calibrated by imaging a calibration sample, which has features with +well defined sizes, and the gain is set with a knob on the Nanoscope. + +photoSensitivity is measured by bumping the cantilever against the +surface, where Zp = Zcant (see bump_aquire() and the bump_analyze +submodule). + +k_cant is measured by watching the cantilever vibrate in free solution +(see the vib_aquire() and the vib_analyze submodule). The average +energy of the cantilever in the vertical direction is given by the +equipartition theorem. + 1/2 k_b T = 1/2 k_cant + so k_cant = k_b T / Zcant**2 + but Zcant = Vphoto / photoSensitivity + so k_cant = k_b T * photoSensitivty**2 / + +We measured photoSensitivity with the surface bumps. We can either +measure T using an external function (see temperature.py), or just +estimate it (see T_aquire() and the T_analyze submodule). Guessing +room temp ~22 deg C is actually fairly reasonable. Assuming the +actual fluid temperature is within +/- 5 deg, the error in the spring +constant k_cant is within 5/273.15 ~= 2%. A time series of Vphoto +while we're far from the surface and not changing Vzp_out will give us +the average variance . + +We do all these measurements a few times to estimate statistical +errors. + +The functions are layed out in the families: + bump_*(), vib_*(), T_*(), and calib_*() +where calib_{save|load|analyze}() deal with derived data, not +real-world data. + +For each family, * can be any of : + aquire get real-world data + save store real-world data to disk + load get real-world data from disk + analyze interperate the real-world data. + plot show a nice graphic to convince people we're working :p + load_analyze_tweaked + read a file with a list of paths to previously saved + real world data load each file using *_load(), analyze + using *_analyze(), and optionally plot using *_plot(). + Intended for re-processing old data. +A family name without any _* extension (e.g. bump()), runs *_aquire(), + *_save(), *_analyze(), *_plot(). + +We also define the two positioning functions: + move_just_onto_surface() and move_far_from_surface() +which make automating the calibration procedure more straightforward. +""" + +import numpy +import time +import z_piezo_utils +from splittable_kwargs import splittableKwargsFunction, \ + make_splittable_kwargs_function + +import common +import config +import bump_analyze +import T_analyze +import vib_analyze +import analyze + +# bump family + +@splittableKwargsFunction() +def bump_aquire(zpiezo, push_depth=200, npoints=1024, freq=100e3) : + """ + Ramps closer push_depth and returns to the original position. + Inputs: + zpiezo an opened zpiezo.zpiezo instance + push_depth distance to approach, in nm + npoints number points during the approach and during the retreat + freq rate at which data is aquired + Returns the aquired ramp data dictionary, with data in DAC/ADC bits. + """ + # generate the bump output + start_pos = zpiezo.curPos() + pos_dist = zpiezo.pos_nm2out(push_depth) - zpiezo.pos_nm2out(0) + close_pos = start_pos + pos_dist + appr = linspace(start_pos, close_pos, npoints) + retr = linspace(close_pos, start_pos, npoints) + out = concatenate((appr, retr)) + # run the bump, and measure deflection + if config.TEXT_VERBOSE : + print "Bump %g nm" % push_depth + data = zpiezo.ramp(out, freq) + return data + +@splittableKwargsFunction(bump_aquire, + (bump_analyze.bump_save, 'data'), + (bump_analyze.bump_analyze, 'data')) +def bump(**kwargs): + """ + Wrapper around bump_aquire(), bump_save(), bump_analyze() + """ + bump_aquire_kwargs,bump_save_kwargs,bump_analyze_kwargs = \ + bump._splitargs(bump, kwargs) + data = bump_aquire(**bump_aquire_kwargs) + bump_analyze.bump_save(data, **bump_save_kwargs) + photoSensitivity = bump_analyze.bump_analyze(data, **bump_analyze_kwargs) + return photoSensitivity + +# T family. +# Fairly stubby, since a one shot Temp measurement is a common thing. +# We just wrap that to provide a consistent interface. + +@splittableKwargsFunction() +def T_aquire(get_T=None) : + """ + Measure the current temperature of the sample, + or, if get_T == None, fake it by returning config.DEFAULT_TEMP + """ + if get_T == None : + if config.TEXT_VERBOSE : + print "Fake temperature %g" % config.DEFAULT_TEMP + return config.DEFAULT_TEMP + else : + if config.TEXT_VERBOSE : + print "Measure temperature" + return get_T() + +@splittableKwargsFunction(T_aquire, + (T_analyze.T_save, 'T'), + (T_analyze.T_analyze, 'T')) +def T(**kwargs): + """ + Wrapper around T_aquire(), T_save(), T_analyze(), T_plot() + """ + T_aquire_kwargs,T_save_kwargs,T_analyze_kwargs = \ + T._splitargs(T, kwargs) + T_raw = T_aquire(**T_aquire_kwargs) + T_analyze.T_save(T_raw, **T_save_kwargs) + T_ret = T_analyze.T_analyze(T_raw, **T_analyze_kwargs) + return T_ret + +# vib family + +@splittableKwargsFunction() +def vib_aquire(zpiezo, time=1, freq=50e3) : + """ + Record data for TIME seconds at FREQ Hz from ZPIEZO at it's current position. + """ + # round up to the nearest power of two, for efficient FFT-ing + nsamps = ceil_pow_of_two(time*freq) + time = nsamps / freq + # take some data, keeping the position voltage at it's current value + out = ones((nsamps,), dtype=uint16) * zpiezo.curPos() + if config.TEXT_VERBOSE : + print "get %g seconds of data" % time + data = zpiezo.ramp(out, freq) + data['sample frequency Hz'] = freq + return data + +@splittableKwargsFunction(vib_aquire, + (vib_analyze.vib_save, 'data'), + (vib_analyze.vib_analyze, 'deflection_bits', 'freq')) +def vib(**kwargs) : + """ + Wrapper around vib_aquire(), vib_save(), vib_analyze() + """ + vib_aquire_kwargs,vib_save_kwargs,vib_analyze_kwargs = \ + vib._splitargs(vib, kwargs) + data = vib_aquire(freq=freq, **vib_aquire_kwargs) + vib_analyze.vib_save(data, **vib_save_kwargs) + freq = data['sample frequency Hz'] + Vphoto_var = vib_analyze.vib_analyze(deflection_bits=data, freq=freq, + **vib_analyze_kwargs) + return Vphoto_var + +# A few positioning functions, so we can run bump_aquire() and vib_aquire() +# with proper spacing relative to the surface. + +@splittableKwargsFunction() +def move_just_onto_surface(stepper, zpiezo, Depth_nm=100, setpoint=2) : + """ + Uses z_piezo_utils.getSurfPos() to pinpoint the position of the surface. + Adjusts the stepper position as required to get within stepper_tol nm + of the surface. + Then set Vzp to place the cantilever Depth_nm onto the surface. + + If getSurfPos() fails to find the surface, backs off (for safety) + and steps in (without moving the zpiezo) until Vphoto > setpoint. + """ + stepper_tol = 250 # nm, generous estimate of the fullstep stepsize + + if config.TEXT_VERBOSE : + print "moving just onto surface" + # Zero the piezo + if config.TEXT_VERBOSE : + print "zero the z piezo output" + zpiezo.jumpToPos(zpiezo.pos_nm2out(0)) + # See if we're near the surface already + if config.TEXT_VERBOSE : + print "See if we're starting near the surface" + try : + dist = zpiezo.pos_out2nm( \ + z_piezo_utils.getSurfPos(zpiezo, zpiezo.def_V2in(setpoint)) + ) + except (z_piezo_utils.tooClose, z_piezo_utils.poorFit), string : + if config.TEXT_VERBOSE : + print "distance failed with: ", string + print "Back off 200 half steps" + # Back away 200 steps + stepper.step_rel(-400) + stepper.step_rel(200) + sp = zpiezo.def_V2in(setpoint) # sp = setpoint in bits + zpiezo.updateInputs() + cd = zpiezo.curDef() # cd = current deflection in bits + if config.TEXT_VERBOSE : + print "Single stepping approach" + while cd < sp : + if config.TEXT_VERBOSE : + print "deflection %g < setpoint %g. step closer" % (cd, sp) + stepper.step_rel(2) # Full step in + zpiezo.updateInputs() + cd = zpiezo.curDef() + # Back off two steps (protecting against backlash) + if config.TEXT_VERBOSE : + print "Step back 4 half steps to get off the setpoint" + stepper.step_rel(-200) + stepper.step_rel(196) + # get the distance to the surface + zpiezo.updateInputs() + if config.TEXT_VERBOSE : + print "get surf pos, with setpoint %g (%d)" % (setpoint, zpiezo.def_V2in(setpoint)) + for i in range(20) : # HACK, keep stepping back until we get a distance + try : + dist = zpiezo.pos_out2nm(getSurfPos(zpiezo, zpiezo.def_V2in(setpoint))) + except (tooClose, poorFit), string : + stepper.step_rel(-200) + stepper.step_rel(198) + continue + break + if i >= 19 : + print "tried %d times, still too close! bailing" % i + print "probably an invalid setpoint." + raise Exception, "weirdness" + if config.TEXT_VERBOSE : + print 'distance to surface ', dist, ' nm' + # fine tune the stepper position + while dist < -stepper_tol : # step back if we need to + stepper.step_rel(-200) + stepper.step_rel(198) + dist = zpiezo.pos_out2nm(getSurfPos(zpiezo, zpiezo.def_V2in(setpoint))) + if config.TEXT_VERBOSE : + print 'distance to surface ', dist, ' nm, step back' + while dist > stepper_tol : # and step forward if we need to + stepper.step_rel(2) + dist = zpiezo.pos_out2nm(getSurfPos(zpiezo, zpiezo.def_V2in(setpoint))) + if config.TEXT_VERBOSE : + print 'distance to surface ', dist, ' nm, step closer' + # now adjust the zpiezo to place us just onto the surface + target = dist + Depth_nm + zpiezo.jumpToPos(zpiezo.pos_nm2out(target)) + # and we're there :) + if config.TEXT_VERBOSE : + print "We're %g nm into the surface" % Depth_nm + +@splittableKwargsFunction() +def move_far_from_surface(stepper, um_back=50) : + """ + Step back a specified number of microns. + (uses very rough estimate of step distance at the moment) + """ + step_nm = 100 + steps = int(um_back*1000/step_nm) + print "step back %d steps" % steps + stepper.step_rel(-steps) + + +# and finally, the calib family + +@splittableKwargsFunction((move_just_onto_surface, 'stepper', 'zpiezo'), + (bump, 'zpiezo', 'freq', 'log_dir', 'Vphoto_in2V'), + (move_far_from_surface, 'stepper'), + (T, 'log_dir'), + (vib, 'zpiezo', 'log_dir', 'Vphoto_in2V'), + (analyze.calib_save, 'bumps','Ts','vibs','log_dir')) +def calib_aquire(stepper, zpiezo, num_bumps=10, num_Ts=10, num_vibs=20, + bump_freq=100e3, + log_dir=config.LOG_DATA, Vphoto_in2V=config.Vphoto_in2V, + **kwargs): + """ + Aquire data for calibrating a cantilever in one function. + return (bump, T, vib), each of which is an array. + Inputs : + stepper a stepper.stepper_obj for coarse Z positioning + zpiezo a z_piezo.z_piezo for fine positioning and deflection readin + setpoint maximum allowed deflection (in Volts) during approaches + num_bumps number of 'a's (see Outputs) + push_depth_nm depth of each push when generating a + num_temps number of 'T's (see Outputs) + num_vibs number of 'vib's (see Outputs) + log_dir directory to log data to. Default 'None' disables logging. + Outputs (all are arrays of recorded data) : + bumps measured (V_photodiode / nm_tip) proportionality constant + Ts measured temperature (K) + vibs measured V_photodiode variance in free solution + """ + move_just_onto_surface_kwargs,bump_kwargs,move_far_from_surface_kwargs, \ + T_kwargs,vib_kwargs,calib_save_kwargs = \ + calib_aquire._splitargs(calib_aquire, kwargs) + # get bumps + move_just_onto_surface(stepper, zpiezo, **move_just_onto_surface_kwargs) + bumps=zeros((num_bumps,)) + for i in range(num_bumps) : + bumps[i] = bump(zpiezo, freq=bump_freq, log_dir=log_dir, + Vphot_in2V=Vphoto_in2V, **bump_kwargs) + if config.TEXT_VERBOSE : + print bumps + + move_far_from_surface(stepper, **move_far_from_surface_kwargs) + + # get Ts + Ts=zeros((num_Ts,), dtype=float) + for i in range(num_Ts) : + Ts[i] = T(**T_kwargs) + time.sleep(1) # wait a bit to get an independent temperature measure + print Ts + + # get vibs + vibs=zeros((num_vibs,)) + for i in range(num_vibs) : + vibs[i] = vib(zpiezo, log_dir=log_dir, Vphoto_in2V=Vphoto_in2V, + **vib_kwargs) + print vibs + + analyze.calib_save(bumps, Ts, vibs, log_dir, **calib_save_kwargs) + + return (bumps, Ts, vibs) + + +@splittableKwargsFunction( \ + (calib_aquire, 'log_dir'), + (analyze.calib_analyze, 'bumps','Ts','vibs')) +def calib(log_dir=None, **kwargs) : + """ + Calibrate a cantilever in one function. + The I-don't-care-about-the-details black box version :p. + return (k, k_s) + Inputs: + (see calib_aquire()) + Outputs : + k cantilever spring constant (in N/m, or equivalently nN/nm) + k_s standard deviation in our estimate of k + Notes : + See get_calibration_data() for the data aquisition code + See analyze_calibration_data() for the analysis code + """ + calib_aquire_kwargs,calib_analyze_kwargs = \ + calib._splitargs(calib, kwargs) + a, T, vib = calib_aquire(**calib_aquire_kwargs) + k,k_s,ps2_m, ps2_s,T_m,T_s,one_o_Vp2_m,one_o_Vp2_s = \ + analyze.calib_analyze(a, T, vib, log_dir=log_dir, + **calib_analyze_kwargs) + analyze.calib_save_analysis(k, k_s, ps2_m, ps2_s, T_m, T_s, + one_o_Vp2_m, one_o_Vp2_s, log_dir) + return (k, k_s) + + + diff --git a/calibcant/calibrate_cantilever.py b/calibcant/calibrate_cantilever.py deleted file mode 100644 index 6721605..0000000 --- a/calibcant/calibrate_cantilever.py +++ /dev/null @@ -1,911 +0,0 @@ -#!/usr/bin/python - -""" -Aquire and analyze cantilever calibration data. - -W. Trevor King Dec. 2007-Jan. 2008 - -GPL BOILERPLATE - - -The relevent physical quantities are : - Vzp_out Output z-piezo voltage (what we generate) - Vzp Applied z-piezo voltage (after external ZPGAIN) - Zp The z-piezo position - Zcant The cantilever vertical deflection - Vphoto The photodiode vertical deflection voltage (what we measure) - Fcant The force on the cantilever - T The temperature of the cantilever and surrounding solution - (another thing we measure) - k_b Boltzmann's constant - -Which are related by the parameters : - zpGain Vzp_out / Vzp - zpSensitivity Zp / Vzp - photoSensitivity Vphoto / Zcant - k_cant Fcant / Zcant - -Cantilever calibration assumes a pre-calibrated z-piezo (zpSensitivity) and -a amplifier (zpGain). -In our lab, the z-piezo is calibrated by imaging a calibration sample, -which has features with well defined sizes, and the gain is set with a knob -on the Nanoscope. - -photoSensitivity is measured by bumping the cantilever against the surface, -where Zp = Zcant (see the bump_*() family of functions) -The measured slope Vphoto/Vout is converted to photoSensitivity via -Vphoto/Vzp_out * Vzp_out/Vzp * Vzp/Zp * Zp/Zcant = Vphoto/Zcant - (measured) (1/zpGain) (1/zpSensitivity) (1) (photoSensitivity) - -k_cant is measured by watching the cantilever vibrate in free solution -(see the vib_*() family of functions) -The average energy of the cantilever in the vertical direction is given -by the equipartition theorem. - 1/2 k_b T = 1/2 k_cant Zcant**2 - so k_cant = k_b T / Zcant**2 - but Zcant = Vphoto / photoSensitivity - so k_cant = k_b T * photoSensitivty**2 / Vphoto**2 -We measured photoSensitivity above. -We can either measure T using an external function (see temperature.py), -or just estimate it (see the T_*() family of functions). - (room temp ~22 deg C, accurate to +/- 5 deg, so 5/273.15 ~= 2%) -Finally, a time series of Vphoto while we're far from the surface and not -changing Vzp_out will give us Vphoto. - -We do all these measurements a few times to estimate statistical errors. - -The functions are layed out in the families: - bump_*(), vib_*(), T_*(), and calib_*() -where calib_{save|load|analyze}() deal with derived data, not real-world data. -For each family, * can be any of : - aquire get real-world data - save store real-world data to disk - load get real-world data from disk - analyze interperate the real-world data. - plot show a nice graphic to convince people we're working :p - load_analyze_tweaked - read a file with a list of paths to previously saved real world data - load each file using *_load(), analyze using *_analyze(), and - optionally plot using *_plot(). - Intended for re-processing old data. -A family name without any _* extension (e.g. bump()), - runs *_aquire(), *_save(), *_analyze(), *_plot(). - -Also defines the two positioning functions: - move_just_onto_surface() and move_far_from_surface() -""" - -import numpy -import time -import data_logger -import z_piezo_utils -import FFT_tools -import linfit -import GnuplotBiDir # used for fitting lorentzian - -kb = 1.3806504e-23 # Boltzmann's constant in J/K -DEFAULT_TEMP = 22 # assume 22 deg C - -LOG_DATA = True # quietly grab all real-world data and log to LOG_DIR -LOG_DIR = '/home/wking/rsrch/data/calibrate_cantilever' - -GNUFIT_DATA_BASE='./calibrate_cantilever_fitdata' - -TEXT_VERBOSE = True # for debugging -GNUPLOT_VERBOSE = True # turn on fit check plotting -PYLAB_VERBOSE = True # turn on plotting -PYLAB_INTERACTIVE = True # select between draw() and show() for flushing plots -BASE_FIGNUM = 20 # to avoid writing to already existing figures - -# handle extra verbose input modules, only imported if we need them -_flush_plot = None -_final_flush_plot = None -_pylab = None -def _import_pylab() : - "Import pylab plotting functions for when we need to plot" - global _pylab - global _flush_plot - global _final_flush_plot - if _pylab == None : - import pylab as _pylab - if _flush_plot == None : - if PYLAB_INTERACTIVE : - _flush_plot = _pylab.draw - else : - def _flush_plot () : pass - if _final_flush_plot == None : - if PYLAB_INTERACTIVE : - _final_flush_plot = _pylab.draw - else : - _final_flush_plot = _pylab.show - - -# HACK -# make sure you make a system note (add_system_note) if you change these -# in case you don't have access to a z_piezo.z_piezo for conversion functions -_usual_zpGain=20 -_usual_zpSensitivity=7.41 -_usual_zeroVphoto_bits = 2**15 -_usual_zeroVzp_bits = 2**15 -def _usual_Vphoto_in2V(inp) : - return (float(inp)-float(_usual_zeroVphoto_bits))*(10.0/2**15) -def _usual_Vzp_out2V(inp) : - return (float(inp)-float(_usual_zeroVzp_bits))*(10.0/2**15) - -def C_to_K(celsius) : - "Convert Celsius -> Kelvin." - return celsius + 273.15 - -# bump family - -def bump_aquire(zpiezo, push_depth, npoints, freq) : - """ - Ramps closer push_depth and returns to the original position. - Inputs: - zpiezo an opened zpiezo.zpiezo instance - push_depth distance to approach, in nm - npoints number points during the approach and during the retreat - freq rate at which data is aquired - log_dir directory to log data to (see data_logger.py). - None to turn off logging (see also the global LOG_DATA). - Returns the aquired ramp data dictionary, with data in DAC/ADC bits. - """ - # generate the bump output - start_pos = zpiezo.curPos() - pos_dist = zpiezo.pos_nm2out(push_depth) - zpiezo.pos_nm2out(0) - close_pos = start_pos + pos_dist - appr = linspace(start_pos, close_pos, npoints) - retr = linspace(close_pos, start_pos, npoints) - out = concatenate((appr, retr)) - # run the bump, and measure deflection - if TEXT_VERBOSE : - print "Bump %g nm" % push_depth - data = zpiezo.ramp(out, freq) - # default saving, so we have a log in-case the operator is lazy ;) - if LOG_DATA == True : - log = data_logger.data_log(LOG_DIR, noclobber_logsubdir=False, - log_name="bump_surface") - log.write_dict_of_arrays(data) - return data - -def bump_save(data, log_dir) : - "Save the dictionary data, using data_logger.data_log()" - if log_dir != None : - log = data_logger.data_log(log_dir, noclobber_logsubdir=False, - log_name="bump") - log.write_dict_of_arrays(data) - -def bump_load(datafile) : - "Load the dictionary data, using data_logger.date_load()" - dl = data_logger.data_load() - data = dl.read_dict_of_arrays(path) - return data - -def bump_analyze(data, zpGain=_usual_zpGain, - zpSensitivity=_usual_zpSensitivity, - Vzp_out2V=_usual_Vzp_out2V, - Vphoto_in2V=_usual_Vphoto_in2V, - plotVerbose=False) : - """ - Return the slope of the bump ;). - Inputs: - data dictinary of data in DAC/ADC bits - pos_out2V function that converts output DAC bits to Volts - def_in2V funtion that converts input ADC bits to Volts - zpGain zpiezo applied voltage per output Volt - zpSensitivity nm zpiezo response per applied Volt - Returns: - photoSensitivity (Vphoto/Zcant) in Volts/nm - Checks for strong correlation (r-value) and low randomness chance (p-value) - - With the current implementation, the data is regressed in DAC/ADC bits - and THEN converted, so we're assuming that both conversions are LINEAR. - if they aren't, rewrite to convert before the regression. - """ - scale_Vzp_bits2V = Vzp_out2V(1) - Vzp_out2V(0) - scale_Vphoto_bits2V = Vzp_in2V(1) - Vphoto_in2V(0) - Vphoto2Vzp_out_bit, intercept = \ - linfit.linregress(x=data["Z piezo output"], - y=data["Deflection input"], - plotVerbose=plotVerbose) - Vphoto2Vzp_out = Vphoto2Vzp_out * scale_Vphoto_bits2V / scale_Vzp_bits2V - - # 1 / (Vzp/Vzp_out * Zp/Vzp * Zcant/Zp ) - Vzp_out2Zcant = 1.0/ (zpiezo_gain * zpiezo_sensitivity) # * 1 - return Vphoto2Vzp_out * Vzp_out2Zcant - -def bump_plot(data, plotVerbose) : - "Plot the bump (Vphoto vs Vzp) if plotVerbose or PYLAB_VERBOSE == True" - if plotVerbose or PYLAB_VERBOSE : - _import_pylab() - _pylab.figure(BASE_FIGNUM) - _pylab.plot(data["Z piezo output"], data["Deflection input"], - '.', label='bump') - _pylab.title("bump surface") - _pylab.legend(loc='upper left') - _flush_plot() - -def bump(zpiezo, push_depth, npoints=1024, freq=100e3, - log_dir=None, - plotVerbose=False) : - """ - Wrapper around bump_aquire(), bump_save(), bump_analyze(), bump_plot() - """ - data = bump_aquire(zpiezo, push_depth, npoints, freq) - bump_save(data, log_dir) - photoSensitivity = bump_analyze(data, zpiezo.gain, zpiezo.sensitivity, - zpiezo.pos_out2V, zpiezo.def_in2V) - bump_plot(data, plotVerbose) - return photoSensitivity - -def bump_load_analyze_tweaked(tweak_file, zpGain=_usual_zpGain, - zpSensitivity=_usual_zpSensitivity, - Vzp_out2V=_usual_Vzp_out2V, - Vphoto_in2V=_usual_Vphoto_in2V, - plotVerbose=False) : - "Load the output file of tweak_calib_bump.sh, return an array of slopes" - photoSensitivity = [] - for line in file(tweak_file, 'r') : - parsed = line.split() - path = parsed[0].split('\n')[0] - # read the data - full_data = bump_load(path) - if len(parsed) == 1 : - data = full_data # use whole bump - else : - # use the listed sections - zp = [] - df = [] - for rng in parsed[1:] : - p = rng.split(':') - starti = int(p[0]) - stopi = int(p[1]) - zp.extend(full_data['Z piezo output'][starti:stopi]) - df.extend(full_data['Deflection input'][starti:stopi]) - data = {'Z piezo output': array(zp), - 'Deflection input':array(df)} - pSi = bump_analyze(data, zpGain, zpSensitivity, - Vzp_out2V, Vphoto_in2V, plotVerbose) - photoSensitivity.append(pSi) - bump_plot(data, plotVervose) - return array(photoSensitivity, dtype=numpy.float) - -# T family. -# Fairly stubby, since a one shot Temp measurement is a common thing. -# We just wrap that to provide a consistent interface. - -def T_aquire(get_T=None) : - """ - Measure the current temperature of the sample, - or, if get_T == None, fake it by returning DEFAULT_TEMP - """ - if get_T == None : - if TEXT_VERBOSE : - print "Fake temperature %g" % DEFAULT_TEMP - return DEFAULT_TEMP - else : - if TEXT_VERBOSE : - print "Measure temperature" - return get_T() - -def T_save(T, log_dir=None) : - """ - Save either a single T (if you are crazy :p), - or an array of them (more reasonable) - """ - T = numpy.array(T, dtype=numpy.float) - if log_dir != None : - log = data_logger.data_log(log_dir, noclobber_logsubdir=False, - log_name="T_float") - log.write_binary(T.tostring()) - if LOG_DATA != None : - log = data_logger.data_log(LOG_DIR, noclobber_logsubdir=False, - log_name="T_float") - log.write_binary(T.tostring()) - -def T_load(datafile=None) : - """ - Load the saved T array (possibly of length 1), and return it. - If datafile == None, return an array of one DEFAULT_TEMP instead. - """ - if datafile == None : - return numpy.array([DEFAULT_TEMP], dtype=numpy.float) - else : - return fromfile(file=cleanpath, dtype=numpy.float) - -def T_analyze(T, convert_to_K=C_to_K) : - """ - Not much to do here, just convert to Kelvin. - Uses C_to_K (defined above) by default. - """ - try : # if T is an array, convert element by element - for i in range(len(T)) : - T[i] = convert_to_K(T[i]) - except TypeError : - T = convert_to_K(T) - return T - -def T_plot(T, plotVerbose) : - """ - Umm, just print the temperature? - """ - if plotVerbose or PYLAB_VERBOSE or TEXT_VERBOSE : - print "Temperature ", T - -def T(get_T=None, convert_to_K=C_to_K, log_dir=None, plotVerbose=None) : - """ - Wrapper around T_aquire(), T_save(), T_analyze(), T_plot() - """ - T_raw = T_aquire(get_T) - T_save(T_raw, log_dir) - T_ret = T_analyze(T_raw, convert_to_K) - T_plot(T_raw, plotVerbose) - return T_ret - -def T_load_analyze_tweaked(tweak_file=None, convert_to_K=C_to_K, - plotVerbose=False) : - "Read the T files listed in tweak_file, return an array of Ts in Kelvin" - if tweak_file != None : - Tlist=[] - for filepath in file(datafile, 'r') : - cleanpath = filepath.split('\n')[0] - Tlist.extend(T_load(cleanpath)) - Tlist = numpy.array(Tlist, dtype=numpy.float) - T_raw = array(Tlist, dtype=numpy.float) - for Ti in T_raw : - T_plot(T, plotVerbose) - else : - T_raw = T_load(None) - T = T_analyze(T_raw, convert_to_K) - return T - -# vib family - -def vib_aquire(zpiezo, time, freq) : - """ - Record data for TIME seconds at FREQ Hz from ZPIEZO at it's current position. - """ - # round up to the nearest power of two, for efficient FFT-ing - nsamps = ceil_pow_of_two(time*freq) - time = nsamps / freq - # take some data, keeping the position voltage at it's current value - out = ones((nsamps,), dtype=uint16) * zpiezo.curPos() - if TEXT_VERBOSE : - print "get %g seconds of data" % time - data = zpiezo.ramp(out, freq) - if LOG_DATA : - log = data_logger.data_log(LOG_DIR, noclobber_logsubdir=False, - log_name="vib_%gHz" % freq) - log.write_dict_of_arrays(data) - return data - -def vib_save(data, log_dir=None) : - "Save the dictionary data, using data_logger.data_log()" - if log_dir : - log = data_logger.data_log(log_dir, noclobber_logsubdir=False, - log_name="vib_%gHz" % freq) - log.write_dict_of_arrays(data) - -def vib_load(datafile=None) : - "Load the dictionary data, using data_logger.date_load()" - dl = data_logger.data_load() - data = dl.read_dict_of_arrays(path) - return data - -def vib_plot(data, freq, - chunk_size=2048, overlap=True, window=FFT_tools.window_hann, - plotVerbose=True) : - """ - If plotVerbose or PYLAB_VERBOSE == True, plot 3 subfigures: - Time series (Vphoto vs sample index) (show any major drift events), - A histogram of Vphoto, with a gaussian fit (demonstrate randomness), and - FFTed Vphoto data (Vphoto vs Frequency) (show major noise components). - """ - if plotVerbose or PYLAB_VERBOSE : - _import_pylab() - _pylab.hold(False) - _pylab.figure(BASE_FIGNUM+2) - - # plot time series - _pylab.subplot(311) - _pylab.plot(data["Deflection input"], 'r.') - _pylab.title("free oscillation") - - # plot histogram distribution and gaussian fit - _pylab.subplot(312) - n, bins, patches = \ - _pylab.hist(data["Deflection input"], bins=30, - normed=1, align='center') - gaus = numpy.zeros((len(bins),)) - mean = data["Deflection input"].mean() - std = data["Deflection input"].std() - pi = numpy.pi - exp = numpy.exp - for i in range(len(bins)) : - gaus[i] = (2*pi)**(-.5)/std * exp(-0.5*((bins[i]-mean)/std)**2) - _pylab.hold(True) - _pylab.plot(bins, gaus, 'r-'); - _pylab.hold(False) - - # plot FFTed data - _pylab.subplot(313) - AC_data = data["Deflection input"] - mean - (freq_axis, power) = \ - FFT_tools.unitary_avg_power_spectrum(AC_data, freq, chunk_size, - overlap, window) - _pylab.semilogy(freq_axis, power, 'r.-') - _pylab.hold(True) - filtered_power = FFT_tools.windowed_filter(power, - FFT_tools.windowed_median_point, s=5) - _pylab.semilogy(freq_axis, filtered_power, 'b.-') - _flush_plot() - -def vib_analyze_naive(data, Vphoto_in2V=_usual_Vphoto_in2V, - zeroVphoto_bits=_usual_zeroVphoto_bits, - plotVerbose=False) : - """ - Calculate the variance of the raw data, and convert it to Volts**2. - This method is simple and easy to understand, but it highly succeptible - to noise, drift, etc. - - Vphoto_in2V is a function converting Vphoto values from bits to Volts. - This function is assumed linear, see bump_analyze(). - zeroVphoto_bits is the bit value corresponding to Vphoto = zero Volts. - """ - Vphoto_std_bits = data["Deflection input"].std() - Vphoto_std = Vphoto_in2V(Vphoto_std_bits + zpiezo_zeroV_in) - return Vphoto_std**2 - -def vib_analyze(data, freq, minFreq=500, maxFreq=7000, - chunk_size=4096, overlap=True, window=FFT_tools.window_hann, - median_filter_width=5, - Vphoto_in2V=_usual_Vphoto_in2V, plotVerbose=False) : - """ - Calculate the variance in the raw data due to the cantilever's - thermal oscillation and convert it to Volts**2. - - Improves on vib_analyze_naive() by first converting Vphoto(t) to - frequency space, and fitting a Lorentzian in the relevant frequency - range (see cantilever_calib.pdf for derivation). - - The conversion to frequency space generates an average power spectrum - by breaking the data into windowed chunks and averaging the power - spectrums for the chunks together. - See FFT_tools.avg_power_spectrum - - Vphoto_in2V is a function converting Vphoto values from bits to Volts. - This function is assumed linear, see bump_analyze(). - zeroVphoto_bits is the bit value corresponding to Vphoto = zero Volts. - """ - Vphoto_t = numpy.zeros((len(data["Deflection input"]),), - dtype=numpy.float) - # convert the data from bits to volts - if TEXT_VERBOSE : - print "Converting %d bit values to voltages" % len(Vphoto_t) - for i in range(len(data["Deflection input"])) : - Vphoto_t[i] = Vphoto_in2V(data["Deflection input"][i]) - - # Compute the average power spectral density per unit time (in uV**2/Hz) - if TEXT_VERBOSE : - print "Compute the averaged power spectral density in uV**2/Hz" - (freq_axis, power) = \ - FFT_tools.unitary_avg_power_spectrum((Vphoto_t - Vphoto_t.mean())*1e6, - freq, chunk_size,overlap, window) - - # cut out the relevent frequency range - if TEXT_VERBOSE : - print "Cut the frequency range %g to %g Hz" % (minFreq, maxFreq) - imin = 0 - while freq_axis[imin] < minFreq : imin += 1 - imax = imin - while freq_axis[imax] < maxFreq : imax += 1 - assert imax >= imin + 10 , \ - "Less than 10 points in freq range (%g,%g)" % (minFreq, maxFreq) - - # We don't need to filter, because taking data is so cheap... - # median filter the relevent range - #filtered_power = FFT_tools.windowed_filter(power[imin:imax], - # FFT_tools.windowed_median_point, - # s=median_filter_width) - filtered_power = power[imin:imax] - - # check - if (plotVerbose or PYLAB_VERBOSE) and False : - if TEXT_VERBOSE : - print "Plot the FFT data for checking" - vib_plot(data, freq, chunk_size, overlap, window, plotVerbose) - _import_pylab() - _pylab.figure(5) - #_pylab.semilogy(freq_axis, power, 'r.-') - #print "len ", len(freq_axis), len(power), freq_axis.min(), filtered_power.min() - _pylab.semilogy(freq_axis, power, 'r.-', - freq_axis[imin:imax], power[imin:imax], 'b.-', - freq_axis[imin:imax], filtered_power, 'g.-') - _flush_plot() - - # save about-to-be-fitted data to a temporary file - if TEXT_VERBOSE : - print "Save data in temp file for fitting" - datafilename = "%s_%d" % (GNUFIT_DATA_BASE, time.time()) - fd = file(datafilename, 'w') - for i in range(imin, imax) : - fd.write("%g\t%g\n" % (freq_axis[i], filtered_power[i-imin])) - fd.close() - - # generate guesses for Lorentzian parameters A,B,C - if TEXT_VERBOSE : - print "Fit lorentzian" - max_fp_index = numpy.argmin(filtered_power) - max_fp = filtered_power[max_fp_index] - half_max_index = 0 - while filtered_power[half_max_index] < max_fp/2 : - half_max_index += 1 - # Lorentzian L(x) = c / ((a**2-x**2)**2 + (b*x)**2) - # peak centered at a, so - A_guess = freq_axis[max_fp_index+imin] - # Half width w on lower side when L(a-w) = L(a)/2 - # (a**2 - (a-w)**2)**2 + (b*(a-w))**2 = 2*(b*a)**2 - # Let W=(a-w)**2, A=a**2, and B=b**2 - # (A - W)**2 + BW = 2*AB - # W**2 - 2AW + A**2 + BW = 2AB - # W**2 + (B-2A)W + (A**2-2AB) = 0 - # W = (2A-B)/2 * [1 +/- sqrt(1 - 4(A**2-2AB)/(B-2A)**2] - # = (2A-B)/2 * [1 +/- sqrt(1 - 4A(A-2B)/(B-2A)**2] - # (a-w)**2 = (2A-B)/2 * [1 +/- sqrt(1 - 4A(A-2B)/(B-2A)**2] - # so w is a disaster ;) - # For some values of A and B (non-underdamped), W is imaginary or negative. - B_guess = A_guess/2 - # for underdamped, max value at x ~= a, where f(a) = c/(ab)**2, so - C_guess = max_fp * (A_guess*B_guess)**2 - - # fit Lorentzian using Gnuplot's 'fit' command - g = GnuplotBiDir.Gnuplot() - # The Lorentzian is the predicted one-sided power spectral density - # For an oscillator whose position x obeys - # m x'' + gamma x' + kx = F_thermal(t) - # A is the ?driving noise? - # B is gamma, the damping term - # C is omega_0, the resonant frequency - # Fitting the PSD of V = photoSensitivity*x just rescales the parameters - g("f(x) = C / ((A**2-x**2)**2 + (x*B)**2)") - ## The mass m is given by m = k_B T / (pi A) - ## The spring constant k is given by k = m (omega_0)**2 - ## The quality factor Q is given by Q = omega_0 m / gamma - g("A=%g; B=%g; C=%g" % (A_guess, B_guess, C_guess)) - g("fit f(x) '%s' via A,B,C" % datafilename) - A=float(g.getvar('A')) - B=float(g.getvar('B')) - C=float(g.getvar('C')) - if TEXT_VERBOSE : - print "Fitted f(x) = C / ((A**2-x**2)**2 + (x*B)**2) with" - print "A = %g, \t B = %g, \t C = %g" % (A, B, C) - if plotVerbose or GNUPLOT_VERBOSE : - if TEXT_VERBOSE : - print "Fitted f(x) = C / ((A**2-x**2)**2 + (x*B)**2) with" - print "A = %g, \t B = %g, \t C = %g" % (A, B, C) - # write all the ft data now - fd = file(datafilename, 'w') - for i in range(len(freq_axis)) : - fd.write("%g\t%g\n" % (freq_axis[i], power[i])) - fd.write("\n") # blank line to separate fit data. - # write the fit data - for i in range(imin, imax) : - x = freq_axis[i] - fd.write("%g\t%g\n" % (freq_axis[i], - C / ((A**2-x**2)**2 + (x*B)**2) ) ) - fd.close() - print Vphoto_t.var(), A, B, C, ((numpy.pi * C)/(2 * B * A**2) * 1e-12) - g("set terminal epslatex color solid") - g("set output 'calibration.tex") - g("set size 2,2") # multipliers on default 5"x3.5" - #g("set title 'Thermal calibration'") - g("set logscale y") - g("set xlabel 'Frequency (Hz)'") - g("set ylabel 'Power ($\mu$V$^2$/Hz)'") - g("set xrange [0:20000]") - g("g(x) = x < %g || x > %g ? 1/0 : f(x)" % (minFreq, maxFreq)) # only plot on fitted region - g("plot '%s' title 'Vibration data', g(x) title 'Lorentzian fit'" % datafilename) - - g("set mouse") - g("pause mouse 'click with mouse'") - g.getvar("MOUSE_BUTTON") - del(g) - - # Integrating the the power spectral density per unit time (power) over the - # frequency axis [0, fN] returns the total signal power per unit time - # int_0^fN power(f)df = 1/T int_0^T |x(t)**2| dt - # = , the variance for our AC signal. - # The variance from our fitted Lorentzian is the area under the Lorentzian - # = (pi*C) / (2*B*A**2) - # Our A is in uV**2, so convert back to Volts - return (numpy.pi * C) / (2 * B * A**2) * 1e-12 - -def vib(zpiezo, time=1, freq=50e3, log_dir=None, plotVerbose=False) : - """ - Wrapper around vib_aquire(), vib_save(), vib_analyze(), vib_plot() - """ - data = vib_aquire(zpiezo, time, freq) - vib_save(data, log_dir) - Vphoto_var = vib_analyze(data, freq) - vib_plot(data, plotVerbose) - return Vphoto_var - -def vib_load_analyze_tweaked(tweak_file, minFreq=1000, maxFreq=7000, - chunk_size=2048, overlap=True, - window=FFT_tools.window_hann, - median_filter_width=5, - Vphoto_in2V=_usual_Vphoto_in2V, - plotVerbose=False) : - """ - Read the vib files listed in tweak_file. - Return an array of Vphoto variances (due to the cantilever) in Volts**2 - """ - dl = data_logger.data_load() - Vphoto_var = [] - for path in file(tweak_file, 'r') : - if path[-1] == '\n': - path = path.split('\n')[0] - # read the data - data = dl.read_dict_of_arrays(path) - freq = float(path.split('_')[-1].split('Hz')[0]) - if TEXT_VERBOSE and False : - print "Analyzing '%s' at %g Hz" % (path, freq) - # analyze - Vphoto_var.append(vib_analyze(data, freq, minFreq, maxFreq, - chunk_size, overlap, window, - median_filter_width, - Vphoto_in2V, plotVerbose)) - return numpy.array(Vphoto_var, dtype=numpy.float) - - -# A few positioning functions, so we can run bump_aquire() and vib_aquire() -# with proper spacing relative to the surface. - -def move_just_onto_surface(stepper, zpiezo, Depth_nm=100, setpoint=2) : - """ - Uses z_piezo_utils.getSurfPos() to pinpoint the position of the surface. - Adjusts the stepper position as required to get within stepper_tol nm - of the surface. - Then set Vzp to place the cantilever Depth_nm onto the surface. - - If getSurfPos() fails to find the surface, backs off (for safety) - and steps in (without moving the zpiezo) until Vphoto > setpoint. - """ - stepper_tol = 250 # nm, generous estimate of the fullstep stepsize - - if TEXT_VERBOSE : - print "moving just onto surface" - # Zero the piezo - if TEXT_VERBOSE : - print "zero the z piezo output" - zpiezo.jumpToPos(zpiezo.pos_nm2out(0)) - # See if we're near the surface already - if TEXT_VERBOSE : - print "See if we're starting near the surface" - try : - dist = zpiezo.pos_out2nm( \ - z_piezo_utils.getSurfPos(zpiezo, zpiezo.def_V2in(setpoint)) - ) - except (z_piezo_utils.tooClose, z_piezo_utils.poorFit), string : - if TEXT_VERBOSE : - print "distance failed with: ", string - print "Back off 200 half steps" - # Back away 200 steps - stepper.step_rel(-400) - stepper.step_rel(200) - sp = zpiezo.def_V2in(setpoint) # sp = setpoint in bits - zpiezo.updateInputs() - cd = zpiezo.curDef() # cd = current deflection in bits - if TEXT_VERBOSE : - print "Single stepping approach" - while cd < sp : - if TEXT_VERBOSE : - print "deflection %g < setpoint %g. step closer" % (cd, sp) - stepper.step_rel(2) # Full step in - zpiezo.updateInputs() - cd = zpiezo.curDef() - # Back off two steps (protecting against backlash) - if TEXT_VERBOSE : - print "Step back 4 half steps to get off the setpoint" - stepper.step_rel(-200) - stepper.step_rel(196) - # get the distance to the surface - zpiezo.updateInputs() - if TEXT_VERBOSE : - print "get surf pos, with setpoint %g (%d)" % (setpoint, zpiezo.def_V2in(setpoint)) - for i in range(20) : # HACK, keep stepping back until we get a distance - try : - dist = zpiezo.pos_out2nm(getSurfPos(zpiezo, zpiezo.def_V2in(setpoint))) - except (tooClose, poorFit), string : - stepper.step_rel(-200) - stepper.step_rel(198) - continue - break - if i >= 19 : - print "tried %d times, still too close! bailing" % i - print "probably an invalid setpoint." - raise Exception, "weirdness" - if TEXT_VERBOSE : - print 'distance to surface ', dist, ' nm' - # fine tune the stepper position - while dist < -stepper_tol : # step back if we need to - stepper.step_rel(-200) - stepper.step_rel(198) - dist = zpiezo.pos_out2nm(getSurfPos(zpiezo, zpiezo.def_V2in(setpoint))) - if TEXT_VERBOSE : - print 'distance to surface ', dist, ' nm, step back' - while dist > stepper_tol : # and step forward if we need to - stepper.step_rel(2) - dist = zpiezo.pos_out2nm(getSurfPos(zpiezo, zpiezo.def_V2in(setpoint))) - if TEXT_VERBOSE : - print 'distance to surface ', dist, ' nm, step closer' - # now adjust the zpiezo to place us just onto the surface - target = dist + Depth_nm - zpiezo.jumpToPos(zpiezo.pos_nm2out(target)) - # and we're there :) - if TEXT_VERBOSE : - print "We're %g nm into the surface" % Depth_nm - -def move_far_from_surface(stepper, um_back=50) : - """ - Step back a specified number of microns. - (uses very rough estimate of step distance at the moment) - """ - step_nm = 100 - steps = int(um_back*1000/step_nm) - print "step back %d steps" % steps - stepper.step_rel(-steps) - - -# and finally, the calib family - -def calib_aquire(stepper, zpiezo, get_T=None, - approach_setpoint=2, - bump_start_depth=100, bump_push_depth=200, - bump_npoints=1024, bump_freq=100e3, - T_convert_to_K=C_to_K, - vib_time=1, vib_freq=50e3, - num_bumps=10, num_Ts=10, num_vibs=20, - log_dir=None, plotVerbose=False) : - """ - Aquire data for calibrating a cantilever in one function. - return (bump, T, vib), each of which is an array. - Inputs : - stepper a stepper.stepper_obj for coarse Z positioning - zpiezo a z_piezo.z_piezo for fine positioning and deflection readin - setpoint maximum allowed deflection (in Volts) during approaches - num_bumps number of 'a's (see Outputs) - push_depth_nm depth of each push when generating a - num_temps number of 'T's (see Outputs) - num_vibs number of 'vib's (see Outputs) - log_dir directory to log data to. Default 'None' disables logging. - Outputs (all are arrays of recorded data) : - bumps measured (V_photodiode / nm_tip) proportionality constant - Ts measured temperature (K) - vibs measured V_photodiode variance in free solution - """ - # get bumps - move_just_onto_surface(stepper, zpiezo, - bump_start_depth, approach_setpoint) - bumps=zeros((num_bumps,)) - for i in range(num_bumps) : - bumps[i] = bump(zpiezo, bump_push_depth, bump_npoints, bump_freq, - log_dir, plotVerbose) - if TEXT_VERBOSE : - print bumps - - # get Ts - if get_T == None : - get_T = lambda : DEFAULT_TEMP - assert T_convert_to_K == C_to_K, "You didn't define get_T()!" - Ts=zeros((num_Ts,), dtype=float) - for i in range(num_Ts) : - Ts[i] = T(get_T, T_convert_to_K, log_dir, plotVerbose) - time.sleep(1) # wait a bit to get an independent temperature measure - print Ts - - # get vibs - move_far_from_surface(stepper) - vibs=zeros((num_vibs,)) - for i in range(num_vibs) : - vibs[i] = vib(zpiezo, vib_time, vib_freq, log_dir=log_dir) - print vibs - - if LOG_DATA != None : - data = {'bump':bumps, 'T':Ts, 'vib':vibs} - log = data_logger.data_log(LOG_DIR, noclobber_logsubdir=False, - log_name="calib") - log.write_dict_of_arrays(data) - - return (bumps, Ts, vibs) - -def calib_save(bumps, Ts, vibs, log_dir) : - """ - Save a dictonary with the bump, T, and vib data. - """ - if log_dir != None : - data = {'bump':bumps, 'T':Ts, 'vib':vibs} - log = data_logger.data_log(log_dir, noclobber_logsubdir=False, - log_name="calib") - log.write_dict_of_arrays(data) - -def calib_load(datafile) : - "Load the dictionary data, using data_logger.date_load()" - dl = data_logger.data_load() - data = dl.read_dict_of_arrays(path) - return (data['bump'], data['T'], data['vib']) - -def calib_save_analysis(k, k_s, - photoSensitivity2_m, photoSensitivity2_s, - T_m, T_s, one_o_Vphoto2_m, one_o_Vphoto2_s, - log_dir=None) : - if log_dir != None : - log = data_logger.data_log(LOG_DIR, noclobber_logsubdir=False, - log_name="calib_analysis_text") - log.write_binary(( - "k (N/m) : %g +/- %g\n" % (k, k_s) - + "photoSensitivity**2 (V/nm)**2 : %g +/- %g\n" % \ - (photoSensitivity2_m, photoSensitivity2_s) - + "T (K) : %g +/- %g\n" % (T_m, T_s) - + "1/Vp**2 (1/V)**2 : %g +/- %g\n" % \ - (one_o_Vphoto2_m, one_o_Vphoto2_s) - )) - -def calib_plot(bumps, Ts, vibs, plotVerbose) : - if plotVerbose or PYLAB_VERBOSE : - _import_pylab() - _pylab.figure(BASE_FIGNUM+4) - _pylab.subplot(311) - _pylab.plot(bumps, 'g.') - _pylab.title('Photodiode sensitivity (V/nm)') - _pylab.subplot(312) - _pylab.plot(Ts, 'r.') - _pylab.title('Temperature (K)') - _pylab.subplot(313) - _pylab.plot(vibs, 'b.') - _pylab.title('Thermal deflection variance (Volts**2)') - _flush_plot() - -def calib(stepper, zpiezo, tempController=None, - setpoint=2.0, - num_bumps=10, push_depth_nm=300, - num_temps=10, - num_vibs=10, - log_dir=None) : - """ - Calibrate a cantilever in one function. - The I-don't-care-about-the-details black box version :p. - return (k, k_s) - Inputs: - (see calib_aquire()) - Outputs : - k cantilever spring constant (in N/m, or equivalently nN/nm) - k_s standard deviation in our estimate of k - Notes : - See get_calibration_data() for the data aquisition code - See analyze_calibration_data() for the analysis code - """ - a, T, vib = calib_aquire(stepper, zpiezo, tempController, - setpoint, - num_bumps=num_bumps, - push_depth_nm=push_depth_nm, - num_temps=num_temps, - num_vibs=num_vibs, - log_dir=log_dir) - calib_save(a, T, vib, log_dir) - k,k_s,ps2_m, ps2_s,T_m,T_s,one_o_Vp2_m,one_o_Vp2_s = \ - calib_analyze(a, T, vib, log_dir=log_dir) - calib_save_analysis(k,k_s,ps2_m, ps2_s,T_m,T_s,one_o_Vp2_m,one_o_Vp2_s, - log_dir) - calib_plot(a, T, vib, plotVerbose) - return (k, k_s) - -def calib_load_analyze_tweaks(bump_tweaks, vib_tweaks, - T_tweaks=None, - log_dir=None, - plotVerbose=True) : - a = read_tweaked_bumps(bump_tweaks) - vib = V_photo_variance_from_file(vib_tweaks) - return analyze_calibration_data(a, T, vib, log_dir=log_dir) - - diff --git a/calibcant/config.py b/calibcant/config.py index 14af67b..8c03696 100644 --- a/calibcant/config.py +++ b/calibcant/config.py @@ -13,6 +13,12 @@ PYLAB_VERBOSE = True # turn on plotting PYLAB_INTERACTIVE = True # select between draw() and show() for flushing plots BASE_FIGNUM = 20 # to avoid writing to already existing figures + +# HACK +# make sure you make a system note (add_system_note) if you change +# these in case you don't have access to a z_piezo for conversion +# functions + # zpGain zpiezo applied voltage per output Volt zpGain = z_piezo.DEFAULT_GAIN # zpSensitivity nm zpiezo response per applied Volt diff --git a/calibcant/vib_analyze.py b/calibcant/vib_analyze.py index 72c2140..c6bc70f 100755 --- a/calibcant/vib_analyze.py +++ b/calibcant/vib_analyze.py @@ -39,9 +39,12 @@ import common # common module for the calibcant package import config # config module for the calibcant package import data_logger import FFT_tools +from splittable_kwargs import splittableKwargsFunction, \ + make_splittable_kwargs_function PLOT_GUESSED_LORENTZIAN=False +@splittableKwargsFunction() def vib_analyze_naive(deflection_bits, Vphoto_in2V=config.Vphoto_in2V, zeroVphoto_bits=config.zeroVphoto_bits) : """ @@ -55,10 +58,15 @@ def vib_analyze_naive(deflection_bits, Vphoto_in2V=config.Vphoto_in2V, Vphoto_std_bits = deflection_bits.std() Vphoto_std = Vphoto_in2V(Vphoto_std_bits + zeroVphoto_bits) return Vphoto_std**2 - -def vib_analyze(deflection_bits, freq, minFreq=500, maxFreq=7000, + +#@splittableKwargsFunction((fit_psd, 'freq_axis', 'psd_data'), +# (vib_plot, 'deflection_bits', 'freq_axis', 'power', +# 'A', 'B', 'C', 'minFreq', 'maxFreq')) +# Some of the child functions aren't yet defined, so postpone +# make-splittable until later in the module. +def vib_analyze(deflection_bits, freq, chunk_size=4096, overlap=True, window=FFT_tools.window_hann, - Vphoto_in2V=config.Vphoto_in2V, plotVerbose=False) : + Vphoto_in2V=config.Vphoto_in2V, **kwargs) : """ Calculate the variance in the raw data due to the cantilever's thermal oscillation and convert it to Volts**2. @@ -74,6 +82,12 @@ def vib_analyze(deflection_bits, freq, minFreq=500, maxFreq=7000, Vphoto_in2V is a function converting Vphoto values from bits to Volts. This function is assumed linear, see bump_analyze(). """ + fit_psd_kwargs,vib_plot_kwargs = vib_analyze._splitargs(vib_analyze,kwargs) + if 'minFreq' in fit_psd_kwargs: + vib_plot_kwargs['minFreq'] = fit_psd_kwargs['minFreq'] + if 'maxFreq' in fit_psd_kwargs: + vib_plot_kwargs['maxFreq'] = fit_psd_kwargs['maxFreq'] + Vphoto_t = numpy.zeros((len(deflection_bits),), dtype=numpy.float) # convert the data from bits to volts @@ -89,14 +103,13 @@ def vib_analyze(deflection_bits, freq, minFreq=500, maxFreq=7000, FFT_tools.unitary_avg_power_spectrum((Vphoto_t - Vphoto_t.mean())*1e6, freq, chunk_size,overlap, window) - A,B,C = fit_psd(freq_axis, power, minFreq, maxFreq) + A,B,C = fit_psd(freq_axis, power, **kwargs) if config.TEXT_VERBOSE : print "Fitted f(x) = C / ((A**2-x**2)**2 + (x*B)**2) with" print "A = %g, \t B = %g, \t C = %g" % (A, B, C) - vib_plot(deflection_bits, freq_axis, power, A, B, C, - minFreq, maxFreq, plotVerbose=plotVerbose) + vib_plot(deflection_bits, freq_axis, power, A, B, C, **kwargs) # Our A is in uV**2, so convert back to Volts**2 fitted_variance = lorentzian_area(A,B,C) * 1e-12 @@ -109,6 +122,7 @@ def vib_analyze(deflection_bits, freq, minFreq=500, maxFreq=7000, return min(fitted_variance, naive_variance) +@splittableKwargsFunction() def fit_psd(freq_axis, psd_data, minFreq=500, maxFreq=7000) : """ freq_axis : array of frequencies in Hz @@ -250,6 +264,7 @@ def gnuplot_check_fit(datafilename, A, B, C) : string += "plot '%s', f(x)\n" % datafilename return string +@splittableKwargsFunction() def vib_save(data, log_dir=None) : """Save the dictionary data, using data_logger.data_log() data should be a dictionary of arrays with the fields @@ -258,6 +273,7 @@ def vib_save(data, log_dir=None) : 'Deflection input' """ if log_dir : + freq = data['sample frequency Hz'] log = data_logger.data_log(log_dir, noclobber_logsubdir=False, log_name="vib_%gHz" % freq) log.write_dict_of_arrays(data) @@ -273,8 +289,9 @@ def vib_load(datafile=None) : data = dl.read_dict_of_arrays(datafile) return data +@splittableKwargsFunction() def vib_plot(deflection_bits, freq_axis, power, A, B, C, - minFreq=None, maxFreq=None, plotVerbose=True) : + minFreq=None, maxFreq=None, plotVerbose=False) : """ If plotVerbose or config.PYLAB_VERBOSE == True, plot 3 subfigures: Time series (Vphoto vs sample index) (show any major drift events), @@ -360,15 +377,21 @@ def vib_plot(deflection_bits, freq_axis, power, A, B, C, g.getvar("MOUSE_BUTTON") del(g) -def vib_load_analyze_tweaked(tweak_file, minFreq=1000, maxFreq=7000, - chunk_size=2048, overlap=True, - window=FFT_tools.window_hann, - Vphoto_in2V=config.Vphoto_in2V, - plotVerbose=False) : +# Make vib_analyze splittable (was postponed until the child functions +# were defined). +make_splittable_kwargs_function(vib_analyze, + (fit_psd, 'freq_axis', 'psd_data'), + (vib_plot, 'deflection_bits', 'freq_axis', 'power', 'A', 'B', 'C', + 'minFreq', 'maxFreq')) + +@splittableKwargsFunction((vib_analyze, 'deflection_bits', 'freq')) +def vib_load_analyze_tweaked(tweak_file, **kwargs) : """ Read the vib files listed in tweak_file. Return an array of Vphoto variances (due to the cantilever) in Volts**2 """ + vib_analyze_kwargs, = \ + vib_load_analyze_tweaked._splitargs(vib_load_analyze_tweaked, kwargs) dl = data_logger.data_load() Vphoto_var = [] for path in file(tweak_file, 'r') : @@ -381,9 +404,8 @@ def vib_load_analyze_tweaked(tweak_file, minFreq=1000, maxFreq=7000, if config.TEXT_VERBOSE : print "Analyzing '%s' at %g Hz" % (path, freq) # analyze - Vphoto_var.append(vib_analyze(deflection_bits, freq, minFreq, maxFreq, - chunk_size, overlap, window, - Vphoto_in2V, plotVerbose)) + Vphoto_var.append(vib_analyze(deflection_bits, freq, + **vib_analyze_kwargs)) return numpy.array(Vphoto_var, dtype=numpy.float) # commandline interface functions @@ -456,10 +478,6 @@ if __name__ == '__main__' : ofile = file(options.ofilename, 'w') else : ofile = sys.stdout - if options.verbose == True : - vfile = sys.stderr - else : - vfile = None config.TEXT_VERBOSE = options.verbose config.PYLAB_INTERACTIVE = False config.PYLAB_VERBOSE = options.pylab -- 2.26.2