Python GPS2system. 2/25/2017

Overview

The GPS system was designed to play audio clips along the route of the New Hope Valley Railroad. The initial impedes was to play sound effects during the Halloween train rides. At a specified latitude and longitude the device would play a specific sound effect.

After the system is powered up, it may take the GPS receiver a couple of minutes to lock on to enough satellites to retrieve valid data. The system then runs autonomously. On start up, the LED indicator will come on after the system loads and the python application has been launched. This takes less than a minute. The indicator will begin to flash when the GPS receiver is locked and is sending valid data to the system. This could take several minutes, depending on the atmospheric conditions.

In normal operation the LED will flash approximately once per second. The LED will remain on when an audio track is being played and when the system is capturing and storing positional data.

Details All of the files are located in the /home/pi/gps2 folder.

To begin the process we have to know the latitude and longitude of the locations that will play the audio clip.

In the first iteration of this system, a Parallax BS2 stamp chip was used. This system was used for a couple of years. The limitation of this original system, which was chosen because it used a BASIC programming language, was the finite memory of the Parallax chip. Since the route of the railway was NE a compromise was made to use only the latitude data. This was carried over to the Raspberry Pi system. Now I have added the ongitudinal data of the locations.

To overcome the memory limitations of the original Parallax system, the Raspberry Pi was selected. The Raspberry Pi uses Python as its programming language. Version 2.7 was used for this project.

Next you need to know the file names of the audio clips that are to be played.

The file in which this information is assembled is named: gps_data2.csv. This is a spreadsheet file, saved in the .csv format (Comma Separated Values). The Python library has a module to read .csv files. This data in this .csv file is read into the system by the program load_csv_data2.py.

In earlier versions this data was assembled in three text files and each was imported into the python program. The gps2_data.csv contains six fields: Latitude Longitude Location Audio track Volume level

Example of a gps2_data_csv file

When the csv file is loaded into the system it adds another field the location number. In this case it would be 0 to 21. This can be used to play the audio clips in a yet to be designed audio player utility program. Note: There cannot be an empty row at the top of this spreadsheet. If there is, the load_csv_data2.py program will crash.

The the man program that operates when the train is running is named gps2.py. The program that loads the data from the spreadsheet is, as stated above, load_csv_data2.py. This program is run by the import command in the main program.

The audio files are stored in the folder /home/pi/gps2/audio_tracks. The names each audio track is in the form name_name_length.mp3. The length is four digits and is the duration of the clip seconds. For example: There is an audio clip of a “demented man walking” that is 16 seconds in duration. The file name is: demented_man_walking_0016.mp3.

This format was selected so that the audio clips can be easily sorted by length.

When the system is running, and an audio clip is played, the program records the location number, the direction of travel ( n or s), date and time, audio file name, location and the file name of the gps program file. This information is stored in the logfile.txt file. It can easily be imported into a spreadsheet.

There is a utility file which will make it easy to preview the audio clips, off line, that will be played along the route. The file is play_mp3s.py.

There is another utility that will play one audio file at a time. The file is play_file.py. To use this utility, enter python play_file.py x . where x is the location number. You MUST enter a number or the program will crash, ie python play_file.py 4. This will play the file 4. You can then play another file by entering the number or z to exit this program.

There is a also mode built into the system to record the latitude and longitude of the position along the route. This data can be entered into the gps2_data.csv file. A keyboard in required for this operation. I monitor is not necessary, but may also be used. In order to log the position of a location that you wish to have an audio file play, press the ctrl- keys. The LED will stay on. Type in the name of this location and press enter. The LED will begin to flash. The data is saved in the file datafile.txt. This file can be opened by a spreadsheet program and used in the gps2_data.csv file.

I summary:

The following files need to be in the operational directory:

gps2.py gps2_data.csv load_csv_data2.py play_mp3s.py play_file.py gpssock.sh (see below)

The following files will be created: logfile.txt datafile.txt

As stated above, the audio clips used are stored in a separate folder. In the /home/pi/gps2 directory, create a folder audio_tracks Place the audio files, in mp3 format, into this folder, /home/pi/gps2/audio_tracks.

In order to have the system run automatically when power is applied the file: .desktop must be in the folder /home/pi/.config/autostart.

[Desktop Entry] Type = Application Exec = lxterminal -e “sudo python /home/pi/gps2/gps2.py”

The .config folder exists in the /home/pi folder. Create the autostart folder In the .config folder. Then create the .desktop file in the autostart folder.

