33from icm20948 import ICM20948
44import time
55import math
6- import argparse
7- import os
8- import signal
9- import sys
10-
11- BAR_CHAR = u'\u2588 ' # Unicode FULL BLOCK
12-
13- running = True
14- mingraphval = 0
15- maxgraphval = 360
16-
17- # Terminal bar-graph adapted from examples/graph.py file from
18- # https://github.com/pimoroni/vl53l1x-python
19- def graphValue (value ):
20- global cols , mingraphval , maxgraphval
21- if ( value > maxgraphval ):
22- value = maxgraphval
23- elif ( value < mingraphval ):
24- value = mingraphval
25-
26- graphvalue = value - mingraphval
27-
28- bar_size = int ((graphvalue / float (maxgraphval - mingraphval )) * (cols - 10 )) # Scale bar_size to our terminal width
29- bar = BAR_CHAR * bar_size # Create a bar out of `bar_size` unicode FULL BLOCK characters
30- bar = bar .ljust (cols - 7 , u' ' ) # Pad the bar to the full with of the terminal, minus the value prefix
31- sys .stdout .write ("\r " ) # Return the cursor to the beginning of the current line
32- sys .stdout .write (u"{:05.1f} {}" .format (value , bar )) # Output our measurement and bar
33- sys .stdout .flush () # Flush the output buffer, since we're overdrawing the last line
34-
35-
36-
37- def exit_handler (signal , frame ):
38- global running ,args
39- running = False
40- if ( args .graph == True ):
41- # Clean up terminal after using --graph output
42- sys .stdout .write ("\n " )
43- sys .exit (0 )
44-
45- signal .signal (signal .SIGINT , exit_handler )
46-
47-
48- parser = argparse .ArgumentParser ()
49- parser .add_argument ( '--axis' , choices = ['xy' ,'yz' ,'xz' ], default = 'yz' , help = "Axis to measure (default: yz)" )
50- parser .add_argument ( '--graph' , '-g' , action = "store_true" , default = False , help = "Display heading as terminal-graph" )
51- args = parser .parse_args ()
52-
53-
54- if ( args .graph == True ):
55- try :
56- rows , cols = [int (c ) for c in os .popen ("stty size" , "r" ).read ().split ()]
57- except ValueError :
58- print ("Cannot get size of tty! Try running in Terminal." )
59- sys .exit (1 )
60-
616
627print ("""magnetometer.py - Convert raw values to heading
638
64- Rotate the sensor through 360 degrees to calibrate.
9+ Rotate the sensor (X-axis upwards) through 360 degrees to calibrate.
6510
6611Press Ctrl+C to exit!
6712
@@ -71,52 +16,61 @@ def exit_handler(signal, frame):
7116Y = 1
7217Z = 2
7318
74- if ( args .axis == 'xy' ):
75- AXES = X , Y
76- elif ( args .axis == 'yz' ):
77- AXES = Y , Z
78- elif ( args .axis == 'xz' ):
79- AXES = X , Z
80-
81-
82- if ( args .graph == True ):
83- sys .stdout .write ("\n " )
84-
19+ # The two axes which relate to heading, depends on orientation of the sensor
20+ # Think Left & Right, Forwards and Back, ignoring Up and Down
21+ AXES = Y , Z
8522
23+ # Initialise the imu
8624imu = ICM20948 ()
8725
26+ # Store an initial two readings from the Magnetometer
8827amin = list (imu .read_magnetometer_data ())
8928amax = list (imu .read_magnetometer_data ())
9029
91- while running :
30+ while True :
31+ # Read the current, uncalibrated, X, Y & Z magnetic values from the magnetometer and save as a list
9232 mag = list (imu .read_magnetometer_data ())
93- for i in AXES :
33+
34+ # Step through each uncalibrated X, Y & Z magnetic value and calibrate them the best we can
35+ for i in range (3 ):
9436 v = mag [i ]
37+ # If our current reading (mag) is less than our stored minimum reading (amin), then save a new minimum reading
38+ # ie save a new lowest possible value for our calibration of this axis
9539 if v < amin [i ]:
9640 amin [i ] = v
41+ # If our current reading (mag) is greater than our stored maximum reading (amax), then save a new maximum reading
42+ # ie save a new highest possible value for our calibration of this axis
9743 if v > amax [i ]:
9844 amax [i ] = v
45+
46+ # Calibrate value by removing any offset when compared to the lowest reading seen for this axes
9947 mag [i ] -= amin [i ]
48+
49+ # Scale value based on the higest range of values seen for this axes
50+ # Creates a calibrated value between 0 and 1 representing magnetic value
10051 try :
10152 mag [i ] /= amax [i ] - amin [i ]
10253 except ZeroDivisionError :
10354 pass
55+ # Shift magnetic values to between -0.5 and 0.5 to enable the trig to work
10456 mag [i ] -= 0.5
10557
58+ # Convert from Gauss values in the appropriate 2 axis to a heading in Radians using trig
59+ # Note this does not compensate for tilt
10660 heading = math .atan2 (
10761 mag [AXES [0 ]],
10862 mag [AXES [1 ]])
10963
64+ # If heading is negative, convert to positive, 2 x pi is a full circle in Radians
11065 if heading < 0 :
11166 heading += 2 * math .pi
67+
68+ # Convert heading from Radians to Degrees
11269 heading = math .degrees (heading )
70+ # Round heading to nearest full degree
71+ heading = round (heading )
11372
114- if ( args .graph == True ):
115- # Display the heading as a bar-graph in the terminal
116- graphValue (heading )
117- else :
118- # Round the heading value and print out directly
119- heading = round (heading )
120- print ("Heading: {}" .format (heading ))
73+ # Note: Headings will not be correct until a full 360 deg calibration turn has been completed to generate amin and amax data
74+ print ("Heading: {}" .format (heading ))
12175
12276 time .sleep (0.1 )
0 commit comments