forked from zietzm/Helmholtz_Test_Bench
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 767f6e39f8 | |||
| b3edafa796 | |||
| 9184dd1271 | |||
| 42b9590159 | |||
| a49f98ba63 | |||
| c0fb152efa | |||
| ae71a8d31b | |||
| c138facd59 | |||
| 70fa24f905 | |||
| d0649a1af5 | |||
| c8b3a5e0ba | |||
| ac475e1b81 | |||
| 5662a73345 | |||
| 6194035099 | |||
| 5f161fe09e | |||
| aa9efdba5a | |||
| 0683979b3b | |||
| c5a9c2649b | |||
| 25513cc7c5 | |||
| 9eb95b56bd | |||
| 35a0779b2d | |||
| e9937b1269 | |||
| c110605ce7 | |||
| 7f6244f337 | |||
| 85cad05b6e |
+2
-1
@@ -103,5 +103,6 @@ ENV/
|
||||
config.ini
|
||||
log.csv
|
||||
.idea/misc.xml
|
||||
.idea/misc.xml
|
||||
.idea/Python-PS2000B.iml
|
||||
*.xml
|
||||
*.iml
|
||||
|
||||
+23
-23
@@ -1,23 +1,23 @@
|
||||
Time (s);xField (T);yField (T);zField (T)
|
||||
0,5;0,000015;0,000025;0,00002
|
||||
1;0,0000155;0,0000245;0,0000205
|
||||
1,5;0,000016;0,000024;0,000021
|
||||
2;0,0000165;0,0000235;0,0000215
|
||||
2,5;0,000017;0,000023;0,000022
|
||||
3;0,0000175;0,0000225;0,0000225
|
||||
3,5;0,000018;0,000022;0,000023
|
||||
4;0,0000185;0,0000215;0,0000235
|
||||
4,5;0,000019;0,000021;0,000024
|
||||
5;0,0000195;0,0000205;0,0000245
|
||||
5,5;0,00002;0,00002;0,000025
|
||||
6;0,0000205;0,0000195;0,0000245
|
||||
6,5;0,000021;0,000019;0,000024
|
||||
7;0,0000215;0,0000185;0,0000235
|
||||
7,5;0,000022;0,000018;0,000023
|
||||
8;0,0000225;0,0000175;0,0000225
|
||||
8,5;0,000023;0,000017;0,000022
|
||||
9;0,0000235;0,0000165;0,0000215
|
||||
9,5;0,000024;0,000016;0,000021
|
||||
10;0,0000245;0,0000155;0,0000205
|
||||
10,5;0,000025;0,000015;0,00002
|
||||
11;0,000025;0,000015;0,00002
|
||||
Time [s]],xField [T],yField [T],zField [T],Rotation Rate [deg/s]
|
||||
0.5,0.000015,0.000025,0.00002,1
|
||||
1,0.0000155,0.0000245,0.0000205,1
|
||||
1.5,0.000016,0.000024,0.000021,1
|
||||
2,0.0000165,0.0000235,0.0000215,1
|
||||
2.5,0.000017,0.000023,0.000022,1
|
||||
3,0.0000175,0.0000225,0.0000225,1
|
||||
3.5,0.000018,0.000022,0.000023,1
|
||||
4,0.0000185,0.0000215,0.0000235,1
|
||||
4.5,0.000019,0.000021,0.000024,1
|
||||
5,0.0000195,0.0000205,0.0000245,1
|
||||
5.5,0.00002,0.00002,0.000025,1
|
||||
6,0.0000205,0.0000195,0.0000245,1
|
||||
6.5,0.000021,0.000019,0.000024,1
|
||||
7,0.0000215,0.0000185,0.0000235,1
|
||||
7.5,0.000022,0.000018,0.000023,1
|
||||
8,0.0000225,0.0000175,0.0000225,1
|
||||
8.5,0.000023,0.000017,0.000022,1
|
||||
9,0.0000235,0.0000165,0.0000215,1
|
||||
9.5,0.000024,0.000016,0.000021,1
|
||||
10,0.0000245,0.0000155,0.0000205,1
|
||||
10.5,0.000025,0.000015,0.00002,1
|
||||
11,0.000025,0.000015,0.00002,1
|
||||
|
||||
|
+15
-15
@@ -1,15 +1,15 @@
|
||||
Time (s);xField (T);yField (T);zField (T)
|
||||
0;0,0000145;0,0000255;0,0000195
|
||||
0,1;-0,000015;0,000025;0,00002
|
||||
0,2;-0,0000155;-0,0000245;0,0000205
|
||||
0,3;-0,000016;-0,000024;-0,000021
|
||||
0,4;0,0000165;-0,0000235;-0,0000215
|
||||
0,5;0,000017;0,000023;-0,000022
|
||||
0,6;0,0000175;0,0000225;0,0000225
|
||||
0,7;-0,000018;-0,000022;0,000023
|
||||
0,8;0,0000185;-0,0000215;-0,0000235
|
||||
0,9;-0,000019;0,000021;-0,000024
|
||||
1;-0,0000195;-0,0000205;-0,0000245
|
||||
1,1;0,00002;0,00002;0,000025
|
||||
1,2;-0,0000205;-0,0000195;-0,0000245
|
||||
1,3;0,000021;0,000019;0,000024
|
||||
Time [s]],xField [T],yField [T],zField [T],Rotation Rate [deg/s]
|
||||
0,0.0000145,0.0000255,0.0000195,1
|
||||
0.1,-0.000015,0.000025,0.00002,1
|
||||
0.2,-0.0000155,-0.0000245,0.0000205,1
|
||||
0.3,-0.000016,-0.000024,-0.000021,1
|
||||
0.4,0.0000165,-0.0000235,-0.0000215,1
|
||||
0.5,0.000017,0.000023,-0.000022,1
|
||||
0.6,0.0000175,0.0000225,0.0000225,1
|
||||
0.7,-0.000018,-0.000022,0.000023,1
|
||||
0.8,0.0000185,-0.0000215,-0.0000235,1
|
||||
0.9,-0.000019,0.000021,-0.000024,1
|
||||
1,-0.0000195,-0.0000205,-0.0000245,1
|
||||
1.1,0.00002,0.00002,0.000025,1
|
||||
1.2,-0.0000205,-0.0000195,-0.0000245,1
|
||||
1.3,0.000021,0.000019,0.000024,1
|
||||
|
||||
|
+15
-15
@@ -1,15 +1,15 @@
|
||||
Time (s);xField (T);yField (T);zField (T)
|
||||
0;0,0000145;0,0000255;0,0000195
|
||||
1;-0,000015;0,000025;0,00002
|
||||
2;-0,0000155;-0,0000245;0,0000205
|
||||
3;-0,000016;-0,000024;-0,000021
|
||||
4;0,0000165;-0,0000235;-0,0000215
|
||||
5;0,000017;0,000023;-0,000022
|
||||
6;0,0000175;0,0000225;0,0000225
|
||||
7;-0,000018;-0,000022;0,000023
|
||||
8;0,0000185;-0,0000215;-0,0000235
|
||||
9;-0,000019;0,000021;-0,000024
|
||||
10;-0,0000195;-0,0000205;-0,0000245
|
||||
11;0,00002;0,00002;0,000025
|
||||
12;-0,0000205;-0,0000195;-0,0000245
|
||||
13;0,000021;0,000019;0,000024
|
||||
Time [s]],xField [T],yField [T],zField [T],Rotation Rate [deg/s]
|
||||
0,0.0000145,0.0000255,0.0000195,1
|
||||
1,-0.000015,0.000025,0.00002,1
|
||||
2,-0.0000155,-0.0000245,0.0000205,1
|
||||
3,-0.000016,-0.000024,-0.000021,1
|
||||
4,0.0000165,-0.0000235,-0.0000215,1
|
||||
5,0.000017,0.000023,-0.000022,1
|
||||
6,0.0000175,0.0000225,0.0000225,1
|
||||
7,-0.000018,-0.000022,0.000023,1
|
||||
8,0.0000185,-0.0000215,-0.0000235,1
|
||||
9,-0.000019,0.000021,-0.000024,1
|
||||
10,-0.0000195,-0.0000205,-0.0000245,1
|
||||
11,0.00002,0.00002,0.000025,1
|
||||
12,-0.0000205,-0.0000195,-0.0000245,1
|
||||
13,0.000021,0.000019,0.000024,1
|
||||
|
||||
|
@@ -1,12 +1,12 @@
|
||||
Time (s);xField (T);yField (T);zField (T);
|
||||
0;0,00015;-0,00015;0,00002;150
|
||||
1;0,00017;-0,00017;0,00002;170
|
||||
2;0,00018;-0,00018;0,00002;180
|
||||
3;0,00019;-0,00019;0,00002;190
|
||||
4;0,0002;-0,0002;0,00002;200
|
||||
5;0,00021;0,00021;0,00002;210
|
||||
6;0,00022;-0,00022;0,00002;220
|
||||
7;0,0002;-0,0002;0,00002;200
|
||||
8;0,00018;-0,00018;0,00002;180
|
||||
9;0,00005;-0,00005;0,00002;50
|
||||
10;-0,00004;0,00004;0,00002;-40
|
||||
Time [s]],xField [T],yField [T],zField [T],Rotation Rate [deg/s]
|
||||
0,0.00015,-0.00015,0.00002,1
|
||||
1,0.00017,-0.00017,0.00002,1
|
||||
2,0.00018,-0.00018,0.00002,1
|
||||
3,0.00019,-0.00019,0.00002,1
|
||||
4,0.0002,-0.0002,0.00002,1
|
||||
5,0.00021,0.00021,0.00002,1
|
||||
6,0.00022,-0.00022,0.00002,1
|
||||
7,0.0002,-0.0002,0.00002,1
|
||||
8,0.00018,-0.00018,0.00002,1
|
||||
9,0.00005,-0.00005,0.00002,1
|
||||
10,-0.00004,0.00004,0.00002,1
|
||||
|
||||
|
@@ -0,0 +1,181 @@
|
||||
"Time [s]]","xField [T]","yField [T]","zField [T]","Rotation Rate [deg/s]"
|
||||
0.0,0.0,-1.7452406437283511e-06,9.998476951563913e-05,1.0
|
||||
1.0,0.0,-3.4883798303794917e-06,9.993913750958131e-05,0.9991
|
||||
2.0,0.0,-5.22923827596647e-06,9.986318173908324e-05,0.9984
|
||||
3.0,0.0,-6.967638394438434e-06,9.97569647453467e-05,0.9979
|
||||
4.0,0.0,-8.703403395519831e-06,9.962053387397024e-05,0.9976
|
||||
5.0,0.0,-1.0436356385678047e-05,9.945392131731718e-05,0.9974999999999999
|
||||
6.0,0.0,-1.2166319471804114e-05,9.925714417869374e-05,0.9976
|
||||
7.0,0.0,-1.3893112867193057e-05,9.903020455830349e-05,0.9979
|
||||
8.0,0.0,-1.561655399941571e-05,9.877308966101715e-05,0.9984
|
||||
9.0,0.0,-1.733645661968247e-05,9.848577192606996e-05,0.9991
|
||||
10.0,0.0,-1.9052629913311656e-05,9.816820917887003e-05,1.0
|
||||
11.0,0.0,-2.076487761093051e-05,9.782034480517372e-05,1.0011
|
||||
12.0,0.0,-2.247299710005592e-05,9.744210794795481e-05,1.0024
|
||||
13.0,0.0,-2.4176778536724306e-05,9.703341372736604e-05,1.0039
|
||||
14.0,0.0,-2.5876003956866184e-05,9.659416348426153e-05,1.0056
|
||||
15.0,0.0,-2.757044638715028e-05,9.612424504781959e-05,1.0075
|
||||
16.0,0.0,-2.9259868955055523e-05,9.562353302787435e-05,1.0096
|
||||
17.0,0.0,-3.094402399796594e-05,9.50918891326348e-05,1.0119
|
||||
18.0,0.0,-3.262265217112453e-05,9.452916251253802e-05,1.0144
|
||||
19.0,0.0,-3.429548155432671e-05,9.39351901310517e-05,1.0171
|
||||
20.0,0.0,-3.596222675728274e-05,9.330979716330852e-05,1.02
|
||||
21.0,0.0,-3.762258802363148e-05,9.265279742352143e-05,1.0231
|
||||
22.0,0.0,-3.927625033364511e-05,9.196399382219565e-05,1.0264
|
||||
23.0,0.0,-4.092288250572579e-05,9.12431788542166e-05,1.0299
|
||||
24.0,0.0,-4.256213629686185e-05,9.049013511895843e-05,1.0336
|
||||
25.0,0.0,-4.419364550228159e-05,8.970463587361951e-05,1.0375
|
||||
26.0,0.0,-4.581702505461894e-05,8.888644562105304e-05,1.0416
|
||||
27.0,0.0,-4.743187012298561e-05,8.803532073342054e-05,1.0459
|
||||
28.0,0.0,-4.903775521243089e-05,8.715101011305436e-05,1.0504
|
||||
29.0,0.0,-5.063423326436127e-05,8.623325589197157e-05,1.0551
|
||||
30.0,0.0,-5.222083475858915e-05,8.528179417153538e-05,1.06
|
||||
31.0,0.0,-5.379706681778277e-05,8.429635580381285e-05,1.0651
|
||||
32.0,0.0,-5.536241231519777e-05,8.327666721622591e-05,1.0704
|
||||
33.0,0.0,-5.691632898668582e-05,8.222245128113941e-05,1.0759
|
||||
34.0,0.0,-5.84582485480968e-05,8.113342823207298e-05,1.0816
|
||||
35.0,0.0,-5.9987575819318076e-05,8.000931662826251e-05,1.0875
|
||||
36.0,0.0,-6.150368785632916e-05,7.884983436933288e-05,1.0936
|
||||
37.0,0.0,-6.300593309279031e-05,7.765469976187423e-05,1.0998999999999999
|
||||
38.0,0.0,-6.449363049283228e-05,7.642363263974053e-05,1.1064
|
||||
39.0,0.0,-6.596606871686862e-05,7.515635553991005e-05,1.1131
|
||||
40.0,0.0,-6.74225053024151e-05,7.385259493576245e-05,1.1199999999999999
|
||||
41.0,0.0,-6.886216586206999e-05,7.251208252963613e-05,1.1271
|
||||
42.0,0.0,-7.028424330098631e-05,7.113455660653098e-05,1.1343999999999999
|
||||
43.0,0.0,-7.168789705635229e-05,6.971976345081669e-05,1.1419
|
||||
44.0,0.0,-7.307225236158857e-05,6.826745882779226e-05,1.1496
|
||||
45.0,0.0,-7.443639953817088e-05,6.677740953192026e-05,1.1575
|
||||
46.0,0.0,-7.57793933181956e-05,6.524939500352636e-05,1.1656
|
||||
47.0,0.0,-7.71002522010209e-05,6.368320901571287e-05,1.1739
|
||||
48.0,0.0,-7.839795784753942e-05,6.207866143318036e-05,1.1824
|
||||
49.0,0.0,-7.967145451587092e-05,6.043558004458558e-05,1.1911
|
||||
50.0,0.0,-8.091964854250044e-05,5.875381246998535e-05,1.2
|
||||
51.0,0.0,-8.214140787313431e-05,5.703322814482264e-05,1.2090999999999998
|
||||
52.0,0.0,-8.333556164779959e-05,5.5273720381803435e-05,1.2184
|
||||
53.0,0.0,-8.45008998449718e-05,5.347520851188933e-05,1.2279
|
||||
54.0,0.0,-8.563617298978239e-05,5.163764010548956e-05,1.2376
|
||||
55.0,0.0,-8.674009193162976e-05,4.976099327477715e-05,1.2475
|
||||
56.0,0.0,-8.781132769679547e-05,4.784527905787572e-05,1.2576
|
||||
57.0,0.0,-8.884851142194952e-05,4.589054388546408e-05,1.2679
|
||||
58.0,0.0,-8.985023437471637e-05,4.38968721301249e-05,1.2784
|
||||
59.0,0.0,-9.081504806776283e-05,4.186438873852005e-05,1.2891
|
||||
60.0,0.0,-9.174146447316287e-05,3.979326194620631e-05,1.3
|
||||
61.0,0.0,-9.262795634408807e-05,3.7683706074611496e-05,1.3111000000000002
|
||||
62.0,0.0,-9.347295765116815e-05,3.5535984409369075e-05,1.3224
|
||||
63.0,0.0,-9.427486414115998e-05,3.335041215885984e-05,1.3339
|
||||
64.0,0.0,-9.503203402585546e-05,3.112735949142892e-05,1.3456
|
||||
65.0,0.0,-9.574278880944718e-05,2.8867254649335716e-05,1.3575000000000002
|
||||
66.0,0.0,-9.640541426285387e-05,2.6570587137049264e-05,1.3696
|
||||
67.0,0.0,-9.701816155378307e-05,2.42379109810241e-05,1.3819000000000001
|
||||
68.0,0.0,-9.75792485415751e-05,2.1869848057577132e-05,1.3944
|
||||
69.0,0.0,-9.808686124612794e-05,1.946709148493532e-05,1.4071
|
||||
70.0,0.0,-9.85391555004425e-05,1.7030409074934477e-05,1.42
|
||||
71.0,0.0,-9.893425879655386e-05,1.4560646839220595e-05,1.4331
|
||||
72.0,0.0,-9.927027233481838e-05,1.2058732544135474e-05,1.4464
|
||||
73.0,0.0,-9.954527328671002e-05,9.525679307756304e-06,1.4599000000000002
|
||||
74.0,0.0,-9.975731728143635e-05,6.962589231804475e-06,1.4736
|
||||
75.0,0.0,-9.990444112681327e-05,4.370657060339902e-06,1.4875
|
||||
76.0,0.0,-9.998466577493252e-05,1.7511738563136905e-06,1.5016
|
||||
77.0,0.0,-9.999599954321475e-05,-8.944693138367792e-07,1.5159
|
||||
78.0,0.0,-9.993644160145834e-05,-3.5647776983010223e-06,1.5304000000000002
|
||||
79.0,0.0,-9.980398573546578e-05,-6.258149192448348e-06,1.5451000000000001
|
||||
80.0,0.0,-9.95966243977505e-05,-8.972870698578612e-06,1.56
|
||||
81.0,0.0,-9.931235305569338e-05,-1.1707114526701726e-05,1.5751
|
||||
82.0,0.0,-9.894917484732254e-05,-1.4458934851294935e-05,1.5904
|
||||
83.0,0.0,-9.850510555462879e-05,-1.722626424014905e-05,1.6059
|
||||
84.0,0.0,-9.797817890399535e-05,-2.000691027262028e-05,1.6216
|
||||
85.0,0.0,-9.736645220290827e-05,-2.279855226585184e-05,1.6375000000000002
|
||||
86.0,0.0,-9.666801232161705e-05,-2.5598738128815886e-05,1.6536
|
||||
87.0,0.0,-9.58809820278266e-05,-2.8404881365349675e-05,1.6699000000000002
|
||||
88.0,0.0,-9.500352668181514e-05,-3.121425824871764e-05,1.6864000000000001
|
||||
89.0,0.0,-9.403386129858073e-05,-3.4024005191618475e-05,1.7031
|
||||
90.0,0.0,-9.297025798271502e-05,-3.683111633696989e-05,1.7200000000000002
|
||||
91.0,0.0,-9.181105374067828e-05,-3.963244139623856e-05,1.7371
|
||||
92.0,0.0,-9.055465867399797e-05,-4.242468376353232e-05,1.7544
|
||||
93.0,0.0,-8.919956455562706e-05,-4.5204398935131514e-05,1.7719
|
||||
94.0,0.0,-8.774435379026785e-05,-4.796799326559646e-05,1.7896
|
||||
95.0,0.0,-8.618770875788803e-05,-5.071172309304299e-05,1.8075
|
||||
96.0,0.0,-8.452842153791733e-05,-5.3431694267617555e-05,1.8256000000000001
|
||||
97.0,0.0,-8.276540400970972e-05,-5.6123862118616924e-05,1.8439
|
||||
98.0,0.0,-8.08976983227803e-05,-5.878403189707582e-05,1.8624
|
||||
99.0,0.0,-7.892448772806883e-05,-6.140785973197496e-05,1.8811
|
||||
100.0,0.0,-7.684510775903972e-05,-6.399085413949068e-05,1.9
|
||||
101.0,0.0,-7.46590577487918e-05,-6.652837812589881e-05,1.9191
|
||||
102.0,0.0,-7.236601266651652e-05,-6.901565192584626e-05,1.9384000000000001
|
||||
103.0,0.0,-6.996583525360449e-05,-7.144775641869711e-05,1.9579
|
||||
104.0,0.0,-6.745858843645298e-05,-7.381963726652448e-05,1.9776000000000002
|
||||
105.0,0.0,-6.484454798956871e-05,-7.612610981804154e-05,1.9975
|
||||
106.0,0.0,-6.212421541888612e-05,-7.836186482331717e-05,2.0176000000000003
|
||||
107.0,0.0,-5.929833103133426e-05,-8.052147500448747e-05,2.0379
|
||||
108.0,0.0,-5.6367887152580006e-05,-8.259940252782707e-05,2.0584000000000002
|
||||
109.0,0.0,-5.333414145055902e-05,-8.459000742245954e-05,2.0791000000000004
|
||||
110.0,0.0,-5.019863031787675e-05,-8.648755699063967e-05,2.1
|
||||
111.0,0.0,-4.696318226142989e-05,-8.828623625390153e-05,2.1211
|
||||
112.0,0.0,-4.36299312426667e-05,-8.998015947841043e-05,2.1424
|
||||
113.0,0.0,-4.0201329906786734e-05,-9.156338282154988e-05,2.1639
|
||||
114.0,0.0,-3.6680162633885195e-05,-9.302991814008938e-05,2.1856
|
||||
115.0,0.0,-3.3069558339591776e-05,-9.437374799818187e-05,2.2075
|
||||
116.0,0.0,-2.9373002947155928e-05,-9.558884191089653e-05,2.2296
|
||||
117.0,0.0,-2.559435144721021e-05,-9.666917385597483e-05,2.2519
|
||||
118.0,0.0,-2.1737839455627395e-05,-9.760874108296534e-05,2.2744
|
||||
119.0,0.0,-1.7808094174002536e-05,-9.84015842448172e-05,2.2971000000000004
|
||||
120.0,0.0,-1.3810144651370471e-05,-9.904180887235564e-05,2.3200000000000003
|
||||
121.0,0.0,-9.749431239851467e-06,-9.952360820679386e-05,2.3431
|
||||
122.0,0.0,-5.631814131040329e-06,-9.984128739951931e-05,2.3664
|
||||
123.0,0.0,-1.4635808541675665e-06,-9.998928908179772e-05,2.3899
|
||||
124.0,0.0,2.7485473885872958e-06,-9.996222029973469e-05,2.4136
|
||||
125.0,0.0,6.997410679702419e-06,-9.975488080179127e-05,2.4375
|
||||
126.0,0.0,1.1275406772107576e-05,-9.936229265734316e-05,2.4616000000000002
|
||||
127.0,0.0,1.5574489043303514e-05,-9.87797311751759e-05,2.4859
|
||||
128.0,0.0,1.98861661624192e-05,-9.800275708040388e-05,2.5104
|
||||
129.0,0.0,2.4201503618818632e-05,-9.702724989706916e-05,2.5351
|
||||
130.0,0.0,2.851112726430912e-05,-9.584944247160945e-05,2.56
|
||||
131.0,0.0,3.2805229023794614e-05,-9.446595655947376e-05,2.5851
|
||||
132.0,0.0,3.707357493126433e-05,-9.287383938341263e-05,2.6104000000000003
|
||||
133.0,0.0,4.1305515649210165e-05,-9.107060105738215e-05,2.6359000000000004
|
||||
134.0,0.0,4.548999962981594e-05,-8.905425275459531e-05,2.6616
|
||||
135.0,0.0,4.9615589075450825e-05,-8.682334548205341e-05,2.6875
|
||||
136.0,0.0,5.3670478854007505e-05,-8.43770093069287e-05,2.7136
|
||||
137.0,0.0,5.764251852133727e-05,-8.171499286249302e-05,2.7399
|
||||
138.0,0.0,6.151923759832183e-05,-7.883770294295253e-05,2.7664
|
||||
139.0,0.0,6.528787424386095e-05,-7.574624397762439e-05,2.7931
|
||||
140.0,0.0,6.893540745711695e-05,-7.24424571554642e-05,2.8200000000000003
|
||||
141.0,0.0,7.244859293261136e-05,-6.8928958951117e-05,2.8471
|
||||
142.0,0.0,7.58140026800881e-05,-6.52091787835393e-05,2.8744
|
||||
143.0,0.0,7.901806850731089e-05,-6.128739551795232e-05,2.9019000000000004
|
||||
144.0,0.0,8.204712944802844e-05,-5.716877250158922e-05,2.9295999999999998
|
||||
145.0,0.0,8.488748319909571e-05,-5.2859390803557724e-05,2.9575
|
||||
146.0,0.0,8.752544161007193e-05,-4.836628030934244e-05,2.9856000000000003
|
||||
147.0,0.0,8.994739024541854e-05,-4.369744830122675e-05,3.0139000000000005
|
||||
148.0,0.0,9.213985201359944e-05,-3.886190513744783e-05,3.0423999999999998
|
||||
149.0,0.0,9.408955482885929e-05,-3.386968662546322e-05,3.0711
|
||||
150.0,0.0,9.578350324015606e-05,-2.873187266857161e-05,3.1
|
||||
151.0,0.0,9.720905392760702e-05,-2.3460601750585723e-05,3.1291
|
||||
152.0,0.0,9.835399492984207e-05,-1.8069080810616793e-05,3.1584
|
||||
153.0,0.0,9.920662842584688e-05,-1.2571590049629674e-05,3.1879
|
||||
154.0,0.0,9.975585685224551e-05,-6.9834822026192765e-06,3.2176
|
||||
155.0,0.0,9.999127209157403e-05,-1.3211758054129605e-06,3.2475000000000005
|
||||
156.0,0.0,9.990324741902882e-05,4.397858016388089e-06,3.2776
|
||||
157.0,0.0,9.948303184456377e-05,1.0155115706554476e-05,3.3079
|
||||
158.0,0.0,9.87228464342335e-05,1.5931088849251602e-05,3.3384
|
||||
159.0,0.0,9.761598213955617e-05,2.170529960470135e-05,3.3691000000000004
|
||||
160.0,0.0,9.615689860666532e-05,2.7456344446183743e-05,3.4
|
||||
161.0,0.0,9.434132337846043e-05,3.31619466135623e-05,3.4311000000000003
|
||||
162.0,0.0,9.216635084322934e-05,3.879901767110548e-05,3.4624
|
||||
163.0,0.0,8.963054022274018e-05,4.434372852365659e-05,3.4939
|
||||
164.0,0.0,8.67340118320907e-05,4.9771590204761896e-05,3.5256
|
||||
165.0,0.0,8.347854078322675e-05,5.5057544702821115e-05,3.5575
|
||||
166.0,0.0,7.986764724463343e-05,6.017606603631379e-05,3.5896000000000003
|
||||
167.0,0.0,7.590668231196969e-05,6.510127172636267e-05,3.6219
|
||||
168.0,0.0,7.160290848913815e-05,6.980704474403788e-05,3.6544
|
||||
169.0,0.0,6.696557372729566e-05,7.426716593067309e-05,3.6871
|
||||
170.0,0.0,6.200597792154295e-05,7.845545680188934e-05,3.72
|
||||
171.0,0.0,5.673753072246099e-05,8.234593254993105e-05,3.7531
|
||||
172.0,0.0,5.117579948333757e-05,8.591296495431425e-05,3.7864
|
||||
173.0,0.0,4.533854613496372e-05,8.913145479777476e-05,3.8199
|
||||
174.0,0.0,3.924575175943646e-05,9.197701326329688e-05,3.8536
|
||||
175.0,0.0,3.2919627623696125e-05,9.442615165894024e-05,3.8875
|
||||
176.0,0.0,2.6384611433804805e-05,9.645647868073527e-05,3.9216000000000006
|
||||
177.0,0.0,1.9667347583515437e-05,9.804690428070224e-05,3.9559
|
||||
178.0,0.0,1.2796650196782336e-05,9.917784905784754e-05,3.9904
|
||||
179.0,0.0,5.803447804817374e-06,9.98314579357467e-05,4.0251
|
||||
|
+1
-1
@@ -2,7 +2,7 @@ appdirs==1.4.4
|
||||
cycler==0.10.0
|
||||
future==0.18.2
|
||||
kiwisolver==1.3.2
|
||||
matplotlib==3.3.4
|
||||
matplotlib==3.7.0
|
||||
# numpy==1.19.3 ## do not include versioning to avoid versioning conflict
|
||||
pandas==1.1.5
|
||||
Pillow==8.4.0
|
||||
|
||||
+59
-103
@@ -3,10 +3,12 @@ import time
|
||||
from datetime import datetime
|
||||
from threading import Thread
|
||||
import numpy as np
|
||||
from numpy.lib.scimath import sqrt as csqrt
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||
from tkinter import LabelFrame
|
||||
import scipy.optimize
|
||||
from scipy import linalg as linalg_scipy
|
||||
|
||||
from src.utility import ui_print
|
||||
from src.exceptions import DeviceBusy, DeviceAccessError
|
||||
@@ -240,13 +242,14 @@ class CoilConstantCalibration(Thread):
|
||||
|
||||
class MagnetometerCalibrationSimple(Thread):
|
||||
|
||||
def __init__(self, view_queue, calibration_points, calibration_interval, calibration_mag_field,
|
||||
def __init__(self, view_queue, calibration_points, calibration_interval, compensated_field, calibration_mag_field,
|
||||
mgm_to_helmholtz_cos_trans):
|
||||
Thread.__init__(self)
|
||||
self.view_queue = view_queue
|
||||
self.calibration_points = calibration_points
|
||||
self.calibration_mag_field = calibration_mag_field
|
||||
self.calibration_interval = calibration_interval
|
||||
self.compensated_field = compensated_field
|
||||
self.matrix_trans_mgm_to_hh = [[x.get() for x in row] for row in mgm_to_helmholtz_cos_trans]
|
||||
|
||||
# Hardware checks are done in the init method to allow for exception handling in main thread
|
||||
@@ -307,7 +310,10 @@ class MagnetometerCalibrationSimple(Thread):
|
||||
for vec_idx, test_vec in enumerate(test_vectors):
|
||||
# Command output
|
||||
applied_vec = test_vec * self.calibration_mag_field
|
||||
self.cage_dev.set_field_raw(applied_vec)
|
||||
if not self.compensated_field:
|
||||
self.cage_dev.set_field_raw(applied_vec) # Set raw field not compensated field
|
||||
else:
|
||||
self.cage_dev.set_field_compensated(applied_vec) # Set compensated field not raw field
|
||||
|
||||
# Sleep for a certain duration to allow psu to stabilize output and magnetometer to supply readings
|
||||
time.sleep(self.calibration_interval)
|
||||
@@ -408,12 +414,14 @@ class MagnetometerCalibrationSimple(Thread):
|
||||
class MagnetometerCalibrationComplete(Thread):
|
||||
|
||||
def __init__(self, view_queue, calibration_points, calibration_interval, calibration_mag_field,
|
||||
mgm_to_helmholtz_cos_trans, right_column):
|
||||
calibration_oversampling, compensated_field, mgm_to_helmholtz_cos_trans, right_column):
|
||||
Thread.__init__(self)
|
||||
self.view_queue = view_queue
|
||||
self.calibration_points = calibration_points
|
||||
self.calibration_interval = calibration_interval
|
||||
self.calibration_mag_field = calibration_mag_field
|
||||
self.var_oversampling = calibration_oversampling
|
||||
self.compensated_field = compensated_field
|
||||
self.matrix_trans_mgm_to_hh = [[x.get() for x in row] for row in mgm_to_helmholtz_cos_trans]
|
||||
self.right_column = right_column
|
||||
|
||||
@@ -434,9 +442,8 @@ class MagnetometerCalibrationComplete(Thread):
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
raw_data = self.calibration_procedure()
|
||||
self.calibration_procedure()
|
||||
self.put_message('finished', None)
|
||||
return raw_data
|
||||
except Exception as e:
|
||||
self.put_message('failed', e)
|
||||
finally:
|
||||
@@ -450,19 +457,21 @@ class MagnetometerCalibrationComplete(Thread):
|
||||
# This contains the raw experiment data for exporting
|
||||
# Each row is a dict containing the applied vector and measured vector
|
||||
raw_data = []
|
||||
self.var_oversampling = 5
|
||||
|
||||
# Find sensor offsets. They must be found prior to applying the chosen calibration algorithm
|
||||
# This will be accurate if the cage was recently calibrated
|
||||
self.cage_dev.set_field_compensated([0, 0, 0])
|
||||
# Sleep for a certain duration to allow psu to stabilize output and magnetometer to supply readings
|
||||
time.sleep(self.calibration_interval)
|
||||
# The offsets can easily be read from the magnetometer
|
||||
offsets = g.MAGNETOMETER.field
|
||||
# Save data point to raw_data list
|
||||
raw_data.append({'applied_x': 0, 'applied_y': 0, 'applied_z': 0,
|
||||
'measured_x': offsets[0], 'measured_y': offsets[1], 'measured_z': offsets[2]})
|
||||
# Set new progress indicator for UI
|
||||
self.set_progress(True, 0)
|
||||
for i in range(self.var_oversampling):
|
||||
# Find sensor offsets. They must be found prior to applying the chosen calibration algorithm
|
||||
# This will be accurate if the cage was recently calibrated
|
||||
self.cage_dev.set_field_compensated([0, 0, 0])
|
||||
# Sleep for a certain duration to allow psu to stabilize output and magnetometer to supply readings
|
||||
time.sleep(self.calibration_interval)
|
||||
# The offsets can easily be read from the magnetometer
|
||||
offsets = g.MAGNETOMETER.field
|
||||
# Save data point to raw_data list
|
||||
raw_data.append({'applied_x': 0, 'applied_y': 0, 'applied_z': 0,
|
||||
'measured_x': offsets[0], 'measured_y': offsets[1], 'measured_z': offsets[2]})
|
||||
# Set new progress indicator for UI
|
||||
self.set_progress(True, 0)
|
||||
|
||||
# Generate our set of test vectors
|
||||
test_vectors = self.fibonacci_sphere(self.calibration_points)
|
||||
@@ -475,29 +484,42 @@ class MagnetometerCalibrationComplete(Thread):
|
||||
for vec_idx, test_vec in enumerate(test_vectors):
|
||||
# Command output
|
||||
applied_vec = test_vec * self.calibration_mag_field
|
||||
self.cage_dev.set_field_raw(applied_vec)
|
||||
if not self.compensated_field:
|
||||
self.cage_dev.set_field_raw(applied_vec) # Set raw field not compensated field
|
||||
else:
|
||||
self.cage_dev.set_field_compensated(applied_vec) # Set compensated field not raw field
|
||||
|
||||
# Sleep for a certain duration to allow psu to stabilize output and magnetometer to supply readings
|
||||
time.sleep(self.calibration_interval)
|
||||
for i in range(self.var_oversampling):
|
||||
# Sleep for a certain duration to allow psu to stabilize output and magnetometer to supply readings
|
||||
time.sleep(self.calibration_interval)
|
||||
|
||||
# Read output and save to array for solver later
|
||||
raw_reading = g.MAGNETOMETER.field
|
||||
reading = raw_reading - offsets
|
||||
for i in range(3):
|
||||
row = {'m': reading[i], 'b_x': applied_vec[0], 'b_y': applied_vec[1], 'b_z': applied_vec[2]}
|
||||
# self.put_message("[Axis {}] {}".format(i, row))
|
||||
samples[i].append(row)
|
||||
# Read output and save to array for solver later
|
||||
raw_reading = g.MAGNETOMETER.field
|
||||
# reading = raw_reading - offsets
|
||||
reading = raw_reading # Do not substract offset since it will artificially suppress (hard-iron) offsets
|
||||
for i in range(3):
|
||||
row = {'m': reading[i], 'b_x': applied_vec[0], 'b_y': applied_vec[1], 'b_z': applied_vec[2]}
|
||||
# self.put_message("[Axis {}] {}".format(i, row))
|
||||
samples[i].append(row)
|
||||
|
||||
# Save data point to raw_data list
|
||||
raw_data.append({'applied_x': applied_vec[0], 'applied_y': applied_vec[1], 'applied_z': applied_vec[2],
|
||||
'measured_x': raw_reading[0], 'measured_y': raw_reading[1], 'measured_z': raw_reading[2]})
|
||||
# Save data point to raw_data list
|
||||
raw_data.append({'applied_x': applied_vec[0], 'applied_y': applied_vec[1], 'applied_z': applied_vec[2],
|
||||
'measured_x': raw_reading[0], 'measured_y': raw_reading[1],
|
||||
'measured_z': raw_reading[2]})
|
||||
|
||||
# Set new progress indicator for UI
|
||||
self.set_progress(True, vec_idx + 1)
|
||||
|
||||
# Put device into an off and ready state
|
||||
self.cage_dev.idle()
|
||||
return raw_data
|
||||
|
||||
# Use collected data to build and solve system of equations
|
||||
# sensor_parameters = self.solve_system(raw_data) # FLAG: untested!
|
||||
sensor_parameters, mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m, cal_x, cal_y, cal_z, mag_amp_avg_set = MagnetometerCalibrationComplete.solve_system(
|
||||
raw_data, self.matrix_trans_mgm_to_hh)
|
||||
|
||||
# Pass results to UI
|
||||
self.put_message('calibration_data', {'results': sensor_parameters, 'raw_data': raw_data})
|
||||
|
||||
def set_progress(self, offset_complete, test_vec_index):
|
||||
progress = int(offset_complete) * 0.2 + (test_vec_index / self.calibration_points) * 0.8
|
||||
@@ -574,7 +596,9 @@ class MagnetometerCalibrationComplete(Thread):
|
||||
# Retrieve calibration parameters
|
||||
q_mat_inv = np.linalg.inv(q_mat)
|
||||
b = -np.dot(q_mat_inv, n)
|
||||
a_mat_inv = np.real(mag_amp_avg_set / np.sqrt(np.dot(n.T, np.dot(q_mat_inv, n)) - d) * scipy.linalg.sqrtm(q_mat))
|
||||
# a_mat_inv = np.real(1 / csqrt(np.dot(n.T, np.dot(q_mat_inv, n)) - d) * linalg_scipy.sqrtm(q_mat))
|
||||
a_mat_inv = np.real(
|
||||
mag_amp_avg_set / np.sqrt(np.dot(n.T, np.dot(q_mat_inv, n)) - d) * scipy.linalg.sqrtm(q_mat))
|
||||
a_mat = np.linalg.inv(a_mat_inv)
|
||||
# Calculate error
|
||||
cal_x = np.zeros(mag_x_m.shape)
|
||||
@@ -816,78 +840,10 @@ class MagnetometerCalibrationComplete(Thread):
|
||||
|
||||
return m, n, d
|
||||
|
||||
def fit_ellipsoid(mag_x_m, mag_y_m, mag_z_m):
|
||||
""" Estimate ellipsoid parameters from a set of points.
|
||||
Parameters
|
||||
----------
|
||||
mag_x_m, mag_y_m, mag_z_m : array_like, array_like, array_like
|
||||
The samples (M,N) where M=3 (x,y,z) and N=number of samples.
|
||||
Returns
|
||||
-------
|
||||
s : array_like
|
||||
The samples (M,N) where M=3 (x,y,z) and N=number of samples.
|
||||
Returns
|
||||
-------
|
||||
M, n, d : array_like, array_like, float
|
||||
The ellipsoid parameters M, n, d.
|
||||
References
|
||||
----------
|
||||
.. [1] Qingde Li; Griffiths, J.G., "Least squares ellipsoid specific
|
||||
fitting," in Geometric Modeling and Processing, 2004.
|
||||
Proceedings, vol., no., pp.335-340, 2004
|
||||
.. https://github.com/nliaudat/magnetometer_calibration/blob/main/calibrate.py
|
||||
"""
|
||||
|
||||
# Converts to samples (M,N) where M=3 (x,y,z) and N=number of samples.
|
||||
s = np.array([mag_x_m, mag_y_m, mag_z_m])
|
||||
|
||||
# d (samples)
|
||||
d = np.array([s[0] ** 2., s[1] ** 2., s[2] ** 2.,
|
||||
2. * s[1] * s[2], 2. * s[0] * s[2], 2. * s[0] * s[1],
|
||||
2. * s[0], 2. * s[1], 2. * s[2], np.ones_like(s[0])])
|
||||
|
||||
# s, s_11, s_12, s_21, s_22 (eq. 11)
|
||||
s = np.dot(d, d.T)
|
||||
s_11 = s[:6, :6]
|
||||
s_12 = s[:6, 6:]
|
||||
s_21 = s[6:, :6]
|
||||
s_22 = s[6:, 6:]
|
||||
|
||||
# c (Eq. 8, k=4)
|
||||
c = np.array([[-1, 1, 1, 0, 0, 0],
|
||||
[1, -1, 1, 0, 0, 0],
|
||||
[1, 1, -1, 0, 0, 0],
|
||||
[0, 0, 0, -4, 0, 0],
|
||||
[0, 0, 0, 0, -4, 0],
|
||||
[0, 0, 0, 0, 0, -4]])
|
||||
|
||||
# v_1 (eq. 15, solution)
|
||||
e = np.dot(np.linalg.inv(c),
|
||||
s_11 - np.dot(s_12, np.dot(np.linalg.inv(s_22), s_21)))
|
||||
|
||||
e_w, e_v = np.linalg.eig(e)
|
||||
|
||||
v_1 = e_v[:, np.argmax(e_w)]
|
||||
if v_1[0] < 0:
|
||||
v_1 = -v_1
|
||||
|
||||
# v_2 (eq. 13, solution)
|
||||
v_2 = np.dot(np.dot(-np.linalg.inv(s_22), s_21), v_1)
|
||||
|
||||
# Quadratic-form parameters
|
||||
m = np.array([[v_1[0], v_1[3], v_1[4]],
|
||||
[v_1[3], v_1[1], v_1[5]],
|
||||
[v_1[4], v_1[5], v_1[2]]])
|
||||
n = np.array([[v_2[0]],
|
||||
[v_2[1]],
|
||||
[v_2[2]]])
|
||||
d = v_2[3]
|
||||
|
||||
return m, n, d
|
||||
|
||||
@staticmethod
|
||||
def plot_magnetometer_calibration(target_column, mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m,
|
||||
cal_x, cal_y, cal_z, mag_amp_avg_set):
|
||||
def plot_magnetometer_calibration(target_column, mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m, cal_x,
|
||||
cal_y,
|
||||
cal_z, mag_amp_avg_set):
|
||||
plot_fontsize = 5
|
||||
ax_width = 0.2
|
||||
|
||||
|
||||
+108
-24
@@ -9,6 +9,7 @@ import numpy as np
|
||||
from threading import *
|
||||
from tkinter import messagebox
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||
|
||||
from src.exceptions import DeviceBusy, DeviceAccessError
|
||||
from src.utility import ui_print
|
||||
@@ -60,23 +61,31 @@ class ExecCSVThread(Thread):
|
||||
return self._stop_event.is_set()
|
||||
|
||||
def execute_sequence(self, array, delay, parent, controller):
|
||||
# Initialize plot
|
||||
ui_print("Initializing plots...")
|
||||
figure, avx_lines = display_plot(parent)
|
||||
|
||||
# main execution method of the class
|
||||
# runs through array with times and desired fields and commands test bench accordingly
|
||||
|
||||
# array format: [time (s), xField (T), yField (T), zField (T)]
|
||||
ui_print("Initializing cage...")
|
||||
self.cage_dev.idle() # sets outputs on PSUs to 0 and Arduino pins to LOW before starting
|
||||
t_zero = time.time() # set reference time for start of run
|
||||
|
||||
# Check if everything is properly connected:
|
||||
all_connected = (parent.xy_override.get() or g.CAGE_DEVICE.psu1 is not None) and\
|
||||
(parent.z_override.get() or g.CAGE_DEVICE.psu2 is not None) and\
|
||||
all_connected = (parent.xy_override.get() or g.CAGE_DEVICE.psu1 is not None) and \
|
||||
(parent.z_override.get() or g.CAGE_DEVICE.psu2 is not None) and \
|
||||
(parent.arduino_override.get() or g.CAGE_DEVICE.arduino is not None)
|
||||
compensate_field = parent.compensated_field_var.get()
|
||||
|
||||
# True or False depending on devices status, checks for some devices may be overridden by user
|
||||
if not all_connected:
|
||||
ui_print("Required devices are not present, sequence aborted.")
|
||||
messagebox.showwarning("Device Error!", "Required devices are not present, sequence aborted.")
|
||||
return
|
||||
|
||||
ui_print("Starting csv replay...")
|
||||
i = 0 # index of the current array row
|
||||
while i < len(array):
|
||||
if self.stopped or g.exit_flag:
|
||||
@@ -86,9 +95,9 @@ class ExecCSVThread(Thread):
|
||||
return
|
||||
|
||||
# while array is not finished, devices are connected, user has not cancelled and application is running
|
||||
|
||||
t = time.time() - t_zero # get time relative to start of run
|
||||
target_t = array[i, 0] # Target execution time of data point
|
||||
|
||||
if t >= target_t: # time for this row has come
|
||||
field_vec = array[i, 1:4] # extract desired field vector
|
||||
ui_print("[{:5.3f}s] B=[{:.1f}, {:.1f}, {:.1f}]\u03BCT for t={:.2f}s".format(t,
|
||||
@@ -96,13 +105,28 @@ class ExecCSVThread(Thread):
|
||||
field_vec[1] * 1e6,
|
||||
field_vec[2] * 1e6,
|
||||
target_t))
|
||||
self.cage_dev.set_field_compensated(field_vec) # send field vector to test bench
|
||||
|
||||
if compensate_field:
|
||||
self.cage_dev.set_field_compensated(field_vec) # send field vector to test bench
|
||||
else:
|
||||
self.cage_dev.set_field_raw(field_vec) # send field vector to test bench
|
||||
# log change to the log file if user has selected event logging in the Configure Logging window
|
||||
logger = controller.pages[ui.ConfigureLogging] # get object of logging configurator
|
||||
if logger.event_logging: # data should be logged when test bench is commanded
|
||||
logger.log_datapoint() # log data
|
||||
|
||||
# Update figure
|
||||
try:
|
||||
if abs(t - target_t) < 0.2:
|
||||
for j in range(4):
|
||||
avx_lines[j].set_data([t, t], [0, 1])
|
||||
# print("The next line might crash the programm, outcomment if necessary")
|
||||
parent.plot_canvas.draw() # equivalent to matplotlib.show()
|
||||
else:
|
||||
ui_print("Update rate of plot slows down field generation and is thus skipped.")
|
||||
|
||||
except DeviceAccessError as e:
|
||||
ui_print("Failed to update figure: ", e)
|
||||
|
||||
i = i + 1 # next row
|
||||
|
||||
elif t <= target_t - delay - 0.02: # is there enough time to sleep before the next row?
|
||||
@@ -112,13 +136,13 @@ class ExecCSVThread(Thread):
|
||||
|
||||
|
||||
def read_csv_to_array(filepath): # convert a given csv file to a numpy array
|
||||
# csv format: time (s); xField (T); yField (T); zField (T) (german excel)
|
||||
# decimal or period commas. Do not use these characters as a thousands seperator!
|
||||
# csv format: time [s], xField [T], yField [T], zField [T] (german excel)
|
||||
# decimal or period commas. Do not use these characters as a thousand separator!
|
||||
with open(filepath, 'r') as csv_file:
|
||||
# Normalize seperators
|
||||
csv_string = csv_file.read().replace(',', '.')
|
||||
# Normalize separators
|
||||
csv_string = csv_file.read()
|
||||
# read csv file without column headers
|
||||
file = pandas.read_csv(StringIO(csv_string), sep=';', decimal='.', header=0)
|
||||
file = pandas.read_csv(StringIO(csv_string), sep=',', decimal='.', header=0)
|
||||
array = file.to_numpy() # convert csv to array
|
||||
return array
|
||||
|
||||
@@ -134,7 +158,7 @@ def check_array_ok(array):
|
||||
data_point = array[row_idx, i + 1] # extract data for this axis from array
|
||||
if data_point > max_val or data_point < min_val:
|
||||
# Out of bounds
|
||||
warnings.append({'row': row_idx+1, 'axis': g.AXIS_NAMES[i]})
|
||||
warnings.append({'row': row_idx + 1, 'axis': g.AXIS_NAMES[i]})
|
||||
|
||||
# show warning pop-up if values are exceeding limits
|
||||
nr_warnings = len(warnings)
|
||||
@@ -149,31 +173,75 @@ def check_array_ok(array):
|
||||
messagebox.showwarning("Value Limits Warning!", warning_msg)
|
||||
|
||||
|
||||
def display_plot(parent): # create plot of fixed size (pixels) from array
|
||||
# calculate available height for plot (in pixels):
|
||||
height_others = 0 # initialize variable to calculate height of other widgets
|
||||
for element in parent.row_elements: # go through all rows in the widget except the plot frame
|
||||
height_others += element.winfo_height() # add up heights
|
||||
|
||||
# calculate available plot height:
|
||||
height = parent.parent.winfo_height() - height_others - 50 # height of parent frame - other widgets - margin
|
||||
width = min(parent.parent.winfo_width() - 100, 1100) # set width to available space but max. 1100
|
||||
|
||||
# Create plot
|
||||
figure = plot_field_sequence(parent.sequence_array, width, height) # create figure to be displayed
|
||||
# Clear previous plots first
|
||||
|
||||
try:
|
||||
if parent.plot_canvas is not None:
|
||||
parent.plot_canvas.get_tk_widget().destroy()
|
||||
except Exception as e:
|
||||
ui_print("Something went wrong while plotting csv data!", e)
|
||||
messagebox.showerror("Error!", "Something went wrong while plotting csv data: \n%s" % e)
|
||||
pass
|
||||
|
||||
axes = figure.axes
|
||||
avx_lines = [axes[0].axvline(x=0, color="r"), axes[1].axvline(x=0, color="r"),
|
||||
axes[2].axvline(x=0, color="r"), axes[3].axvline(x=0, color="r")]
|
||||
# Show new plot
|
||||
parent.plot_canvas = FigureCanvasTkAgg(figure, parent.plot_frame) # create canvas to draw figure on
|
||||
# print("The next line might crash the programm, outcomment if necessary")
|
||||
parent.plot_canvas.draw() # equivalent to matplotlib.show()
|
||||
parent.plot_canvas.get_tk_widget().grid(row=0, column=0, sticky="nesw") # place canvas in the UI
|
||||
|
||||
return figure, avx_lines
|
||||
|
||||
|
||||
def plot_field_sequence(array, width, height): # create plot of fixed size (pixels) from array
|
||||
# ToDo (optional): polar plots, plots of angle...
|
||||
fig_dpi = 100 # set figure resolution (dots per inch)
|
||||
px = 1/fig_dpi # get pixel to inch size conversion
|
||||
figure = plt.Figure(figsize=(width*px, height*px), dpi=fig_dpi) # create figure with correct size
|
||||
px = 1 / fig_dpi # get pixel to inch size conversion
|
||||
figure = plt.Figure(figsize=(width * px, height * px), dpi=fig_dpi) # create figure with correct size
|
||||
|
||||
# noinspection PyTypeChecker,SpellCheckingInspection
|
||||
axes = figure.subplots(3, sharex=True, sharey=True, gridspec_kw={'hspace': 0.4}) # create subplots with shared axes
|
||||
axes = figure.subplots(4, sharex=True, sharey=False, gridspec_kw={'hspace': 0.4}) # make subplots with shared axes
|
||||
|
||||
figure.suptitle("Magnetic Field Sequence") # set figure title
|
||||
|
||||
# modify data to show instantaneous jumps in field to reflect test bench operation
|
||||
new_array = np.array([[0, 0, 0, 0]], dtype=float) # initialize modified array, zeros to show start from no fields
|
||||
new_array = np.array([[0, 0, 0, 0, 0]], dtype=float) # initialize modified array, zeros to show start from no field
|
||||
|
||||
last_vals = [0, 0, 0] # [x,y,z] field values from last data point (zero here), used to create step in data
|
||||
for row in array[:, 0:4]: # go through each row in the original array
|
||||
length = len(array[:, 0])
|
||||
max_length = 1000
|
||||
if length > max_length:
|
||||
n = round(length/max_length) # plot every n-th element
|
||||
ui_print("Array contains {:d} elements more than the maximum amount of {:d}. "
|
||||
"To improve display only every {:d} element is plotted!"
|
||||
.format(length, max_length, n))
|
||||
else:
|
||||
n = 1 # plot every element
|
||||
|
||||
last_values = [0, 0, 0, 0] # [x,y,z, rr] field values / rot rate from last data point (zero here)
|
||||
for row in array[::n, 0:5]: # go through each row in the original array
|
||||
# create extra datapoint at current timestamp, with field values from last to create a "step" in the plot:
|
||||
new_array = np.append(new_array, [[row[0], *last_vals]], axis=0)
|
||||
new_array = np.append(new_array, [[row[0], *last_values]], axis=0)
|
||||
new_array = np.append(new_array, [row], axis=0) # add actual datapoint for current timestamp
|
||||
last_vals = row[1:4] # save values from current timestamp for next
|
||||
new_array = np.append(new_array, [[new_array[-1, 0], 0, 0, 0]], axis=0) # append last datapoint with 0 fields
|
||||
last_values = row[1:5] # save values from current timestamp for next
|
||||
new_array = np.append(new_array, [[new_array[-1, 0], 0, 0, 0, 0]], axis=0) # append last datapoint with 0 fields
|
||||
|
||||
# extract data and plot:
|
||||
# extract data and plot magnetic fields:
|
||||
t = new_array[:, 0] # extract time column
|
||||
for i in [0, 1, 2]: # go through all three axes
|
||||
for i in [0, 1, 2]: # go through all three axes plus rotation rate
|
||||
data = new_array[:, i + 1] * 1e6 # extract field column of this axis and convert to microtesla
|
||||
min_val, max_val = g.CAGE_DEVICE.axes[i].max_comp_field * 1e6 # get limits of achievable field
|
||||
plot = axes[i] # get appropriate subplot
|
||||
@@ -189,9 +257,25 @@ def plot_field_sequence(array, width, height): # create plot of fixed size (pix
|
||||
plot.text(t[-1], min_val, "min", horizontalalignment='center', color='r')
|
||||
|
||||
plot.set_title(g.AXIS_NAMES[i], size=10) # set subplot title (e.g. "X-Axis")
|
||||
plot.grid()
|
||||
# plot rotation rate
|
||||
plot = axes[3]
|
||||
plot.plot(t, new_array[:, 4], linestyle='solid', marker='.') # plot data
|
||||
plot.grid()
|
||||
plot.set_title("Abs. Rotation Rate", size=10) # set subplot title (e.g. "X-Axis")
|
||||
# set ylim of magnetic field axis to same value
|
||||
ylim_mag = ([min(axes[0].get_ylim()), max(axes[0].get_ylim()),
|
||||
min(axes[1].get_ylim()), max(axes[1].get_ylim()),
|
||||
min(axes[2].get_ylim()), max(axes[2].get_ylim())])
|
||||
for i in range(3):
|
||||
axes[0].set_ylim(min(ylim_mag), max(ylim_mag))
|
||||
|
||||
# set shared axis labels:
|
||||
axes[2].set_xlabel("Time (s)")
|
||||
axes[1].set_ylabel("Magnetic Field (\u03BCT)")
|
||||
axes[3].set_xlabel("Time [s]")
|
||||
axes[1].set_ylabel("Magnetic Field [\u03BCT]")
|
||||
axes[3].set_ylabel("Rate [°/s]")
|
||||
# set y axis limits
|
||||
for i in range(4):
|
||||
axes[i].set_xlim(left=-round((new_array[-1, 0]+0.5)*0.01), right=round((new_array[-1, 0]+0.5)*1.01))
|
||||
|
||||
return figure # return the created figure to be inserted somewhere
|
||||
|
||||
+432
-88
@@ -14,10 +14,12 @@ import matplotlib.pyplot as plt
|
||||
|
||||
# import general packages:
|
||||
import numpy as np
|
||||
from scipy.optimize import fsolve
|
||||
import os
|
||||
import os.path
|
||||
from datetime import datetime
|
||||
from math import pi
|
||||
import warnings
|
||||
|
||||
# import other project files:
|
||||
import src.globals as g
|
||||
@@ -27,9 +29,12 @@ import src.csv_logging as log
|
||||
from src.calibration import AmbientFieldCalibration, CoilConstantCalibration, MagnetometerCalibrationSimple, \
|
||||
MagnetometerCalibrationComplete
|
||||
from src.exceptions import DeviceAccessError, MagFieldOutOfBounds
|
||||
from src.utility import ui_print, save_dict_list_to_csv, load_dict_list_from_csv
|
||||
from src.utility import ui_print, save_dict_list_to_csv, save_dict_list_to_csv2, load_dict_list_from_csv
|
||||
import src.helmholtz_cage_device as helmholtz_cage_device
|
||||
|
||||
# Filter warning messages
|
||||
warnings.filterwarnings("error")
|
||||
|
||||
# Define global font styles:
|
||||
screen_height_limit1 = 1100 # Limit after which font size gets reduced
|
||||
screen_height_limit2 = 900 # Limit after which font size gets reduced
|
||||
@@ -41,6 +46,8 @@ BIG_BUTTON_FONT = (font, points[1], "bold")
|
||||
SMALL_BUTTON_FONT = (font, points[2])
|
||||
DEFAULT_FONT = (font, points[2])
|
||||
|
||||
target_values = [np.nan, np.nan, np.nan, np.nan]
|
||||
|
||||
|
||||
class HelmholtzGUI(Tk):
|
||||
# main application window, almost everything else here is called from this class
|
||||
@@ -306,11 +313,11 @@ class ManualMode(Frame):
|
||||
else: # this really should never happen
|
||||
field = [0, 0]
|
||||
ui_print("Unexpected value encountered: compensate =", compensate)
|
||||
messagebox.showerror("Unexpected Value!", ("Unexpected value encountered: compensate =", compensate))
|
||||
messagebox.showerror("Unexpected Value! Unexpected value encountered: compensate = ", str(compensate))
|
||||
var.set("(%0.1f to %0.1f \u03BCT)" % (field[0], field[1])) # update the label text with the new values
|
||||
|
||||
def switch_to_current_mode(self): # called when switching to the input current mode
|
||||
self.compensate_checkbox.config(state=DISABLED) # disable the compensate ambient field checkbox
|
||||
self.compensate_checkbox.config(state=DISABLED) # disable the compensation of the ambient field checkbox
|
||||
|
||||
# update the labels showing the min/max achievable values
|
||||
for i, var in enumerate(self.max_value_vars): # go through the max value labels for each axis
|
||||
@@ -369,8 +376,8 @@ class ManualMode(Frame):
|
||||
pass
|
||||
else: # this really should never happen
|
||||
ui_print("Unexpected value encountered: compensate =", compensate)
|
||||
messagebox.showerror("Unexpected Value!",
|
||||
("Unexpected value encountered: compensate =", compensate))
|
||||
messagebox.showerror("Unexpected Value! Unexpected value encountered: compensate = ",
|
||||
str(compensate))
|
||||
except helmholtz_cage_device.DeviceBusy:
|
||||
ui_print("Error: Could not acquire control. Is the HW already in use?")
|
||||
|
||||
@@ -405,6 +412,18 @@ class ExecuteCSVMode(Frame):
|
||||
self.xy_override = BooleanVar(value=False) # True to disable connection check for XY PSU
|
||||
self.z_override = BooleanVar(value=False) # True to disable connection check for Z PSU
|
||||
self.arduino_override = BooleanVar(value=False) # True to disable connection check for arduino
|
||||
self.compensated_field_var = BooleanVar(value=True)
|
||||
|
||||
# Generate CSV sequence variables
|
||||
self.rot_vector_vars = [DoubleVar(value=1), DoubleVar(value=0), DoubleVar(value=0)]
|
||||
self.rot_center_vars = [DoubleVar(value=0), DoubleVar(value=0), DoubleVar(value=0)]
|
||||
self.rot_mag_vars = DoubleVar(value=100)
|
||||
self.rot_time_step_vars = DoubleVar(value=1)
|
||||
self.rot_cycle_number_vars = IntVar(value=1)
|
||||
self.rot_rate_vars = [DoubleVar(value=1), DoubleVar(value=0), DoubleVar(value=0),
|
||||
DoubleVar(value=0), DoubleVar(value=0), DoubleVar(value=0)]
|
||||
self.rot_rate_last_vars = [DoubleVar(value=1), DoubleVar(value=0), DoubleVar(value=0),
|
||||
DoubleVar(value=0), DoubleVar(value=0), DoubleVar(value=0)]
|
||||
|
||||
# --- UI ELEMENTS ---
|
||||
row_counter = 0 # keep track of which grid row we are in
|
||||
@@ -468,6 +487,9 @@ class ExecuteCSVMode(Frame):
|
||||
arduino_checkbox = Checkbutton(self.checkbox_frame, text="Arduino",
|
||||
variable=self.arduino_override, onvalue=True, offvalue=False)
|
||||
arduino_checkbox.grid(row=0, column=3, padx=3)
|
||||
self.compensate_checkbox = Checkbutton(self.checkbox_frame, text="Compensate ambient field",
|
||||
variable=self.compensated_field_var, onvalue=True, offvalue=False)
|
||||
self.compensate_checkbox.grid(row=1, column=1, columnspan=3, pady=5, sticky="nw")
|
||||
|
||||
row_counter += 1
|
||||
|
||||
@@ -479,6 +501,98 @@ class ExecuteCSVMode(Frame):
|
||||
|
||||
self.plot_canvas = None # Is generated upon plotting
|
||||
|
||||
# Second panel with CSV generator
|
||||
# setup header
|
||||
col_counter = 1
|
||||
row_counter = 0 # reset row counter
|
||||
header = Label(self, text="Generate CSV File", font=HEADER_FONT, padx=100, pady=3)
|
||||
header.grid(row=row_counter, column=col_counter, padx=100, sticky=W)
|
||||
row_counter += 1
|
||||
generate_csv_button_frame = Frame(self)
|
||||
generate_csv_button_frame.grid(row=row_counter, column=col_counter, padx=100, sticky=W)
|
||||
# Generate and export csv sequence
|
||||
self.generate_export_csv_button = Button(generate_csv_button_frame, text=" Generate and Export to CSV",
|
||||
command=self.export_csv_sequence,
|
||||
state="normal",
|
||||
pady=5, padx=5)
|
||||
self.generate_export_csv_button.grid(row=0, column=0, padx=5, pady=5)
|
||||
# Generate and export csv sequence
|
||||
self.generate_load_csv_button = Button(generate_csv_button_frame, text=" Generate and Load Sequence",
|
||||
command=self.generate_load_csv_sequence,
|
||||
state="normal",
|
||||
pady=5, padx=5)
|
||||
self.generate_load_csv_button.grid(row=0, column=1, padx=5, pady=5)
|
||||
row_counter += 1
|
||||
info_text = Label(self,
|
||||
text="Generate data of a circular, counter clockwise motion around an arbitrary axis.",
|
||||
padx=100, pady=3)
|
||||
info_text.grid(row=row_counter, column=col_counter, padx=0, sticky=W)
|
||||
row_counter += 1
|
||||
|
||||
# Inputs and generation button
|
||||
input_csv_gen_frame = LabelFrame(self, text="Input Parameters for Rotating Magnetic Field")
|
||||
input_csv_gen_frame.grid(row=row_counter, column=col_counter, padx=(100, 0), pady=20, sticky="nw")
|
||||
for i, label in enumerate(['X', 'Y', 'Z']):
|
||||
axis_label = Label(input_csv_gen_frame, text=label)
|
||||
axis_label.grid(row=row_counter, column=i + 1, padx=5, pady=5, sticky="nw")
|
||||
row_counter += 1
|
||||
rot_vector_label = Label(input_csv_gen_frame, text="Axis of rotation: ")
|
||||
rot_vector_label.grid(row=row_counter, column=0, padx=5, pady=5, sticky="nw")
|
||||
for i in range(3):
|
||||
rot_vector_data = Entry(input_csv_gen_frame,
|
||||
textvariable=self.rot_vector_vars[i],
|
||||
width=15)
|
||||
rot_vector_data.grid(row=row_counter, column=i + 1, padx=5, pady=5, sticky="nw")
|
||||
rot_vector_unit = Label(input_csv_gen_frame, text="-")
|
||||
rot_vector_unit.grid(row=row_counter, column=4, padx=5, pady=5, sticky="nw")
|
||||
row_counter += 1
|
||||
rot_center_label = Label(input_csv_gen_frame, text="Center of rotation: ")
|
||||
rot_center_label.grid(row=row_counter, column=0, padx=5, pady=5, sticky="nw")
|
||||
for i in range(3):
|
||||
rot_center_data = Entry(input_csv_gen_frame,
|
||||
textvariable=self.rot_center_vars[i],
|
||||
width=15)
|
||||
rot_center_data.grid(row=row_counter, column=i + 1, padx=5, pady=5, sticky="nw")
|
||||
rot_center_unit = Label(input_csv_gen_frame, text="\u03BCT")
|
||||
rot_center_unit.grid(row=row_counter, column=4, padx=5, pady=5, sticky="nw")
|
||||
row_counter += 1
|
||||
rot_mag_label = Label(input_csv_gen_frame, text="Field magnitude: ")
|
||||
rot_mag_label.grid(row=row_counter, column=0, padx=5, pady=5, sticky="nw")
|
||||
rot_mag_data = Entry(input_csv_gen_frame, textvariable=self.rot_mag_vars, width=15)
|
||||
rot_mag_data.grid(row=row_counter, column=1, padx=5, pady=5, sticky="nw")
|
||||
rot_mag_unit = Label(input_csv_gen_frame, text="\u03BCT")
|
||||
rot_mag_unit.grid(row=row_counter, column=4, padx=5, pady=5, sticky="nw")
|
||||
row_counter += 1
|
||||
time_step_label = Label(input_csv_gen_frame, text="Time step: ")
|
||||
time_step_label.grid(row=row_counter, column=0, padx=5, pady=5, sticky="nw")
|
||||
time_step_data = Entry(input_csv_gen_frame, textvariable=self.rot_time_step_vars, width=15)
|
||||
time_step_data.grid(row=row_counter, column=1, padx=5, pady=5, sticky="nw")
|
||||
time_step_unit = Label(input_csv_gen_frame, text="s")
|
||||
time_step_unit.grid(row=row_counter, column=4, padx=5, pady=5, sticky="nw")
|
||||
row_counter += 1
|
||||
time_step_label = Label(input_csv_gen_frame, text="Cycle number: ")
|
||||
time_step_label.grid(row=row_counter, column=0, padx=5, pady=5, sticky="nw")
|
||||
time_step_data = Entry(input_csv_gen_frame, textvariable=self.rot_cycle_number_vars, width=15)
|
||||
time_step_data.grid(row=row_counter, column=1, padx=5, pady=5, sticky="nw")
|
||||
time_step_unit = Label(input_csv_gen_frame, text="-")
|
||||
time_step_unit.grid(row=row_counter, column=4, padx=5, pady=5, sticky="nw")
|
||||
row_counter += 1
|
||||
rot_rate_label = Label(input_csv_gen_frame, text="Rotation rate: ")
|
||||
rot_rate_label.grid(row=row_counter, column=0, padx=5, pady=5, sticky="nw")
|
||||
for i in range(3):
|
||||
for j in range(2):
|
||||
rot_rate_data = Entry(input_csv_gen_frame, textvariable=self.rot_rate_vars[i + 3 * j], width=15)
|
||||
rot_rate_data.grid(row=row_counter + j, column=1 + i, padx=5, pady=5, sticky="nw")
|
||||
rot_rate_unit = Label(input_csv_gen_frame, text="\u00b0/s, \u00b0/s^2, \u00b0/s^3")
|
||||
rot_rate_unit.grid(row=row_counter, column=4, padx=5, pady=5, sticky="nw")
|
||||
row_counter += 1
|
||||
rot_rate_unit = Label(input_csv_gen_frame, text="\u00b0/s, 1/s, -")
|
||||
rot_rate_unit.grid(row=row_counter, column=4, padx=5, pady=5, sticky="nw")
|
||||
row_counter += 1
|
||||
|
||||
rot_rate_label = Label(input_csv_gen_frame, text="r(t) = r_0 + r_1*t + r_2*t^2 + r3 * sin( r_4*t + r_5 )")
|
||||
rot_rate_label.grid(row=row_counter, column=1, columnspan=4, padx=5, pady=5, sticky="nw")
|
||||
|
||||
def page_switch(self): # function that is called when switching to this window
|
||||
# every class in the UI needs this, even if it doesn't do anything
|
||||
pass
|
||||
@@ -569,7 +683,8 @@ class ExecuteCSVMode(Frame):
|
||||
figure = csv_threading.plot_field_sequence(self.sequence_array, width, height) # create figure to be displayed
|
||||
# Clear previous plots first
|
||||
try:
|
||||
self.plot_canvas.get_tk_widget().destroy()
|
||||
if self.plot_canvas is not None:
|
||||
self.plot_canvas.get_tk_widget().destroy()
|
||||
except Exception as e:
|
||||
ui_print("Something went wrong while plotting csv data!", e)
|
||||
messagebox.showerror("Error!", "Something went wrong while plotting csv data: \n%s" % e)
|
||||
@@ -580,6 +695,174 @@ class ExecuteCSVMode(Frame):
|
||||
self.plot_canvas.draw() # equivalent to matplotlib.show()
|
||||
self.plot_canvas.get_tk_widget().grid(row=0, column=0, sticky="nesw") # place canvas in the UI
|
||||
|
||||
def export_csv_sequence(self): # Generate and export csv sequence
|
||||
# Generate rotation data
|
||||
t, x, y, z, rr = self.generate_csv_sequence()
|
||||
# Assemble data to export
|
||||
rot_sequence = dict(enumerate(t))
|
||||
for i in range(len(t)):
|
||||
rot_sequence[i] = {'Time [s]]': t[i], 'xField [T]': x[i], 'yField [T]': y[i], 'zField [T]': z[i],
|
||||
'Rotation Rate [deg/s]': rr[i]}
|
||||
# Save dictionary to disk
|
||||
save_dict_list_to_csv2('test_sequence_rotation.csv', rot_sequence, query_path=True)
|
||||
ui_print("Saved test sequence to disc.")
|
||||
|
||||
def generate_load_csv_sequence(self): # Generate and load csv sequence
|
||||
# Generate rotation data
|
||||
t, x, y, z, rr = self.generate_csv_sequence()
|
||||
# Write data into array
|
||||
temp_array = np.ndarray((len(t), 5))
|
||||
for i in range(len(t)):
|
||||
temp_array[i] = [t[i], x[i], y[i], z[i], rr[i]]
|
||||
self.sequence_array = temp_array
|
||||
try: # try to check the values and display the plot
|
||||
csv_threading.check_array_ok(self.sequence_array[:, 0:4]) # check for values exceeding limits
|
||||
self.sequence_array_ok = True # Has nothing to do with limits. Just means the data was parsed
|
||||
self.display_plot() # plot data and display
|
||||
except Exception as e: # something went wrong
|
||||
self.sequence_array_ok = False
|
||||
# display error messages:
|
||||
ui_print("Error while plotting rotation sequence:", e)
|
||||
messagebox.showerror("Error!", "Error while plotting rotation sequence: \n%s" % e)
|
||||
else: # nothing went wrong
|
||||
self.execute_button["state"] = "normal" # activate run button --> enable execution
|
||||
|
||||
def generate_csv_sequence(self):
|
||||
ui_print("Generate CSV File: started generating csv file from user inputs.")
|
||||
# Iteration limit for while loop trying to find rotation solution
|
||||
interation_limit = 100
|
||||
# Some fixed values
|
||||
rot_center_geom_mean_max = 100 # [µT] Maximum geometric mean
|
||||
rot_center_default = [0, 0, 0] # [µT]
|
||||
rot_mag_default = 100 # [µT]
|
||||
rot_mag_max = 180 # [µT]
|
||||
rot_time_step_default = 1 # [s]
|
||||
rot_time_step_min = 0.2 # [s]
|
||||
# Initialize and get vectors
|
||||
rot_vector = [0, 0, 0]
|
||||
rot_center = [0, 0, 0]
|
||||
r = [0, 0, 0, 0, 0, 0]
|
||||
for i in range(len(rot_vector)):
|
||||
rot_vector[i] = float(self.rot_vector_vars[i].get()) # [-]
|
||||
rot_center[i] = float(self.rot_center_vars[i].get()) # [uT]
|
||||
rot_mag = float(self.rot_mag_vars.get()) # [uT]
|
||||
for i in range(len(r)):
|
||||
r[i] = float(self.rot_rate_vars[i].get()) # [deg/s, deg/s^2, deg/s^3, deg/s, 1/s, -]
|
||||
rot_time_step = float(self.rot_time_step_vars.get()) # [s]
|
||||
# Normalize rotation vector
|
||||
if np.linalg.norm(rot_vector) == 0:
|
||||
ui_print("Error: Rotation axis cannot be singular.")
|
||||
raise ValueError('Zero vector for rotation axis given!')
|
||||
else:
|
||||
v = rot_vector / np.linalg.norm(rot_vector)
|
||||
if np.linalg.norm(rot_vector) != 1:
|
||||
ui_print("Warning: Rotation axis not normalised. Normalising automatically.")
|
||||
for i in range(len(rot_vector)):
|
||||
self.rot_vector_vars[i].set(round(v[i], 3))
|
||||
# Checking magnitude
|
||||
if rot_mag < 0:
|
||||
ui_print("Warning: Magnetic field magnitude cannot be negative. Changing sign automatically.")
|
||||
rot_mag = -rot_mag
|
||||
elif rot_mag == 0:
|
||||
ui_print("Warning: Magnetic field magnitude cannot be zero. Setting default automatically.")
|
||||
rot_mag = rot_mag_default
|
||||
elif rot_mag > rot_mag_max:
|
||||
ui_print("Warning: Magnetic field magnitude too large. Setting default automatically.")
|
||||
rot_mag = rot_mag_default
|
||||
self.rot_mag_vars.set(round(rot_mag, 3))
|
||||
# Checking center point
|
||||
if np.sqrt(rot_center[0] ** 2 + rot_center[1] ** 2 + rot_center[2] ** 2) > rot_center_geom_mean_max:
|
||||
ui_print("Warning: Rotation center excessive. Setting to zero automatically.")
|
||||
rot_center = rot_center_default
|
||||
for i in range(len(rot_center)):
|
||||
self.rot_center_vars[i].set(round(rot_center[i], 3))
|
||||
# Check time step
|
||||
if rot_time_step < 0:
|
||||
ui_print("Warning: Time step cannot be negative. Changing sign automatically.")
|
||||
rot_time_step = -rot_time_step
|
||||
elif rot_time_step == 0:
|
||||
ui_print("Warning: Time step cannot be zero. Setting default automatically.")
|
||||
rot_time_step = rot_time_step_default
|
||||
elif rot_time_step < rot_time_step_min:
|
||||
ui_print("Warning: Time step too small (<{:.3f}). Setting default automatically.".format(rot_time_step_min))
|
||||
rot_time_step = rot_time_step_default
|
||||
self.rot_time_step_vars.set(round(rot_time_step, 3))
|
||||
# Check cycle number
|
||||
rot_cycle_number = self.rot_cycle_number_vars.get()
|
||||
if rot_cycle_number < 1:
|
||||
ui_print("Warning: cycle number cannot be smaller than one!")
|
||||
rot_cycle_number = 1
|
||||
elif isinstance(rot_cycle_number, float):
|
||||
if not rot_cycle_number.is_integer():
|
||||
ui_print("Warning: cycle number must be an integer. Rounding to nearest integer")
|
||||
rot_cycle_number = round(rot_cycle_number)
|
||||
self.rot_cycle_number_vars.set(rot_cycle_number)
|
||||
# Variable rotation rate
|
||||
# Solve integral of r(t) = r_0 + r_1 * t + r_2 * t ^ 2 + r3 * sin(r_4 * t + r_5) from 0 to tau
|
||||
angle = 360 * rot_cycle_number # [deg] Cycle number multiples of full circle
|
||||
|
||||
def func(tau2):
|
||||
return r[0] + r[1] * tau2 + r[2] * tau2 ** 2 + r[3] * (np.sin(r[4] * tau2 + r[5]))
|
||||
|
||||
def func_integral(tau):
|
||||
return angle - (r[0] * tau + r[1] / 2 * tau ** 2 + r[2] / 3 * tau ** 3
|
||||
- r[3] * (np.cos(r[4] * tau + r[5]) + np.cos(r[5]))
|
||||
+ r[3] * r[4] * (np.sin(r[4] * tau + r[5]) - np.sin(r[5])))
|
||||
|
||||
tau_solution = -1
|
||||
i = 0 # iterator and guess variable [s]
|
||||
while tau_solution < 0 or abs(func_integral(tau_solution)) > 0.5:
|
||||
tau_initial_guess = i
|
||||
try:
|
||||
tau_solution = fsolve(func_integral, tau_initial_guess)
|
||||
except RuntimeWarning as w:
|
||||
ui_print("Calculating the cycle time yielded: ", w)
|
||||
i = i + 1
|
||||
if i == interation_limit:
|
||||
ui_print(
|
||||
"Warning: the user inputs for a rotation rate yield no solution. Rate reset to last working input.")
|
||||
# Resetting to last working condition
|
||||
for i in range(len(r)):
|
||||
r[i] = float(self.rot_rate_last_vars[i].get()) # [deg/s, deg/s^2, deg/s^3, deg/s, 1/s, -]
|
||||
self.rot_rate_vars[i].set(self.rot_rate_last_vars[i].get())
|
||||
if tau_solution >= 0 or abs(func_integral(tau_solution)) <= 0.5:
|
||||
# Storing last working inputs
|
||||
for i in range(len(r)):
|
||||
self.rot_rate_last_vars[i].set(self.rot_rate_vars[i].get())
|
||||
ui_print("Generate CSV File: finished successfully with user inputs.")
|
||||
break
|
||||
|
||||
"""
|
||||
ui_print(
|
||||
"The cycle_time is tau = {:3f} with f(tau) = {:3f}".format(tau_solution[0], func_integral(tau_solution)[0]))
|
||||
"""
|
||||
# Calculate timing
|
||||
cycle_time = tau_solution
|
||||
arr_len = int(np.floor(cycle_time / rot_time_step))
|
||||
# Find perpendicular vectors
|
||||
if v[1] == 0 and v[2] == 0:
|
||||
a = np.cross(v, [0, 1, 0])
|
||||
else:
|
||||
a = np.cross(v, [1, 0, 0])
|
||||
b = np.cross(v, a)
|
||||
# Initialize vectors
|
||||
th = 0 # [rad]
|
||||
t = np.zeros(arr_len) # [s]
|
||||
x = np.zeros(arr_len) # [T]
|
||||
y = np.zeros(arr_len) # [T]
|
||||
z = np.zeros(arr_len) # [T]
|
||||
rot_rate = np.zeros(arr_len) # [T]
|
||||
# Calculate vectors
|
||||
for i in range(arr_len):
|
||||
t[i] = i * rot_time_step
|
||||
rot_rate[i] = func(t[i])
|
||||
th = th + rot_rate[i] * np.pi / 180 * rot_time_step
|
||||
x[i] = (rot_center[0] + rot_mag * np.cos(th) * a[0] + rot_mag * np.sin(th) * b[0]) * 1e-6
|
||||
y[i] = (rot_center[1] + rot_mag * np.cos(th) * a[1] + rot_mag * np.sin(th) * b[1]) * 1e-6
|
||||
z[i] = (rot_center[2] + rot_mag * np.cos(th) * a[2] + rot_mag * np.sin(th) * b[2]) * 1e-6
|
||||
# Return vectors
|
||||
return t, x, y, z, rot_rate
|
||||
|
||||
|
||||
class CalibrateAmbientField(Frame):
|
||||
def __init__(self, parent, controller):
|
||||
@@ -602,8 +885,13 @@ class CalibrateAmbientField(Frame):
|
||||
# UI variables
|
||||
self.connected_state_var = StringVar(value="Not connected")
|
||||
self.field_value_vars = [StringVar(value="No data"),
|
||||
StringVar(value="No data"),
|
||||
StringVar(value="No data"),
|
||||
StringVar(value="No data")]
|
||||
self.target_value_vars = [StringVar(value="No data"),
|
||||
StringVar(value="No data"),
|
||||
StringVar(value="No data"),
|
||||
StringVar(value="No data")]
|
||||
self.calibration_procedure_progress_var = IntVar(value=0)
|
||||
# Contains results for ambient field calibration
|
||||
self.ambient_field_result_vars = [StringVar(), StringVar(), StringVar()]
|
||||
@@ -641,16 +929,26 @@ class CalibrateAmbientField(Frame):
|
||||
field_data_frame.grid(row=row_counter, column=0, sticky="nw")
|
||||
field_data_label = Label(field_data_frame, text="Field data:", font=SUB_HEADER_FONT)
|
||||
field_data_label.grid(row=0, column=0, padx=10, pady=3, sticky="nw")
|
||||
axis_labels = ['X:', 'Y:', 'Z:']
|
||||
for i in range(3):
|
||||
field_data_axis_label = Label(field_data_frame, text=axis_labels[i])
|
||||
field_data_axis_label.grid(row=i, column=1, padx=10, pady=3)
|
||||
axis_labels_mgm = ['X_mgm:', 'Y_mgm:', 'Z_mgm:', 'T_mgm:']
|
||||
axis_labels_hh = ['X_hh:', 'Y_hh:', 'Z_hh:', 'T_hh:']
|
||||
for i in range(4):
|
||||
field_data_axis_label = Label(field_data_frame, text=axis_labels_mgm[i])
|
||||
field_data_axis_label.grid(row=i + 1, column=0, padx=10, pady=3)
|
||||
|
||||
field_data_axis_data = Label(field_data_frame, textvariable=self.field_value_vars[i])
|
||||
field_data_axis_data.grid(row=i, column=2, padx=(20, 0), pady=3)
|
||||
field_data_axis_data.grid(row=i + 1, column=1, padx=(20, 0), pady=3)
|
||||
|
||||
field_data_axis_units = Label(field_data_frame, text="\u03BCT")
|
||||
field_data_axis_units.grid(row=i, column=3, padx=5, pady=3)
|
||||
field_data_axis_units.grid(row=i + 1, column=2, padx=5, pady=3)
|
||||
|
||||
field_data_axis_label = Label(field_data_frame, text=axis_labels_hh[i])
|
||||
field_data_axis_label.grid(row=i + 1, column=3, padx=10, pady=3)
|
||||
|
||||
field_data_axis_data = Label(field_data_frame, textvariable=self.target_value_vars[i])
|
||||
field_data_axis_data.grid(row=i + 1, column=4, padx=(20, 0), pady=3)
|
||||
|
||||
field_data_axis_units = Label(field_data_frame, text="\u03BCT")
|
||||
field_data_axis_units.grid(row=i + 1, column=5, padx=5, pady=3)
|
||||
row_counter += 1
|
||||
|
||||
# Calibration start and reinitialize buttons
|
||||
@@ -819,9 +1117,14 @@ class CalibrateAmbientField(Frame):
|
||||
|
||||
# Get new field data
|
||||
new_field = g.MAGNETOMETER.field
|
||||
global target_values
|
||||
for i in range(3):
|
||||
# Display in uT
|
||||
self.field_value_vars[i].set("{:.3f}".format(new_field[i] * 1e6))
|
||||
self.target_value_vars[i].set("{:.3f}".format(target_values[i] * 1e6))
|
||||
self.field_value_vars[3].set(
|
||||
"{:.3f}".format(np.sqrt(new_field[0] ** 2 + new_field[1] ** 2 + new_field[2] ** 2) * 1e6))
|
||||
self.target_value_vars[3].set("{:.3f}".format(target_values[3] * 1e6))
|
||||
|
||||
# Get mpi messages from calibration procedures
|
||||
try:
|
||||
@@ -832,7 +1135,7 @@ class CalibrateAmbientField(Frame):
|
||||
if cmd == 'finished':
|
||||
self.reactivate_buttons()
|
||||
elif cmd == 'failed':
|
||||
messagebox.showerror("Calibration error", "Error occured during calibration:\n{}".format(arg))
|
||||
messagebox.showerror("Calibration error", "Error occurred during calibration:\n{}".format(arg))
|
||||
self.reactivate_buttons()
|
||||
elif cmd == 'progress':
|
||||
self.calibration_procedure_progress_var.set(min(int(arg * 100), 100))
|
||||
@@ -1014,13 +1317,19 @@ class CalibrateMagnetometerSimple(Frame):
|
||||
# UI variables
|
||||
self.connected_state_var = StringVar(value="Not connected")
|
||||
self.field_value_vars = [StringVar(value="No data"),
|
||||
StringVar(value="No data"),
|
||||
StringVar(value="No data"),
|
||||
StringVar(value="No data")]
|
||||
self.target_value_vars = [StringVar(value="No data"),
|
||||
StringVar(value="No data"),
|
||||
StringVar(value="No data"),
|
||||
StringVar(value="No data")]
|
||||
self.calibration_procedure_progress_var = IntVar(value=0)
|
||||
# Calibration parameters
|
||||
self.calibration_points_var = IntVar(value=8)
|
||||
self.mag_field_magnitude_var = DoubleVar(value=100)
|
||||
self.calibration_interval_var = DoubleVar(value=5)
|
||||
self.compensated_field_var = BooleanVar(value=False)
|
||||
# Calibration results
|
||||
self.sensitivity_result_vars = [StringVar(), StringVar(), StringVar()]
|
||||
self.offset_result_vars = [StringVar(), StringVar(), StringVar()]
|
||||
@@ -1056,16 +1365,26 @@ class CalibrateMagnetometerSimple(Frame):
|
||||
field_data_frame.grid(row=row_counter, column=0, sticky="nw")
|
||||
field_data_label = Label(field_data_frame, text="Field data:", font=SUB_HEADER_FONT)
|
||||
field_data_label.grid(row=0, column=0, padx=10, pady=3, sticky="nw")
|
||||
axis_labels = ['X:', 'Y:', 'Z:']
|
||||
for i in range(3):
|
||||
field_data_axis_label = Label(field_data_frame, text=axis_labels[i])
|
||||
field_data_axis_label.grid(row=i, column=1, padx=10, pady=3)
|
||||
axis_labels_mgm = ['X_mgm:', 'Y_mgm:', 'Z_mgm:', 'T_mgm:']
|
||||
axis_labels_hh = ['X_hh:', 'Y_hh:', 'Z_hh:', 'T_hh:']
|
||||
for i in range(4):
|
||||
field_data_axis_label = Label(field_data_frame, text=axis_labels_mgm[i])
|
||||
field_data_axis_label.grid(row=i + 1, column=0, padx=10, pady=3)
|
||||
|
||||
field_data_axis_data = Label(field_data_frame, textvariable=self.field_value_vars[i])
|
||||
field_data_axis_data.grid(row=i, column=2, padx=(20, 0), pady=3)
|
||||
field_data_axis_data.grid(row=i + 1, column=1, padx=(20, 0), pady=3)
|
||||
|
||||
field_data_axis_units = Label(field_data_frame, text="\u03BCT")
|
||||
field_data_axis_units.grid(row=i, column=3, padx=5, pady=3)
|
||||
field_data_axis_units.grid(row=i + 1, column=2, padx=5, pady=3)
|
||||
|
||||
field_data_axis_label = Label(field_data_frame, text=axis_labels_hh[i])
|
||||
field_data_axis_label.grid(row=i + 1, column=3, padx=10, pady=3)
|
||||
|
||||
field_data_axis_data = Label(field_data_frame, textvariable=self.target_value_vars[i])
|
||||
field_data_axis_data.grid(row=i + 1, column=4, padx=(20, 0), pady=3)
|
||||
|
||||
field_data_axis_units = Label(field_data_frame, text="\u03BCT")
|
||||
field_data_axis_units.grid(row=i + 1, column=5, padx=5, pady=3)
|
||||
row_counter += 1
|
||||
|
||||
# Centered controls
|
||||
@@ -1086,43 +1405,39 @@ class CalibrateMagnetometerSimple(Frame):
|
||||
calibration_point_nr__unit = Label(controls_frame, text="- (> 8 points)")
|
||||
calibration_point_nr__unit.grid(row=1, column=2, pady=5, sticky="nw")
|
||||
# Measurement interval
|
||||
calibration_point_nr_label = Label(controls_frame, text="Measurement interval [s]")
|
||||
calibration_point_nr_label.grid(row=2, column=0, pady=5, sticky="nw")
|
||||
calibration_point_nr_entry = Entry(controls_frame, textvariable=self.calibration_interval_var)
|
||||
calibration_point_nr_entry.grid(row=2, column=1, pady=5, sticky="nw")
|
||||
calibration_point_nr_unit = Label(controls_frame, text="s (> 2 s)")
|
||||
calibration_point_nr_unit.grid(row=2, column=2, pady=5, sticky="nw")
|
||||
# Measurement interval
|
||||
calibration_point_nr_label = Label(controls_frame, text="Measurement interval [s]")
|
||||
calibration_point_nr_label = Label(controls_frame, text="Measurement interval")
|
||||
calibration_point_nr_label.grid(row=2, column=0, pady=5, sticky="nw")
|
||||
calibration_point_nr_entry = Entry(controls_frame, textvariable=self.calibration_interval_var)
|
||||
calibration_point_nr_entry.grid(row=2, column=1, pady=5, sticky="nw")
|
||||
calibration_point_nr_unit = Label(controls_frame, text="s (> 2 s)")
|
||||
calibration_point_nr_unit.grid(row=2, column=2, pady=5, sticky="nw")
|
||||
# Compensated field Checkbox
|
||||
self.compensate_checkbox = Checkbutton(controls_frame, text="Compensate ambient field",
|
||||
variable=self.compensated_field_var, onvalue=True, offvalue=False)
|
||||
self.compensate_checkbox.grid(row=3, column=0, pady=5, sticky="nw")
|
||||
# Calibration start buttons
|
||||
start_button_frame = Frame(controls_frame)
|
||||
start_button_frame.grid(row=3, column=0, columnspan=1, sticky="nw")
|
||||
start_button_frame.grid(row=4, column=0, columnspan=1, sticky="nw")
|
||||
self.start_calibration_button = Button(start_button_frame, text="Start Calibration",
|
||||
command=self.start_calibration_procedure,
|
||||
pady=5, padx=5, font=SMALL_BUTTON_FONT)
|
||||
self.start_calibration_button.grid(row=0, column=0, padx=10, pady=(30, 10), sticky="we")
|
||||
# Reinitialize button
|
||||
reinitialize_button_frame = Frame(controls_frame)
|
||||
reinitialize_button_frame.grid(row=3, column=1, columnspan=1)
|
||||
reinitialize_button_frame.grid(row=4, column=1, columnspan=1)
|
||||
self.reinitialize_button = Button(reinitialize_button_frame, text="Reinitialize",
|
||||
command=self.reinitialize,
|
||||
pady=5, padx=5, font=SMALL_BUTTON_FONT)
|
||||
self.reinitialize_button.grid(row=0, column=0, padx=10, pady=(30, 10), sticky="we")
|
||||
# Calibration progress bar
|
||||
progress_bar_frame = Frame(controls_frame)
|
||||
progress_bar_frame.grid(row=4, column=0, columnspan=2)
|
||||
progress_bar_frame.grid(row=5, column=0, columnspan=2)
|
||||
calibration_procedure_progress_label = Label(progress_bar_frame, text="Progress:")
|
||||
calibration_procedure_progress_label.grid(row=0, column=0, padx=10, pady=10, sticky="nw")
|
||||
calibration_procedure_progress = ttk.Progressbar(progress_bar_frame,
|
||||
length=240,
|
||||
variable=self.calibration_procedure_progress_var)
|
||||
calibration_procedure_progress.grid(row=0, column=1, padx=10, pady=10, sticky="nw")
|
||||
row_counter += 1
|
||||
|
||||
# CENTER COLUMN
|
||||
# Magnetometer calibration results
|
||||
@@ -1209,7 +1524,8 @@ class CalibrateMagnetometerSimple(Frame):
|
||||
# Notes on the calibration method
|
||||
calibration_method_notes_frame = LabelFrame(self.right_column, text="Calibration method notes:")
|
||||
calibration_method_notes_frame.grid(row=row_counter, column=1, padx=(100, 0), pady=20, sticky="nw")
|
||||
label = "-Implementation according to Zikmund et al. [DOI: 10.1109/I2MTC.2014.6860790]\n-Points created by Fibonacci sphere\n-Only accounts for hard-iron offset and MGM scaling errors!"
|
||||
label = "-Implementation according to Zikmund et al. [DOI: 10.1109/I2MTC.2014.6860790]\n" \
|
||||
"-Points created by Fibonacci sphere\n-Only accounts for hard-iron offset and MGM scaling errors!"
|
||||
calibration_method_notes = Label(calibration_method_notes_frame, anchor='w', justify='left', text=label)
|
||||
calibration_method_notes.grid(row=3, column=0, padx=5, pady=5, sticky="nw")
|
||||
|
||||
@@ -1301,6 +1617,10 @@ class CalibrateMagnetometerSimple(Frame):
|
||||
for i in range(3):
|
||||
# Display in uT
|
||||
self.field_value_vars[i].set("{:.3f}".format(new_field[i] * 1e6))
|
||||
self.target_value_vars[i].set("{:.3f}".format(target_values[i] * 1e6))
|
||||
self.field_value_vars[3].set(
|
||||
"{:.3f}".format(np.sqrt(new_field[0] ** 2 + new_field[1] ** 2 + new_field[2] ** 2) * 1e6))
|
||||
self.target_value_vars[3].set("{:.3f}".format(target_values[3] * 1e6))
|
||||
|
||||
# Get mpi messages from calibration procedures
|
||||
try:
|
||||
@@ -1311,7 +1631,7 @@ class CalibrateMagnetometerSimple(Frame):
|
||||
if cmd == 'finished':
|
||||
self.reactivate_buttons()
|
||||
elif cmd == 'failed':
|
||||
messagebox.showerror("Calibration error", "Error occured during calibration:\n{}".format(arg))
|
||||
messagebox.showerror("Calibration error", "Error occurred during calibration:\n{}".format(arg))
|
||||
self.reactivate_buttons()
|
||||
elif cmd == 'progress':
|
||||
self.calibration_procedure_progress_var.set(min(int(arg * 100), 100))
|
||||
@@ -1373,12 +1693,14 @@ class CalibrateMagnetometerSimple(Frame):
|
||||
try:
|
||||
calibration_points = self.calibration_points_var.get()
|
||||
calibration_interval = self.calibration_interval_var.get()
|
||||
compensated_field = self.compensated_field_var.get()
|
||||
calibration_mag_field = self.mag_field_magnitude_var.get() * 1e-6 # converted to micro Tesla
|
||||
if calibration_mag_field <= 0 or calibration_mag_field > g.MAG_MAG_FIELD:
|
||||
raise MagFieldOutOfBounds
|
||||
self.calibration_thread = MagnetometerCalibrationSimple(self.view_mpi_queue,
|
||||
calibration_points,
|
||||
calibration_interval,
|
||||
compensated_field,
|
||||
calibration_mag_field,
|
||||
self.mgm_to_helmholtz_cos_trans)
|
||||
self.calibration_thread.start()
|
||||
@@ -1497,13 +1819,20 @@ class CalibrateMagnetometerComplete(Frame):
|
||||
# UI variables
|
||||
self.connected_state_var = StringVar(value="Not connected")
|
||||
self.field_value_vars = [StringVar(value="No data"),
|
||||
StringVar(value="No data"),
|
||||
StringVar(value="No data"),
|
||||
StringVar(value="No data")]
|
||||
self.target_value_vars = [StringVar(value="No data"),
|
||||
StringVar(value="No data"),
|
||||
StringVar(value="No data"),
|
||||
StringVar(value="No data")]
|
||||
self.calibration_procedure_progress_var = IntVar(value=0)
|
||||
# Calibration parameters
|
||||
self.mag_field_magnitude_var = DoubleVar(value=100)
|
||||
self.calibration_points_var = IntVar(value=8)
|
||||
self.calibration_interval_var = DoubleVar(value=5)
|
||||
self.calibration_oversampling_var = DoubleVar(value=1)
|
||||
self.compensated_field_var = BooleanVar(value=True)
|
||||
# Calibration results
|
||||
self.a_mat_result_vars = [[DoubleVar(), DoubleVar(), DoubleVar()],
|
||||
[DoubleVar(), DoubleVar(), DoubleVar()],
|
||||
@@ -1549,16 +1878,26 @@ class CalibrateMagnetometerComplete(Frame):
|
||||
field_data_frame.grid(row=row_counter, column=0, sticky="nw")
|
||||
field_data_label = Label(field_data_frame, text="Field data:", font=SUB_HEADER_FONT)
|
||||
field_data_label.grid(row=0, column=0, padx=10, pady=3, sticky="nw")
|
||||
axis_labels = ['X:', 'Y:', 'Z:']
|
||||
for i in range(3):
|
||||
field_data_axis_label = Label(field_data_frame, text=axis_labels[i])
|
||||
field_data_axis_label.grid(row=i, column=1, padx=10, pady=3)
|
||||
axis_labels_mgm = ['X_mgm:', 'Y_mgm:', 'Z_mgm:', 'T_mgm:']
|
||||
axis_labels_hh = ['X_hh:', 'Y_hh:', 'Z_hh:', 'T_hh:']
|
||||
for i in range(4):
|
||||
field_data_axis_label = Label(field_data_frame, text=axis_labels_mgm[i])
|
||||
field_data_axis_label.grid(row=i + 1, column=0, padx=10, pady=3)
|
||||
|
||||
field_data_axis_data = Label(field_data_frame, textvariable=self.field_value_vars[i])
|
||||
field_data_axis_data.grid(row=i, column=2, padx=(20, 0), pady=3)
|
||||
field_data_axis_data.grid(row=i + 1, column=1, padx=(20, 0), pady=3)
|
||||
|
||||
field_data_axis_units = Label(field_data_frame, text="\u03BCT")
|
||||
field_data_axis_units.grid(row=i, column=3, padx=5, pady=3)
|
||||
field_data_axis_units.grid(row=i + 1, column=2, padx=5, pady=3)
|
||||
|
||||
field_data_axis_label = Label(field_data_frame, text=axis_labels_hh[i])
|
||||
field_data_axis_label.grid(row=i + 1, column=3, padx=10, pady=3)
|
||||
|
||||
field_data_axis_data = Label(field_data_frame, textvariable=self.target_value_vars[i])
|
||||
field_data_axis_data.grid(row=i + 1, column=4, padx=(20, 0), pady=3)
|
||||
|
||||
field_data_axis_units = Label(field_data_frame, text="\u03BCT")
|
||||
field_data_axis_units.grid(row=i + 1, column=5, padx=5, pady=3)
|
||||
row_counter += 1
|
||||
|
||||
# Centered controls
|
||||
@@ -1579,35 +1918,47 @@ class CalibrateMagnetometerComplete(Frame):
|
||||
calibration_point_nr__unit = Label(controls_frame, text="- (> 8 points)")
|
||||
calibration_point_nr__unit.grid(row=1, column=2, pady=5, sticky="nw")
|
||||
# Measurement interval
|
||||
calibration_point_nr_label = Label(controls_frame, text="Measurement interval [s]")
|
||||
calibration_point_nr_label = Label(controls_frame, text="Measurement interval")
|
||||
calibration_point_nr_label.grid(row=2, column=0, pady=5, sticky="nw")
|
||||
calibration_point_nr_entry = Entry(controls_frame, textvariable=self.calibration_interval_var)
|
||||
calibration_point_nr_entry.grid(row=2, column=1, pady=5, sticky="nw")
|
||||
calibration_point_nr_unit = Label(controls_frame, text="s (> 2 s)")
|
||||
calibration_point_nr_unit.grid(row=2, column=2, pady=5, sticky="nw")
|
||||
# Oversampling
|
||||
calibration_point_nr_label = Label(controls_frame, text="Oversampling (samples/set point)")
|
||||
calibration_point_nr_label.grid(row=3, column=0, pady=5, sticky="nw")
|
||||
calibration_point_nr_entry = Entry(controls_frame, textvariable=self.calibration_oversampling_var)
|
||||
calibration_point_nr_entry.grid(row=3, column=1, pady=5, sticky="nw")
|
||||
calibration_point_nr_unit = Label(controls_frame, text="- (>= 1)")
|
||||
calibration_point_nr_unit.grid(row=3, column=2, pady=5, sticky="nw")
|
||||
# Compensated field Checkbox
|
||||
self.compensate_checkbox = Checkbutton(controls_frame, text="Compensate ambient field",
|
||||
variable=self.compensated_field_var, onvalue=True, offvalue=False)
|
||||
self.compensate_checkbox.grid(row=4, column=0, pady=5, sticky="nw")
|
||||
# Calibration start buttons
|
||||
start_button_frame = Frame(controls_frame)
|
||||
start_button_frame.grid(row=3, column=0, columnspan=1, sticky="nw")
|
||||
start_button_frame.grid(row=5, column=0, columnspan=1, sticky="nw")
|
||||
self.start_calibration_button = Button(start_button_frame, text="Start Calibration",
|
||||
command=self.start_calibration_procedure,
|
||||
pady=5, padx=5, font=SMALL_BUTTON_FONT)
|
||||
self.start_calibration_button.grid(row=0, column=0, padx=10, pady=(30, 10), sticky="we")
|
||||
# Reinitialize button
|
||||
reinitialize_button_frame = Frame(controls_frame)
|
||||
reinitialize_button_frame.grid(row=3, column=1, columnspan=1)
|
||||
reinitialize_button_frame.grid(row=5, column=1, columnspan=1)
|
||||
self.reinitialize_button = Button(reinitialize_button_frame, text="Reinitialize",
|
||||
command=self.reinitialize,
|
||||
pady=5, padx=5, font=SMALL_BUTTON_FONT)
|
||||
self.reinitialize_button.grid(row=0, column=0, padx=10, pady=(30, 10), sticky="we")
|
||||
# Calibration progress bar
|
||||
progress_bar_frame = Frame(controls_frame)
|
||||
progress_bar_frame.grid(row=4, column=0, columnspan=2)
|
||||
progress_bar_frame.grid(row=6, column=0, columnspan=2)
|
||||
calibration_procedure_progress_label = Label(progress_bar_frame, text="Progress:")
|
||||
calibration_procedure_progress_label.grid(row=0, column=0, padx=10, pady=10, sticky="nw")
|
||||
calibration_procedure_progress = ttk.Progressbar(progress_bar_frame,
|
||||
length=240,
|
||||
variable=self.calibration_procedure_progress_var)
|
||||
calibration_procedure_progress.grid(row=0, column=1, padx=10, pady=10, sticky="nw")
|
||||
|
||||
row_counter += 1
|
||||
|
||||
# CENTER COLUMN
|
||||
@@ -1696,19 +2047,7 @@ class CalibrateMagnetometerComplete(Frame):
|
||||
results_label_unit = Label(calibration_results_frame, text="-")
|
||||
results_label_unit.grid(row=row_counter + row, column=1 + 3, padx=5, pady=5, sticky="nw")
|
||||
row_counter += 3
|
||||
"""
|
||||
# A_mat
|
||||
results_label_a_mat_inv = Label(calibration_results_frame, text="A =")
|
||||
results_label_a_mat_inv.grid(row=row_counter+1, column=0, padx=5, pady=5, sticky="nw")
|
||||
for row in range(3):
|
||||
for column in range(3):
|
||||
axis_data = Entry(calibration_results_frame,
|
||||
textvariable=self.a_mat_result_vars[row][column],
|
||||
width=15,
|
||||
state='readonly')
|
||||
axis_data.grid(row=row_counter+row, column=1 + column, padx=5, pady=5, sticky="nw")
|
||||
row_counter += 3
|
||||
"""
|
||||
|
||||
# b
|
||||
results_label_a_mat_inv = Label(calibration_results_frame, text="b^T =")
|
||||
results_label_a_mat_inv.grid(row=row_counter, column=0, padx=5, pady=5, sticky="nw")
|
||||
@@ -1746,11 +2085,15 @@ class CalibrateMagnetometerComplete(Frame):
|
||||
# Notes on the calibration method
|
||||
calibration_method_notes_frame = LabelFrame(self.right_column, text="Calibration method notes:")
|
||||
calibration_method_notes_frame.grid(row=0, column=0, padx=(100, 0), pady=20, sticky="nw")
|
||||
label = "-Implementation of calibration according to Kok et al. [ISBN: 978-0-9824438-5-9]\n-Implementation of ellipsoid fit according to Li et al. [DOI: 10.1109/GMAP.2004.1290055]\n-Points created by Fibonacci sphere\n-Accounts for soft-iron (A matrix) and hard-iron (b offset vector) effects!\n-Measured to calibrated field function: h=A^-1 (h_m-b)"
|
||||
label = "-Implementation of calibration according to Kok et al. [ISBN: 978-0-9824438-5-9]\n" \
|
||||
"-Implementation of ellipsoid fit according to Li et al. [DOI: 10.1109/GMAP.2004.1290055]\n" \
|
||||
"-Points created by Fibonacci sphere\n" \
|
||||
"-Accounts for soft-iron (A matrix) and hard-iron (b offset vector) effects!\n" \
|
||||
"-Measured to calibrated field function: h=A^-1 (h_m-b)"
|
||||
calibration_method_notes = Label(calibration_method_notes_frame, anchor='w', justify='left', text=label)
|
||||
calibration_method_notes.grid(row=0, column=0, padx=5, pady=5, sticky="nw")
|
||||
|
||||
# Plot frame (overwrite plotframe)
|
||||
# Plot frame
|
||||
plot_frame = LabelFrame(self.right_column, text="Result plots:")
|
||||
plot_frame.grid(row=1, column=0, padx=(100, 0), pady=20, sticky="nw")
|
||||
fig1 = plt.figure('MGM_cal_complete_left', figsize=(2.5, 3), dpi=100)
|
||||
@@ -1773,8 +2116,6 @@ class CalibrateMagnetometerComplete(Frame):
|
||||
pady=5, padx=5)
|
||||
self.export_figure_right_button.grid(row=1, column=2, padx=5, pady=5)
|
||||
|
||||
# FLAG Buttons
|
||||
|
||||
# This starts an endless polling loop
|
||||
self.update_view()
|
||||
|
||||
@@ -1796,6 +2137,10 @@ class CalibrateMagnetometerComplete(Frame):
|
||||
for i in range(3):
|
||||
# Display in uT
|
||||
self.field_value_vars[i].set("{:.3f}".format(new_field[i] * 1e6))
|
||||
self.target_value_vars[i].set("{:.3f}".format(target_values[i] * 1e6))
|
||||
self.field_value_vars[3].set(
|
||||
"{:.3f}".format(np.sqrt(new_field[0] ** 2 + new_field[1] ** 2 + new_field[2] ** 2) * 1e6))
|
||||
self.target_value_vars[3].set("{:.3f}".format(target_values[3] * 1e6))
|
||||
|
||||
# Get mpi messages from calibration procedures
|
||||
try:
|
||||
@@ -1806,7 +2151,7 @@ class CalibrateMagnetometerComplete(Frame):
|
||||
if cmd == 'finished':
|
||||
self.reactivate_buttons()
|
||||
elif cmd == 'failed':
|
||||
messagebox.showerror("Calibration error", "Error occured during calibration:\n{}".format(arg))
|
||||
messagebox.showerror("Calibration error", "Error occurred during calibration:\n{}".format(arg))
|
||||
self.reactivate_buttons()
|
||||
elif cmd == 'progress':
|
||||
self.calibration_procedure_progress_var.set(min(int(arg * 100), 100))
|
||||
@@ -1840,9 +2185,9 @@ class CalibrateMagnetometerComplete(Frame):
|
||||
self.b_result_vars[row].set("{:.3e}".format(result[0]['b'][row][0]))
|
||||
self.n_result_vars[row].set("{:.3e}".format(result[0]['n'][row][0]))
|
||||
for column in range(3):
|
||||
self.a_mat_result_vars[row][column].set("{:.6f}".format(result[0]['a_mat'][row][column]))
|
||||
self.a_mat_inv_result_vars[row][column].set("{:.6f}".format(result[0]['a_mat_inv'][row][column]))
|
||||
self.q_mat_result_vars[row][column].set("{:.6f}".format(result[0]['q_mat'][row][column]))
|
||||
self.a_mat_result_vars[row][column].set(round(result[0]['a_mat'][row][column], 6))
|
||||
self.a_mat_inv_result_vars[row][column].set(round(result[0]['a_mat_inv'][row][column], 6))
|
||||
self.q_mat_result_vars[row][column].set(round(result[0]['q_mat'][row][column], 6))
|
||||
|
||||
# Populate clipboard string
|
||||
self.clipboard_calibration_results_complete = "A_mat [-]\n"
|
||||
@@ -1880,24 +2225,20 @@ class CalibrateMagnetometerComplete(Frame):
|
||||
calibration_points = self.calibration_points_var.get()
|
||||
calibration_interval = self.calibration_interval_var.get()
|
||||
calibration_mag_field = self.mag_field_magnitude_var.get() * 1e-6 # converted to micro Tesla
|
||||
calibration_oversampling = self.calibration_oversampling_var.get()
|
||||
compensated_field = self.compensated_field_var.get()
|
||||
if calibration_mag_field <= 0 or calibration_mag_field > g.MAG_MAG_FIELD:
|
||||
raise MagFieldOutOfBounds
|
||||
self.calibration_thread = MagnetometerCalibrationComplete(self.view_mpi_queue,
|
||||
calibration_points,
|
||||
calibration_interval,
|
||||
calibration_mag_field,
|
||||
calibration_oversampling,
|
||||
compensated_field,
|
||||
self.mgm_to_helmholtz_cos_trans,
|
||||
self.right_column)
|
||||
self.calibration_thread.start()
|
||||
self.deactivate_buttons()
|
||||
# Use collected data to build and solve system of equations
|
||||
sensor_parameters, mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m, cal_x, cal_y, cal_z, mag_amp_avg_set = MagnetometerCalibrationComplete.solve_system(raw_data, [[1, 0, 0], [0, 1, 0], [0, 0, 1]])
|
||||
# Pass results to UI
|
||||
self.put_message('calibration_data', {'results': sensor_parameters, 'raw_data': raw_data})
|
||||
MagnetometerCalibrationComplete.plot_magnetometer_calibration(self.right_column,
|
||||
mag_x_set, mag_y_set, mag_z_set,
|
||||
mag_x_m, mag_y_m, mag_z_m,
|
||||
cal_x, cal_y, cal_z, mag_amp_avg_set)
|
||||
except MagFieldOutOfBounds as e:
|
||||
messagebox.showwarning("Calibration failed", "\n{}".format(e))
|
||||
except (DeviceAccessError, TclError) as e:
|
||||
@@ -1929,10 +2270,11 @@ class CalibrateMagnetometerComplete(Frame):
|
||||
# Execute calibration function and display results
|
||||
sensor_parameters, mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m, cal_x, cal_y, cal_z, mag_amp_avg_set = MagnetometerCalibrationComplete.solve_system(
|
||||
raw_data, mgm_to_helmholtz_cos_trans)
|
||||
MagnetometerCalibrationComplete.plot_magnetometer_calibration(self.right_column,
|
||||
mag_x_set, mag_y_set, mag_z_set,
|
||||
mag_x_m, mag_y_m, mag_z_m,
|
||||
cal_x, cal_y, cal_z, mag_amp_avg_set)
|
||||
MagnetometerCalibrationComplete.plot_magnetometer_calibration(self.right_column, mag_x_set, mag_y_set,
|
||||
mag_z_set,
|
||||
mag_x_m,
|
||||
mag_y_m, mag_z_m, cal_x, cal_y,
|
||||
cal_z, mag_amp_avg_set)
|
||||
# Pass results to UI
|
||||
self.put_message('calibration_data', {'results': sensor_parameters, 'raw_data': raw_data})
|
||||
except TypeError:
|
||||
@@ -2068,7 +2410,7 @@ class HardwareConfiguration(Frame):
|
||||
self.file_select_frame.grid_columnconfigure(ALL, weight=1)
|
||||
self.file_select_frame.grid(row=row_counter, column=0, sticky=W, padx=20)
|
||||
|
||||
# Create and place buttons
|
||||
# Create and place buttons in frame
|
||||
# button to load a config file:
|
||||
load_file_button = Button(self.file_select_frame, text="Load config file...", command=self.load_config,
|
||||
pady=5, padx=5, font=SMALL_BUTTON_FONT)
|
||||
@@ -2108,10 +2450,7 @@ class HardwareConfiguration(Frame):
|
||||
info_label.grid(row=row, column=2, sticky=W)
|
||||
row += 1
|
||||
|
||||
row_counter += 1
|
||||
|
||||
# Label(self, text="", pady=0).grid(row=row_counter, column=0) # add spacer
|
||||
row_counter += 1
|
||||
row_counter += 2
|
||||
|
||||
# setup main entry field for operational constants
|
||||
# setup frame:
|
||||
@@ -2163,7 +2502,6 @@ class HardwareConfiguration(Frame):
|
||||
|
||||
self.update_fields() # set current values from config file
|
||||
|
||||
# Label(self, text="", pady=3).grid(row=row_counter, column=0) # add spacer
|
||||
row_counter += 1
|
||||
|
||||
# Setup buttons to implement and restore defaults
|
||||
@@ -2173,7 +2511,7 @@ class HardwareConfiguration(Frame):
|
||||
self.buttons_frame.grid_columnconfigure(ALL, weight=1)
|
||||
self.buttons_frame.grid(row=row_counter, column=0, sticky=W, padx=20)
|
||||
|
||||
# Create and place buttons
|
||||
# Create and place buttons in frame
|
||||
# button to read the values from all fields, update the config and reinitialize the test bench
|
||||
implement_button = Button(self.buttons_frame, text="Update and Reinitialize", command=self.implement,
|
||||
pady=5, padx=5, font=BIG_BUTTON_FONT)
|
||||
@@ -2184,7 +2522,6 @@ class HardwareConfiguration(Frame):
|
||||
restore_button.grid(row=0, column=1, padx=5)
|
||||
|
||||
row_counter += 1
|
||||
# Label(self, text="", pady=3).grid(row=row_counter, column=0) # add spacer
|
||||
|
||||
def page_switch(self): # function that is called when switching to this window
|
||||
self.update_fields() # update values in the entry fields from config
|
||||
@@ -2306,7 +2643,7 @@ class HardwareConfiguration(Frame):
|
||||
# open file selection dialogue and save path of selected file
|
||||
filename = filedialog.asksaveasfilename(initialdir=directory, title="Save config to file",
|
||||
filetypes=([("Config File", "*.ini*")]),
|
||||
defaultextension=[("Config File", "*.ini*")])
|
||||
defaultextension="*.ini*")
|
||||
|
||||
if filename == '': # this happens when file selection window is closed without selecting a file
|
||||
ui_print("No file selected, could not save config.")
|
||||
@@ -2492,8 +2829,8 @@ class ConfigureLogging(Frame):
|
||||
|
||||
def stop_logging(self): # stop the data logging, called by "Stop Logging" button
|
||||
ui_print("Stopped data logging. Remember to save data to file!")
|
||||
self.regular_logging = False # tell everything its time to stop periodic logging
|
||||
self.event_logging = False # tell everything its time to stop logging on test bench commands
|
||||
self.regular_logging = False # tell everything it is time to stop periodic logging
|
||||
self.event_logging = False # tell everything it is time to stop logging on test bench commands
|
||||
self.write_to_file_button["state"] = "normal" # enable write to file button
|
||||
self.stop_logging_button["state"] = "disabled" # disable stop logging button
|
||||
self.start_logging_button["state"] = "normal" # enable start logging button
|
||||
@@ -2509,7 +2846,7 @@ class ConfigureLogging(Frame):
|
||||
else: # a valid filename was selected
|
||||
log.write_to_file(filepath) # write logged data to the file
|
||||
|
||||
def clear_data(self): # called on button press, asks user if he want to save logged data and then deletes it
|
||||
def clear_data(self): # called on button press, asks user if logged data should be saved and then deletes it
|
||||
if log.unsaved_data: # there is logged data that has not been written to a file yet
|
||||
# open pop-up to ask user if he wants to save the data or cancel clearing it:
|
||||
save_log = messagebox.askyesnocancel("Save log data?", "There seems to be unsaved logging data. "
|
||||
@@ -2671,9 +3008,16 @@ class StatusDisplay(Frame):
|
||||
|
||||
def update_label_poll_method(self):
|
||||
"""Infinite loop to poll for status updates to display"""
|
||||
global target_values
|
||||
try:
|
||||
new_status = self.update_label_queue.get(block=False) # Blocks until new data is available.
|
||||
self.update_labels(new_status)
|
||||
target_values = [new_status['axes'][0]['target_field'],
|
||||
new_status['axes'][1]['target_field'],
|
||||
new_status['axes'][2]['target_field'],
|
||||
np.sqrt(new_status['axes'][0]['target_field'] ** 2 +
|
||||
new_status['axes'][1]['target_field'] ** 2 +
|
||||
new_status['axes'][2]['target_field'] ** 2)]
|
||||
except Empty:
|
||||
pass
|
||||
self.controller.after(200, self.update_label_poll_method)
|
||||
|
||||
@@ -31,9 +31,28 @@ def save_dict_list_to_csv(filename, data, query_path=False):
|
||||
|
||||
csv_writer.writeheader()
|
||||
for row in data:
|
||||
print(row)
|
||||
csv_writer.writerow(row)
|
||||
|
||||
|
||||
def save_dict_list_to_csv2(filename, data, query_path=False):
|
||||
"""Creates a csv file under the specified path containing one row for each dict in the list 'data'.
|
||||
The file receives a header containing the keys of the first dict entry.
|
||||
Each dict should use the same keys."""
|
||||
if query_path:
|
||||
filename = filedialog.asksaveasfilename(initialfile=filename, title="Select csv save location...",
|
||||
filetypes=(("CSV", "*.csv"),))
|
||||
|
||||
with open(filename, mode='w', newline='') as csv_file:
|
||||
fieldnames = data[0].keys()
|
||||
csv_writer = csv.DictWriter(csv_file, fieldnames=fieldnames, delimiter=',', quotechar='"',
|
||||
quoting=csv.QUOTE_NONNUMERIC)
|
||||
|
||||
csv_writer.writeheader()
|
||||
for row in data:
|
||||
csv_writer.writerow(data[row]) # this line makes issues in original file
|
||||
|
||||
|
||||
def load_dict_list_from_csv(filename, query_path=False):
|
||||
""" Reads a csv file under the specified path containing one row for each dict in the list 'data'.
|
||||
The file header containing the keys of the first dict entry is deleted upon reading.
|
||||
|
||||
Reference in New Issue
Block a user