This will run the program in a lxterminal. You could automatically run the program by inserting a line in the /etc/.rclocal file. But, the program runs in the background and does not show any information in the monitor. You can also run the program by inserting a line in the /etc/profile file. This works fine with a Raspberry Pi 2, but with the 2B and the 3 it plays the audio files twice with an echo effect. This file is needed when running the system using a Raspberry Pi 2B or Pi 3B: gpssock.sh The line /home/pi/gps2/./gpssock.sh & must be added to the bottom of the file: /etc/profile

The gpssock.sh is a linux script that is executable. To run an executable file it must be preceded by a ./ Adding this line to the /etc/profile file, it runs the gpssock.sh file during startup. The & is important. If it is not there the Raspberry Pi will not boot into the GUI. If this happens, press the ctrl-alt-F4, log in and edit the /etc/profile file.

The file gpssock.sh contains the following three lines: sudo killall gpsd sudo gpsl /dev/ttyUSB0 -F /var/run/gpsd.sock sudo service ntp restart

The software needed to read the GPS receiver can be installed from this link: http://www.danmandle.com/blog/getting-gpsd-to-work-with-python/

There is another procedure that I used to install the GPS receiver from this link: http://www.instantsupportsite.com/self-help/raspberry-pi/raspberry-globalsat-353s4-install/

This worked when the initial installation was performed on both the 2 and the 2B systems. However I tried it recently and found that the /lib/udev/gpsd.hotplug file does not exist and I was unable to read the data from the receiver.

The SD chip used on the Rapsberry Pi 2B works on the 3B

If a Raspberry Pi 2B or 3B are used the time and date are correct If a Raspberry Pi 2 is used the time will be correct, but the date is off by 7168 days!

Once this is installed, the system is functional and can be used.

______

Notes:

The system can play a different audio track at a location depending in the direction of the train. There are times that you may not want to play a track in one direction. In the folder audio_clips there is a file named silent.mp3 for this purpose.

The system to date only monitors the latitude data. When the train proceeds past Bonsal Crossing Rd., the train’ heading is approximately north-west. In warm weather, the announcer usually sits in north end of car 101. In cold weather he sits in the caboose. He offset between these two positions is .7 times the distance between these two points. Or 150 ft. Times .7 or 105 ft. This translates into .00027degrees. The GPS receiver chosen is the Globecast BU353.

. It is a sealed weather proof unit with a USB interface and costs about $35.

The Parallax system used an RS-232 receiver that was not weather proof.

Installing the BU353 GPS receiver in the Raspberry Pi http://www.instantsupportsite.com/self-help/raspberry-pi/raspberry-globalsat-353s4-install/

Note: This installation worked fine when it was originally used in August 2016. It is not working at this time. There are also problems with it on the RaspPi-2B. The RaspPi-2 works fine. See anomalies section for details and solution

The program uses python code developed by Dan Mandle. Here is the link : http://www.danmandle.com/blog/getting-gpsd-to-work-with-python/

The first few lines of for gps-344.py code are shown below: import argv was used to display the file name of the program automatically The remainder of the imports were from Dan Mendle

System Setup

Hardware: Raspberry Pi 3 with LED connected to Globcast BU353 GPS receiver Power supply USB keyboard

Software: Pre configure 16 gig SD chip.

The Source code for gps2-8-402.py

### Gps System for the New Hope Valley Railway ### Created by Ted Dunn 7/28/2016 #### GPS-102 Added mp3[] #### 7/31/16 Adeded reset lats at new_hill_south_switch #### 8/2/16 Changed delta to .0002 #### added speed #! /usr/bin/python # Written by Dan Mandle http://dan.mandle.me September 2012 # License: GPL 2.0 #### version 300s use spread sheets and .csv files

############ This version adds gain control for each audio track ###### gps-339 force audio to local ### WAS: xxx = 'omxplayer --vol '+ gain[x] +' %s' ### IS: xxx = 'omxplayer -o local --vol '+ gain[x] +' %s' ##### Gps-341 Added flashing pin 11 if receiver NOT locked ##### gps-342 Added lock and unlock to logfile,txt #### gps-343 Added keyboard detection for data capture #### gps-344 Clean up of 343 #### gps-401 Changes datafile Capture to ctrl-c by Bob Malkin #### gps2-402 Adds Longitude data ### gps2-1-402 fixes Lognitude reset ### gps2-1-402 changes autostart file to gps2.py ### gps2-2-402 changed delts for longitude by 57/69 ### gps2-3-402 Changes folder for lagfile and datafile to /gps2 ### gps2-5 Changed script to version in text files ###gps2-6-402 added commas to logfile.txt ### gps2-7 fixed file.write for GPS locked ### gps2-8 fixed 12:00 AM to PM version = '[gps2-8-402.py]' nhv = '--- New Hope Valley Railway ---' from sys import argv #used to get file name from load_usb_ports import usbnum ## Find active number of USB ports for capture data import os from gps import * from time import * import time import threading ###################### reading latitude and longitude data from RPi import GPIO pin = 7 GPIO.setmode(GPIO.BOARD) GPIO.setup(7, GPIO.IN, pull_up_down = GPIO.PUD_UP) GPIO.setup(11, GPIO.OUT) GPIO.output(11,0)

