25 Commits

Author SHA1 Message Date
mkoller 767f6e39f8 Plot formatting improved 2023-03-03 10:54:16 +01:00
mkoller b3edafa796 Plotting only part of csv if csv has more than 1000 plot points 2023-03-03 10:33:07 +01:00
mkoller 9184dd1271 Update csv_threading.py 2023-03-02 16:35:47 +01:00
mkoller 42b9590159 Remove numpy requirement to stop dependency conflict 2023-03-02 11:59:42 +01:00
mkoller a49f98ba63 Updating canvas slows down loop. only increment it if new field is set
Takes ca 50-100ms
2023-02-24 11:45:33 +01:00
mkoller c0fb152efa Updating matplotlib version likely solves tkinter plot_canvas.draw() threading issue 2023-02-24 11:40:25 +01:00
mkoller ae71a8d31b Tkinter plot_canvas.draw() Crash
Tkinter might have issues with thread safety:
https://github.com/matplotlib/matplotlib/issues/13293
2023-02-24 11:18:15 +01:00
mkoller c138facd59 Minor formatting warnings for tkinter gui fixed 2023-02-23 18:30:08 +01:00
mkoller 70fa24f905 Introduced red status indicator line, minor warning fixes 2023-02-23 18:17:01 +01:00
mkoller d0649a1af5 Added compensate field checkbox, minor adaptation 2023-02-23 15:17:20 +01:00
mkoller c8b3a5e0ba Reset csv generation inputs to last reasonable value 2023-02-22 17:14:45 +01:00
mkoller ac475e1b81 Includes rotation rates to plots 2023-02-20 18:27:14 +01:00
mkoller 5662a73345 Introduced adaptable rotation rates 2023-02-20 16:51:30 +01:00
mkoller 6194035099 Introduced checkboxes to control ambient field compensation (untested!) 2023-02-13 09:44:42 +01:00
mkoller 5f161fe09e Delete development file for rotation field 2023-02-11 21:56:46 +01:00
mkoller aa9efdba5a Minor whitespace adaptations 2023-02-11 21:56:17 +01:00
mkoller 0683979b3b Minor bug fixes 2023-02-11 21:55:26 +01:00
mkoller c5a9c2649b Replacing csv sequence decimal and seperator signs throuout code and examples
decimal sign now point, seperator now comma
2023-02-11 21:30:36 +01:00
mkoller 25513cc7c5 Rotation rate csv file generator initial issue added 2023-02-11 21:14:34 +01:00
mkoller 9eb95b56bd Create rotating_field.py
Dummyscript for csv execution sequence -  will be updated and integrated to GUI
2023-02-10 16:42:02 +01:00
mkoller 35a0779b2d Implemented oversampling (untested!) 2023-01-31 10:54:09 +01:00
mkoller e9937b1269 Oversampling x5 2023-01-30 14:31:40 +01:00
mkoller c110605ce7 Added set magnetic field and vector norms to the field data displays
Marius fancy implementation using globals
2023-01-30 14:16:49 +01:00
mkoller 7f6244f337 BUG: Calibration routine uses raw field instead of compensated field
Substracting the ambient field from the set magnetic field values during calibration, no longer substract initial offset measurement from all readings
2023-01-30 14:15:43 +01:00
mkoller 85cad05b6e Gitignore 2023-01-30 07:55:20 +01:00
11 changed files with 867 additions and 282 deletions
+2 -1
View File
@@ -103,5 +103,6 @@ ENV/
config.ini
log.csv
.idea/misc.xml
.idea/misc.xml
.idea/Python-PS2000B.iml
*.xml
*.iml
+23 -23
View File
@@ -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
1 Time (s) Time [s]] xField (T) xField [T] yField (T) yField [T] zField (T) zField [T] Rotation Rate [deg/s]
2 0,5 0.5 0,000015 0.000015 0,000025 0.000025 0,00002 0.00002 1
3 1 1 0,0000155 0.0000155 0,0000245 0.0000245 0,0000205 0.0000205 1
4 1,5 1.5 0,000016 0.000016 0,000024 0.000024 0,000021 0.000021 1
5 2 2 0,0000165 0.0000165 0,0000235 0.0000235 0,0000215 0.0000215 1
6 2,5 2.5 0,000017 0.000017 0,000023 0.000023 0,000022 0.000022 1
7 3 3 0,0000175 0.0000175 0,0000225 0.0000225 0,0000225 0.0000225 1
8 3,5 3.5 0,000018 0.000018 0,000022 0.000022 0,000023 0.000023 1
9 4 4 0,0000185 0.0000185 0,0000215 0.0000215 0,0000235 0.0000235 1
10 4,5 4.5 0,000019 0.000019 0,000021 0.000021 0,000024 0.000024 1
11 5 5 0,0000195 0.0000195 0,0000205 0.0000205 0,0000245 0.0000245 1
12 5,5 5.5 0,00002 0.00002 0,00002 0.00002 0,000025 0.000025 1
13 6 6 0,0000205 0.0000205 0,0000195 0.0000195 0,0000245 0.0000245 1
14 6,5 6.5 0,000021 0.000021 0,000019 0.000019 0,000024 0.000024 1
15 7 7 0,0000215 0.0000215 0,0000185 0.0000185 0,0000235 0.0000235 1
16 7,5 7.5 0,000022 0.000022 0,000018 0.000018 0,000023 0.000023 1
17 8 8 0,0000225 0.0000225 0,0000175 0.0000175 0,0000225 0.0000225 1
18 8,5 8.5 0,000023 0.000023 0,000017 0.000017 0,000022 0.000022 1
19 9 9 0,0000235 0.0000235 0,0000165 0.0000165 0,0000215 0.0000215 1
20 9,5 9.5 0,000024 0.000024 0,000016 0.000016 0,000021 0.000021 1
21 10 10 0,0000245 0.0000245 0,0000155 0.0000155 0,0000205 0.0000205 1
22 10,5 10.5 0,000025 0.000025 0,000015 0.000015 0,00002 0.00002 1
23 11 11 0,000025 0.000025 0,000015 0.000015 0,00002 0.00002 1
+15 -15
View File
@@ -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
1 Time (s) Time [s]] xField (T) xField [T] yField (T) yField [T] zField (T) zField [T] Rotation Rate [deg/s]
2 0 0 0,0000145 0.0000145 0,0000255 0.0000255 0,0000195 0.0000195 1
3 0,1 0.1 -0,000015 -0.000015 0,000025 0.000025 0,00002 0.00002 1
4 0,2 0.2 -0,0000155 -0.0000155 -0,0000245 -0.0000245 0,0000205 0.0000205 1
5 0,3 0.3 -0,000016 -0.000016 -0,000024 -0.000024 -0,000021 -0.000021 1
6 0,4 0.4 0,0000165 0.0000165 -0,0000235 -0.0000235 -0,0000215 -0.0000215 1
7 0,5 0.5 0,000017 0.000017 0,000023 0.000023 -0,000022 -0.000022 1
8 0,6 0.6 0,0000175 0.0000175 0,0000225 0.0000225 0,0000225 0.0000225 1
9 0,7 0.7 -0,000018 -0.000018 -0,000022 -0.000022 0,000023 0.000023 1
10 0,8 0.8 0,0000185 0.0000185 -0,0000215 -0.0000215 -0,0000235 -0.0000235 1
11 0,9 0.9 -0,000019 -0.000019 0,000021 0.000021 -0,000024 -0.000024 1
12 1 1 -0,0000195 -0.0000195 -0,0000205 -0.0000205 -0,0000245 -0.0000245 1
13 1,1 1.1 0,00002 0.00002 0,00002 0.00002 0,000025 0.000025 1
14 1,2 1.2 -0,0000205 -0.0000205 -0,0000195 -0.0000195 -0,0000245 -0.0000245 1
15 1,3 1.3 0,000021 0.000021 0,000019 0.000019 0,000024 0.000024 1
+15 -15
View File
@@ -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 Time (s) Time [s]] xField (T) xField [T] yField (T) yField [T] zField (T) zField [T] Rotation Rate [deg/s]
2 0 0,0000145 0.0000145 0,0000255 0.0000255 0,0000195 0.0000195 1
3 1 -0,000015 -0.000015 0,000025 0.000025 0,00002 0.00002 1
4 2 -0,0000155 -0.0000155 -0,0000245 -0.0000245 0,0000205 0.0000205 1
5 3 -0,000016 -0.000016 -0,000024 -0.000024 -0,000021 -0.000021 1
6 4 0,0000165 0.0000165 -0,0000235 -0.0000235 -0,0000215 -0.0000215 1
7 5 0,000017 0.000017 0,000023 0.000023 -0,000022 -0.000022 1
8 6 0,0000175 0.0000175 0,0000225 0.0000225 0,0000225 0.0000225 1
9 7 -0,000018 -0.000018 -0,000022 -0.000022 0,000023 0.000023 1
10 8 0,0000185 0.0000185 -0,0000215 -0.0000215 -0,0000235 -0.0000235 1
11 9 -0,000019 -0.000019 0,000021 0.000021 -0,000024 -0.000024 1
12 10 -0,0000195 -0.0000195 -0,0000205 -0.0000205 -0,0000245 -0.0000245 1
13 11 0,00002 0.00002 0,00002 0.00002 0,000025 0.000025 1
14 12 -0,0000205 -0.0000205 -0,0000195 -0.0000195 -0,0000245 -0.0000245 1
15 13 0,000021 0.000021 0,000019 0.000019 0,000024 0.000024 1
+12 -12
View File
@@ -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
1 Time (s) Time [s]] xField (T) xField [T] yField (T) yField [T] zField (T) zField [T] Rotation Rate [deg/s]
2 0 0,00015 0.00015 -0,00015 -0.00015 0,00002 0.00002 150 1
3 1 0,00017 0.00017 -0,00017 -0.00017 0,00002 0.00002 170 1
4 2 0,00018 0.00018 -0,00018 -0.00018 0,00002 0.00002 180 1
5 3 0,00019 0.00019 -0,00019 -0.00019 0,00002 0.00002 190 1
6 4 0,0002 0.0002 -0,0002 -0.0002 0,00002 0.00002 200 1
7 5 0,00021 0.00021 0,00021 0.00021 0,00002 0.00002 210 1
8 6 0,00022 0.00022 -0,00022 -0.00022 0,00002 0.00002 220 1
9 7 0,0002 0.0002 -0,0002 -0.0002 0,00002 0.00002 200 1
10 8 0,00018 0.00018 -0,00018 -0.00018 0,00002 0.00002 180 1
11 9 0,00005 0.00005 -0,00005 -0.00005 0,00002 0.00002 50 1
12 10 -0,00004 -0.00004 0,00004 0.00004 0,00002 0.00002 -40 1
+181
View File
@@ -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 Time [s]] xField [T] yField [T] zField [T] Rotation Rate [deg/s]
2 0.0 0.0 -1.7452406437283511e-06 9.998476951563913e-05 1.0
3 1.0 0.0 -3.4883798303794917e-06 9.993913750958131e-05 0.9991
4 2.0 0.0 -5.22923827596647e-06 9.986318173908324e-05 0.9984
5 3.0 0.0 -6.967638394438434e-06 9.97569647453467e-05 0.9979
6 4.0 0.0 -8.703403395519831e-06 9.962053387397024e-05 0.9976
7 5.0 0.0 -1.0436356385678047e-05 9.945392131731718e-05 0.9974999999999999
8 6.0 0.0 -1.2166319471804114e-05 9.925714417869374e-05 0.9976
9 7.0 0.0 -1.3893112867193057e-05 9.903020455830349e-05 0.9979
10 8.0 0.0 -1.561655399941571e-05 9.877308966101715e-05 0.9984
11 9.0 0.0 -1.733645661968247e-05 9.848577192606996e-05 0.9991
12 10.0 0.0 -1.9052629913311656e-05 9.816820917887003e-05 1.0
13 11.0 0.0 -2.076487761093051e-05 9.782034480517372e-05 1.0011
14 12.0 0.0 -2.247299710005592e-05 9.744210794795481e-05 1.0024
15 13.0 0.0 -2.4176778536724306e-05 9.703341372736604e-05 1.0039
16 14.0 0.0 -2.5876003956866184e-05 9.659416348426153e-05 1.0056
17 15.0 0.0 -2.757044638715028e-05 9.612424504781959e-05 1.0075
18 16.0 0.0 -2.9259868955055523e-05 9.562353302787435e-05 1.0096
19 17.0 0.0 -3.094402399796594e-05 9.50918891326348e-05 1.0119
20 18.0 0.0 -3.262265217112453e-05 9.452916251253802e-05 1.0144
21 19.0 0.0 -3.429548155432671e-05 9.39351901310517e-05 1.0171
22 20.0 0.0 -3.596222675728274e-05 9.330979716330852e-05 1.02
23 21.0 0.0 -3.762258802363148e-05 9.265279742352143e-05 1.0231
24 22.0 0.0 -3.927625033364511e-05 9.196399382219565e-05 1.0264
25 23.0 0.0 -4.092288250572579e-05 9.12431788542166e-05 1.0299
26 24.0 0.0 -4.256213629686185e-05 9.049013511895843e-05 1.0336
27 25.0 0.0 -4.419364550228159e-05 8.970463587361951e-05 1.0375
28 26.0 0.0 -4.581702505461894e-05 8.888644562105304e-05 1.0416
29 27.0 0.0 -4.743187012298561e-05 8.803532073342054e-05 1.0459
30 28.0 0.0 -4.903775521243089e-05 8.715101011305436e-05 1.0504
31 29.0 0.0 -5.063423326436127e-05 8.623325589197157e-05 1.0551
32 30.0 0.0 -5.222083475858915e-05 8.528179417153538e-05 1.06
33 31.0 0.0 -5.379706681778277e-05 8.429635580381285e-05 1.0651
34 32.0 0.0 -5.536241231519777e-05 8.327666721622591e-05 1.0704
35 33.0 0.0 -5.691632898668582e-05 8.222245128113941e-05 1.0759
36 34.0 0.0 -5.84582485480968e-05 8.113342823207298e-05 1.0816
37 35.0 0.0 -5.9987575819318076e-05 8.000931662826251e-05 1.0875
38 36.0 0.0 -6.150368785632916e-05 7.884983436933288e-05 1.0936
39 37.0 0.0 -6.300593309279031e-05 7.765469976187423e-05 1.0998999999999999
40 38.0 0.0 -6.449363049283228e-05 7.642363263974053e-05 1.1064
41 39.0 0.0 -6.596606871686862e-05 7.515635553991005e-05 1.1131
42 40.0 0.0 -6.74225053024151e-05 7.385259493576245e-05 1.1199999999999999
43 41.0 0.0 -6.886216586206999e-05 7.251208252963613e-05 1.1271
44 42.0 0.0 -7.028424330098631e-05 7.113455660653098e-05 1.1343999999999999
45 43.0 0.0 -7.168789705635229e-05 6.971976345081669e-05 1.1419
46 44.0 0.0 -7.307225236158857e-05 6.826745882779226e-05 1.1496
47 45.0 0.0 -7.443639953817088e-05 6.677740953192026e-05 1.1575
48 46.0 0.0 -7.57793933181956e-05 6.524939500352636e-05 1.1656
49 47.0 0.0 -7.71002522010209e-05 6.368320901571287e-05 1.1739
50 48.0 0.0 -7.839795784753942e-05 6.207866143318036e-05 1.1824
51 49.0 0.0 -7.967145451587092e-05 6.043558004458558e-05 1.1911
52 50.0 0.0 -8.091964854250044e-05 5.875381246998535e-05 1.2
53 51.0 0.0 -8.214140787313431e-05 5.703322814482264e-05 1.2090999999999998
54 52.0 0.0 -8.333556164779959e-05 5.5273720381803435e-05 1.2184
55 53.0 0.0 -8.45008998449718e-05 5.347520851188933e-05 1.2279
56 54.0 0.0 -8.563617298978239e-05 5.163764010548956e-05 1.2376
57 55.0 0.0 -8.674009193162976e-05 4.976099327477715e-05 1.2475
58 56.0 0.0 -8.781132769679547e-05 4.784527905787572e-05 1.2576
59 57.0 0.0 -8.884851142194952e-05 4.589054388546408e-05 1.2679
60 58.0 0.0 -8.985023437471637e-05 4.38968721301249e-05 1.2784
61 59.0 0.0 -9.081504806776283e-05 4.186438873852005e-05 1.2891
62 60.0 0.0 -9.174146447316287e-05 3.979326194620631e-05 1.3
63 61.0 0.0 -9.262795634408807e-05 3.7683706074611496e-05 1.3111000000000002
64 62.0 0.0 -9.347295765116815e-05 3.5535984409369075e-05 1.3224
65 63.0 0.0 -9.427486414115998e-05 3.335041215885984e-05 1.3339
66 64.0 0.0 -9.503203402585546e-05 3.112735949142892e-05 1.3456
67 65.0 0.0 -9.574278880944718e-05 2.8867254649335716e-05 1.3575000000000002
68 66.0 0.0 -9.640541426285387e-05 2.6570587137049264e-05 1.3696
69 67.0 0.0 -9.701816155378307e-05 2.42379109810241e-05 1.3819000000000001
70 68.0 0.0 -9.75792485415751e-05 2.1869848057577132e-05 1.3944
71 69.0 0.0 -9.808686124612794e-05 1.946709148493532e-05 1.4071
72 70.0 0.0 -9.85391555004425e-05 1.7030409074934477e-05 1.42
73 71.0 0.0 -9.893425879655386e-05 1.4560646839220595e-05 1.4331
74 72.0 0.0 -9.927027233481838e-05 1.2058732544135474e-05 1.4464
75 73.0 0.0 -9.954527328671002e-05 9.525679307756304e-06 1.4599000000000002
76 74.0 0.0 -9.975731728143635e-05 6.962589231804475e-06 1.4736
77 75.0 0.0 -9.990444112681327e-05 4.370657060339902e-06 1.4875
78 76.0 0.0 -9.998466577493252e-05 1.7511738563136905e-06 1.5016
79 77.0 0.0 -9.999599954321475e-05 -8.944693138367792e-07 1.5159
80 78.0 0.0 -9.993644160145834e-05 -3.5647776983010223e-06 1.5304000000000002
81 79.0 0.0 -9.980398573546578e-05 -6.258149192448348e-06 1.5451000000000001
82 80.0 0.0 -9.95966243977505e-05 -8.972870698578612e-06 1.56
83 81.0 0.0 -9.931235305569338e-05 -1.1707114526701726e-05 1.5751
84 82.0 0.0 -9.894917484732254e-05 -1.4458934851294935e-05 1.5904
85 83.0 0.0 -9.850510555462879e-05 -1.722626424014905e-05 1.6059
86 84.0 0.0 -9.797817890399535e-05 -2.000691027262028e-05 1.6216
87 85.0 0.0 -9.736645220290827e-05 -2.279855226585184e-05 1.6375000000000002
88 86.0 0.0 -9.666801232161705e-05 -2.5598738128815886e-05 1.6536
89 87.0 0.0 -9.58809820278266e-05 -2.8404881365349675e-05 1.6699000000000002
90 88.0 0.0 -9.500352668181514e-05 -3.121425824871764e-05 1.6864000000000001
91 89.0 0.0 -9.403386129858073e-05 -3.4024005191618475e-05 1.7031
92 90.0 0.0 -9.297025798271502e-05 -3.683111633696989e-05 1.7200000000000002
93 91.0 0.0 -9.181105374067828e-05 -3.963244139623856e-05 1.7371
94 92.0 0.0 -9.055465867399797e-05 -4.242468376353232e-05 1.7544
95 93.0 0.0 -8.919956455562706e-05 -4.5204398935131514e-05 1.7719
96 94.0 0.0 -8.774435379026785e-05 -4.796799326559646e-05 1.7896
97 95.0 0.0 -8.618770875788803e-05 -5.071172309304299e-05 1.8075
98 96.0 0.0 -8.452842153791733e-05 -5.3431694267617555e-05 1.8256000000000001
99 97.0 0.0 -8.276540400970972e-05 -5.6123862118616924e-05 1.8439
100 98.0 0.0 -8.08976983227803e-05 -5.878403189707582e-05 1.8624
101 99.0 0.0 -7.892448772806883e-05 -6.140785973197496e-05 1.8811
102 100.0 0.0 -7.684510775903972e-05 -6.399085413949068e-05 1.9
103 101.0 0.0 -7.46590577487918e-05 -6.652837812589881e-05 1.9191
104 102.0 0.0 -7.236601266651652e-05 -6.901565192584626e-05 1.9384000000000001
105 103.0 0.0 -6.996583525360449e-05 -7.144775641869711e-05 1.9579
106 104.0 0.0 -6.745858843645298e-05 -7.381963726652448e-05 1.9776000000000002
107 105.0 0.0 -6.484454798956871e-05 -7.612610981804154e-05 1.9975
108 106.0 0.0 -6.212421541888612e-05 -7.836186482331717e-05 2.0176000000000003
109 107.0 0.0 -5.929833103133426e-05 -8.052147500448747e-05 2.0379
110 108.0 0.0 -5.6367887152580006e-05 -8.259940252782707e-05 2.0584000000000002
111 109.0 0.0 -5.333414145055902e-05 -8.459000742245954e-05 2.0791000000000004
112 110.0 0.0 -5.019863031787675e-05 -8.648755699063967e-05 2.1
113 111.0 0.0 -4.696318226142989e-05 -8.828623625390153e-05 2.1211
114 112.0 0.0 -4.36299312426667e-05 -8.998015947841043e-05 2.1424
115 113.0 0.0 -4.0201329906786734e-05 -9.156338282154988e-05 2.1639
116 114.0 0.0 -3.6680162633885195e-05 -9.302991814008938e-05 2.1856
117 115.0 0.0 -3.3069558339591776e-05 -9.437374799818187e-05 2.2075
118 116.0 0.0 -2.9373002947155928e-05 -9.558884191089653e-05 2.2296
119 117.0 0.0 -2.559435144721021e-05 -9.666917385597483e-05 2.2519
120 118.0 0.0 -2.1737839455627395e-05 -9.760874108296534e-05 2.2744
121 119.0 0.0 -1.7808094174002536e-05 -9.84015842448172e-05 2.2971000000000004
122 120.0 0.0 -1.3810144651370471e-05 -9.904180887235564e-05 2.3200000000000003
123 121.0 0.0 -9.749431239851467e-06 -9.952360820679386e-05 2.3431
124 122.0 0.0 -5.631814131040329e-06 -9.984128739951931e-05 2.3664
125 123.0 0.0 -1.4635808541675665e-06 -9.998928908179772e-05 2.3899
126 124.0 0.0 2.7485473885872958e-06 -9.996222029973469e-05 2.4136
127 125.0 0.0 6.997410679702419e-06 -9.975488080179127e-05 2.4375
128 126.0 0.0 1.1275406772107576e-05 -9.936229265734316e-05 2.4616000000000002
129 127.0 0.0 1.5574489043303514e-05 -9.87797311751759e-05 2.4859
130 128.0 0.0 1.98861661624192e-05 -9.800275708040388e-05 2.5104
131 129.0 0.0 2.4201503618818632e-05 -9.702724989706916e-05 2.5351
132 130.0 0.0 2.851112726430912e-05 -9.584944247160945e-05 2.56
133 131.0 0.0 3.2805229023794614e-05 -9.446595655947376e-05 2.5851
134 132.0 0.0 3.707357493126433e-05 -9.287383938341263e-05 2.6104000000000003
135 133.0 0.0 4.1305515649210165e-05 -9.107060105738215e-05 2.6359000000000004
136 134.0 0.0 4.548999962981594e-05 -8.905425275459531e-05 2.6616
137 135.0 0.0 4.9615589075450825e-05 -8.682334548205341e-05 2.6875
138 136.0 0.0 5.3670478854007505e-05 -8.43770093069287e-05 2.7136
139 137.0 0.0 5.764251852133727e-05 -8.171499286249302e-05 2.7399
140 138.0 0.0 6.151923759832183e-05 -7.883770294295253e-05 2.7664
141 139.0 0.0 6.528787424386095e-05 -7.574624397762439e-05 2.7931
142 140.0 0.0 6.893540745711695e-05 -7.24424571554642e-05 2.8200000000000003
143 141.0 0.0 7.244859293261136e-05 -6.8928958951117e-05 2.8471
144 142.0 0.0 7.58140026800881e-05 -6.52091787835393e-05 2.8744
145 143.0 0.0 7.901806850731089e-05 -6.128739551795232e-05 2.9019000000000004
146 144.0 0.0 8.204712944802844e-05 -5.716877250158922e-05 2.9295999999999998
147 145.0 0.0 8.488748319909571e-05 -5.2859390803557724e-05 2.9575
148 146.0 0.0 8.752544161007193e-05 -4.836628030934244e-05 2.9856000000000003
149 147.0 0.0 8.994739024541854e-05 -4.369744830122675e-05 3.0139000000000005
150 148.0 0.0 9.213985201359944e-05 -3.886190513744783e-05 3.0423999999999998
151 149.0 0.0 9.408955482885929e-05 -3.386968662546322e-05 3.0711
152 150.0 0.0 9.578350324015606e-05 -2.873187266857161e-05 3.1
153 151.0 0.0 9.720905392760702e-05 -2.3460601750585723e-05 3.1291
154 152.0 0.0 9.835399492984207e-05 -1.8069080810616793e-05 3.1584
155 153.0 0.0 9.920662842584688e-05 -1.2571590049629674e-05 3.1879
156 154.0 0.0 9.975585685224551e-05 -6.9834822026192765e-06 3.2176
157 155.0 0.0 9.999127209157403e-05 -1.3211758054129605e-06 3.2475000000000005
158 156.0 0.0 9.990324741902882e-05 4.397858016388089e-06 3.2776
159 157.0 0.0 9.948303184456377e-05 1.0155115706554476e-05 3.3079
160 158.0 0.0 9.87228464342335e-05 1.5931088849251602e-05 3.3384
161 159.0 0.0 9.761598213955617e-05 2.170529960470135e-05 3.3691000000000004
162 160.0 0.0 9.615689860666532e-05 2.7456344446183743e-05 3.4
163 161.0 0.0 9.434132337846043e-05 3.31619466135623e-05 3.4311000000000003
164 162.0 0.0 9.216635084322934e-05 3.879901767110548e-05 3.4624
165 163.0 0.0 8.963054022274018e-05 4.434372852365659e-05 3.4939
166 164.0 0.0 8.67340118320907e-05 4.9771590204761896e-05 3.5256
167 165.0 0.0 8.347854078322675e-05 5.5057544702821115e-05 3.5575
168 166.0 0.0 7.986764724463343e-05 6.017606603631379e-05 3.5896000000000003
169 167.0 0.0 7.590668231196969e-05 6.510127172636267e-05 3.6219
170 168.0 0.0 7.160290848913815e-05 6.980704474403788e-05 3.6544
171 169.0 0.0 6.696557372729566e-05 7.426716593067309e-05 3.6871
172 170.0 0.0 6.200597792154295e-05 7.845545680188934e-05 3.72
173 171.0 0.0 5.673753072246099e-05 8.234593254993105e-05 3.7531
174 172.0 0.0 5.117579948333757e-05 8.591296495431425e-05 3.7864
175 173.0 0.0 4.533854613496372e-05 8.913145479777476e-05 3.8199
176 174.0 0.0 3.924575175943646e-05 9.197701326329688e-05 3.8536
177 175.0 0.0 3.2919627623696125e-05 9.442615165894024e-05 3.8875
178 176.0 0.0 2.6384611433804805e-05 9.645647868073527e-05 3.9216000000000006
179 177.0 0.0 1.9667347583515437e-05 9.804690428070224e-05 3.9559
180 178.0 0.0 1.2796650196782336e-05 9.917784905784754e-05 3.9904
181 179.0 0.0 5.803447804817374e-06 9.98314579357467e-05 4.0251
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+19
View File
@@ -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.