##################################### gps_lock = 0 lattold =0 ## Used to reset latitude and to deterime direction of travel longold = 0 ##Used to reset longitude script = argv # This is the file name from load_csv_data2 import* def EDST(utc): # Strips time from gpsd.utc timee=utc[11:19] hr=utc[11:13] dayy=int(gpsd.utc[8:10]) hrnum=int(hr) -4 #hour - zolu offset hrstr=str(hrnum) if hrnum<0: # Previous day

hrnum=hrnum+24 dayy=dayy-1 hrstr =str(hrnum)

if hrnum > 12: hrnum =hrnum-12 hrstr= str(hrnum) daynite ='PM' else: daynite = 'AM' if hrnum==12: daynite = 'PM' if hrnum<10: #adds 0 hrstr ='0'+hrstr ttime =hrstr + utc[13:19] + ' ' +daynite +' '+gpsd.utc[5:7]+'/' +str(dayy)+'/'+gpsd.utc[2:4]

return (ttime) delta =.000166 # The spread between the latituude and play point direction = "n" last = 0 #used to store the last location to reset latitude lastlong =0 ## used to store last location to reset longitude gpsd = None #seting the global variable os.system('clear') #clear the terminal (optional) class GpsPoller(threading.Thread): def __init__(self): threading.Thread.__init__(self) global gpsd #bring it in scope gpsd = gps(mode=WATCH_ENABLE) #starting the stream of info self.current_value = None self.running = True #setting the thread running to true

def run(self): global gpsd while gpsp.running: gpsd.next() #this will continue to loop and grab EACH set of gpsd info to clear the buffer if __name__ == '__main__': gpsp = GpsPoller() # create the thread gpsp.start() # start it up

while True: try:

while True: #It may take a second or two to get good data #print gpsd.fix.latitude,', ',gpsd.fix.longitude,' Time: ',gpsd.utc

os.system('clear')

latt = gpsd.fix.latitude long = gpsd.fix.longitude

#### Reset the lattitude ### 1 degree of latitue is 57 miles. ### 1 degree of logitude at 37 degrees latitute is 69 miles

if abs(lats[last]-10 -latt) > delta*3 and lats[last] >45: lats[last] =lats[last]-10 if abs(lons[last]+10 -long) > delta*3*57/69 and lons[last] <-84: lons[last] =lons[last]+10 print nhv #print gpsd.utc #print gpsd.fix.time print ' GPS system for the New Hope Valley Railway' print 'Created by Ted Dunn with software written by Dan Mandle' print 'With help from Bob Malkin of NC State and' print 'NC Museum of Natural Sciences.' print '------' print 'latitude ' , gpsd.fix.latitude print 'longitude ' , gpsd.fix.longitude print 'time utc ' , gpsd.utc,' + ', gpsd.fix.time print 'altitude (m)' , gpsd.fix.altitude #print 'eps ' , gpsd.fix.eps #print 'epx ' , gpsd.fix.epx #print 'epv ' , gpsd.fix.epv #print 'ept ' , gpsd.fix.ept #print 'speed (m/s) ' , gpsd.fix.speed #print 'climb ' , gpsd.fix.climb #print 'track ' , gpsd.fix.track print 'mode ' , gpsd.fix.mode print 'Altitude : ' ,'%.0f'%(gpsd.fix.altitude/.305), 'Feet' #print 'sats ',gpsd.satellites print print 'Delta ', delta,' ', '%.1f'%(delta *57*5280), ' ft.' ###print 'Delta ', delta,' ', '%.1f'%(delta *float(364025.16 )), ' ft.' ax = 0 #print latt,' ', long #print lats[ax],' ',lons[ax] #print locs[ax],' ',lats[ax], '%.0f'%((latt-lats[ax])*float(364025.16)), ' ft. ',lons[ax], '%.0f'%((long-lons[ax])*float(364025.16)), ' ft. ' print 'Current latitude ',latt print 'Previous latitude ',lattold, ' ','%.0f'%((latt-lattold)*float(364025.16)), ' ft.' print 'Current longitude ',long print 'Previous longitude ',longold,' ','%.0f'%((long-longold)*float(364025.16)), ' ft.'

print 'Speed : '+ str (gpsd.fix.speed *2.236)[0:5] + ' mph' if latt-lattold > 0: print "Traveling to New Hill" direction = "n" else: print "Traveling to Bonsal" direction = "s" print gpsd.utc

if len(gpsd.utc)> 6: # To make sure the GPS receiver data is good print 'EDST = ', EDST(gpsd.utc) ######################################### ######Log Data Points ie capture Latitudes and Longitues to datafile.txt if GPIO.input(7): print 'Active USB Ports: ', usbnum print if usbnum<5: print ' Keyboard not connected' else: print 'Keyboard connected' print " Waiting to capture GPS data points." print else: print " ",gpsd.fix.latitude print " ",gpsd.fix.longitude print'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%' file = open('/home/pi/gps2/datafile.txt', 'a') file.write(' '+str(gpsd.fix.latitude)+',') file.write(' '+str(gpsd.fix.longitude)+',') GPIO.output(11,1) print usbnum ### The number of active USB ports if usbnum >4: # The keyboaard in connected datapoint = raw_input('Enter location: ') else: print 'No Keyboard connected. Saving Data' datapoint = 'No keyboard' time.sleep(2) GPIO.output(11,0) file.write(datapoint+',') file.write(EDST(gpsd.utc)+'\n') file.close()

#time.sleep(1)

###################################################

file = open('/home/pi/gps2/logfile.txt','a') for x in range(0,len(locs)): #print str(x), latt , locs[x], str(lats[x]), str(lats[x] -latt) # if (abs(lats[x] -latt) < delta) and (abs(lons[x] -long) < delta*57/69): folder = '/home/pi/gps2/audio_tracks/' files = mp3s[x]

sound = folder +files # print x, files, gain[x]

xxx = 'omxplayer -o local --vol '+ gain[x] +' %s' os.system(xxx %sound)

lons[x] = lons[x] -10 lats[x] =lats[x] + 10 # takes the last played file out of list file.write('\n' + str(x)+', ') # Tne number of the location list

file.write (str(gpsd.fix.speed *2.236)[0:5]+' mph' + ', ' + direction +', ') file.write(EDST(gpsd.utc)+', ') file.write(' '+files+',') #the audio file name file.write(' '+locs[x]+',') #the location name file.write(' '+version+',') #lats[x] =lats[x] + 10 # takes the last played file out of list last = x #### The last location's lats to reset it # Do we need lastlong = x ??? x = x+1 time.sleep(1) file.close() lattold = latt longold = long

print script, ' ',version

######################### log GPS lock and unlock if gps_lock == 0: print 'GPS Receiver NOT LOCKED'

if latt >35 and gps_lock == 0: gps_lock = 1 print 'GPS Locked' file = open('/home/pi/gps2/logfile.txt','a') file.write('\n') file.write(EDST(gpsd.utc) +' '+'GPS Receiver Locked ') file.write(' '+version +'\n') file.close() GPIO.output(11,1)

if gpsd.fix.mode < 2 and gps_lock ==1: gps_lock = 0 file = open('logfile.txt','a') file.write('GPS NOT LOCKED \n') #file.write('GPS Receiver NOT LOCKED \n') file.write(' '+version) file.close()

if latt >35: GPIO.output(11,0) #####################################

time.sleep(1.0) #set to whatever GPIO.output(11,1)

except (KeyboardInterrupt, SystemExit): #when you press ctrl+c print "\nTEDDDDD"

print " ",gpsd.fix.latitude print " ",gpsd.fix.longitude print'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%' file = open('/home/pi/gps2/datafile.txt', 'a') file.write(' '+str(gpsd.fix.latitude)+',') file.write(' '+str(gpsd.fix.longitude)+',') GPIO.output(11,1) print usbnum ### The number of active USB ports if usbnum >4: # The keyboaard in connected datapoint = raw_input('Enter location: ') else: print 'No Keyboard connected. Saving Data' datapoint = 'No keyboard' time.sleep(2) GPIO.output(11,0) file.write(datapoint+',') file.write(EDST(gpsd.utc)+'\n') file.close()

#time.sleep(1) #gpsp.running = False #gpsp.join() # wait for the thread to finish what it's doing

#print "Done.\nExiting."