Skip navigation.

Next Round of Enhancements

These are just a few extra bug fixes and enhancements that I have implemented ready for testing on the next bush walk. 

1) I am just waiting on version 0.7 of the Picamera software to be released, as that should fix the 'fade-to-back' issue I experienced last time

2) I also added a simple routine to log the GPS the photo's GPS details and the RPi CPU temperature to a logfile for later analysis.  It also makes it easier to upload the photo locations into my QGIS software.

3) As I mentioned previously,  if there is no GPS unit present, ie: the SD card is booted in another RPi, or booting up inside a house, then the local console will not be available due to by GPS-to-OSTime Python script trying to sync the satellite time.  I added an arbitrary four minute time out, so I could regain control of the console. The script also logs the startup time to a logfile, so i can keep track of when I have used it.  It also beeps at me to let me know that it has reached GPS time sync.  I run my Ultimate GPS unit with the coin battery, so if I turn on PiPole-Cam outside with good sky coverage, the GPS unit will have sync'ed by the time the RPi has finished booting up. Without the battery, it can take many minutes to synchronise, possibly more than the 4 minutes I allocated.

Both sets of code below could be written in a more efficient and more Pythonesque manor.  The code still contains remnants of debug code.  It works for me, but it could wipe or destroy your raspberry Pi, so please review it carefully before you use it!

I have used this webpage (which is based on someone else's code) for some handy code to read the gpsd daemon.  I just cut and pasted the GPSController.py code into a file with the same name and place the file in my code directory.

Time Sync Code:

Here is a snapshot of the TimeSync code I am using today.  Use the code as you like.  Setting the RPi OS time came from a Python stackexchange webpage (what a great site!). 

#!/usr/bin/env python
#
# this Python script will read gpsd (running via an Adafruit UltimateGPS) for the current Satellite datetime string and change the RPi system time to the GPS time.  This script needs to run via sudo, so it can set the new time.
#
#  Parview: 2013-10-24
#
#

import sys
import datetime
from GPSController import *
import time
import wiringpi2
from time import sleep

def sound_buzzer(length):
  wiringpi2.digitalWrite(buzzer,1)  #turn on buzzer
  sleep(length)
  wiringpi2.digitalWrite(buzzer,0)  #turn off buzzer

def _linux_set_time(time_tuple):
    import ctypes
    import ctypes.util
    import time

    # /usr/include/linux/time.h:
    #
    # define CLOCK_REALTIME                     0
    CLOCK_REALTIME = 0

    # /usr/include/time.h
    #
    # struct timespec
    #  {
    #    __time_t tv_sec;            /* Seconds.  */
    #    long int tv_nsec;           /* Nanoseconds.  */
    #  };
    class timespec(ctypes.Structure):
        _fields_ = [("tv_sec", ctypes.c_long),
                    ("tv_nsec", ctypes.c_long)]

    librt = ctypes.CDLL(ctypes.util.find_library("rt"))

    ts = timespec()
    ts.tv_sec = int( time.mktime( datetime.datetime( *time_tuple[:6]).timetuple() ) )
    ts.tv_nsec = time_tuple[6] * 1000000 # Millisecond to nanosecond

    # http://linux.die.net/man/3/clock_settime
    librt.clock_settime(CLOCK_REALTIME, ctypes.byref(ts))

# Local Variables
beep_short=0.3
beep_long=1.0
buzzer=10  # set the buzzer GPIO pin number
GPSTIME=''
LOGFILE='/var/log/startup_time.log'
#loop_counter = 10   # debug wait time
loop_counter = 240   # wait 4 minutes to see if we get a GPS time sync, otherwise, end

#setup buzzer gpio port
wiringpi2.wiringPiSetupGpio()
wiringpi2.pinMode(10,1)

#create controller
gpsc = GpsController()

#start controller
gpsc.start()  # need to find a proper test to see if it's actually running ok.  Maybe the GPS unit isn't present?
time.sleep(3)  # wait 3 seconds for the gpsd connection to get it's act together

#read latitude and longitude
GPSTIME = gpsc.fix.time
while gpsc.fix.latitude == 0.0 or gpsc.fix.longitude == 0.0:
   # Loop till we get a correct reading from gpsd
   time.sleep(1)
   sound_buzzer(beep_short)
   loop_counter=loop_counter - 1
   if loop_counter <= 0:
      # Whoops, looks like it can't see the GPS controller, or has GPS issues.  Force the program closed!
      #stop controller
      gpsc.stopController()
      #gpsc.join()
      today = datetime.datetime.today()
      f=open(LOGFILE, 'a')
      DATA1 = today.strftime(' WST:  %Y-%m-%d %H:%M:%S <-- GPS Error')
      DATA=(DATA1)+'\n'
      f.write(DATA)
      f.close()
      import os
      os._exit(-1)   # hard exit the program, as quit() doesn't seem to work!

# keep reading the GPS time, until we get a good time string.  For some reason it sometimes spits out a float
while  not isinstance(GPSTIME, (str, unicode)):
   GPSTIME = gpsc.fix.time
   time.sleep(1)

# example: 2013-10-23T14:15:54.000Z
sutc = datetime.datetime.strptime(GPSTIME, "%Y-%m-%dT%H:%M:%S.000Z")
s = sutc+datetime.timedelta(hours=8)
f=open(LOGFILE, 'a')
DATA1 =s.strftime(' WST:  %Y-%m-%d %H:%M:%S') 
DATA=(DATA1)+'\n'
f.write(DATA)
f.close()
tt = datetime.datetime.timetuple(s)
time_tuple = (tt[0], tt[1], tt[2], tt[3],tt[4],tt[5],0)

if sys.platform=='linux2':
    _linux_set_time(time_tuple)

#stop controller
gpsc.stopController()
#gpsc.join()  # wait for the thread to finish

quit()

 

GPS Photo Code:

#!/usr/bin/env python
#
#  Note:  It can take a few seconds to connect properly.
#
#  This relies on having the python gps module installed and a custom module: GPSController.py
#  installed or located in the same directory, see: http://www.stuffaboutcode.com/2013/09/raspberry-pi-gps-setup-and-python....
#  Also need Picamera installed:  http://picamera.readthedocs.org/
#  the RPi based gpsd module needs to be installed
#
#  Parkview - 2013-11-07
#   Run via:   sudo gps_photo.py  <interval between photos>  <number of photos to take>
#    or the above line can be called from a simple shell script.
#
# ToDo: 
#        * have a proper routine run that checks to see if gpsd is running correctly, ie: is a GPS present, or do we have a valid GPS signal
#        * add a power down or multi function button

# Import the needed Python Modules
from GPSController import *
import time
import datetime
import picamera
import math
import wiringpi2
from time import sleep
from subprocess import PIPE, Popen

# definitions
ALT2='A'
ALT3='1'
beep_short=0.6
beep_long=1.5
buzzer=10  # set the buzzer GPIO pin number
LOGFILE='/var/log/photo-data_'
FILENAME='/home/pi/images/image_'
interval_time = 5
num_photos = 5
photo_width = 1920    # change to suit.  These could be added via a CLI switch?
photo_height = 1080   # change to suit
interval_time = int(sys.argv[1])
num_photos = int(sys.argv[2])

# functions

def get_cpu_temperature():
    process = Popen(['vcgencmd', 'measure_temp'], stdout=PIPE)
    output, _error = process.communicate()
    return float(output[output.index('=') + 1:output.rindex("'")])

def exif_format(num):
  ALT1=math.modf(num)
  ALT3 = int(math.fabs(ALT1[0])*10)
  ALT2=str(int(ALT1[1]))+str(ALT3)+"/10"

  return ALT2

def exif_long_ref(num):
  if num < 0:
    GPSLONGREF = 'W'
  else:
    GPSLONGREF = 'E'
  return GPSLONGREF

def exif_lat_ref(num):
  if num < 0:
    GPSLATREF = 'S'
  else:
    GPSLATREF = 'N'
  return GPSLATREF

def exif_latlong_format(num):
  # convert GPS readings into an exif usable format

  GPSLAT = abs(num)
  LAT1=math.modf(GPSLAT)
  DEG = str(int(LAT1[1]))+"/1"
  MIN1=LAT1[0]*60
  MIN=str(int(MIN1))+"/1"
  MIN2=math.modf(MIN1)
  SEC = str(int(MIN2[0]*6000))+"/100"
  LATLONG = DEG+","+MIN+","+SEC
  return LATLONG

def sound_buzzer(length):
  wiringpi2.digitalWrite(buzzer,1)  #turn on buzzer
  sleep(length)
  wiringpi2.digitalWrite(buzzer,0)  #turn off buzzer

def setup_exif():
  # get the info form the GPS unit
  GPSLAT = gpsc.fix.latitude
  GPSLONG = gpsc.fix.longitude
  GPSALT = gpsc.fix.altitude + .01  # fudge factor to get the int working correctly
  GPSDOP = gpsc.fix.epx
  GPSMODE = gpsc.fix.mode
  # prep the data ready for PiCamera use
  ALT = exif_format(GPSALT)
  DOP = exif_format(GPSDOP)
  LAT = exif_latlong_format(GPSLAT)
  LONG = exif_latlong_format(GPSLONG)
  # Setup variables for PiCamera
  today = datetime.datetime.today()
  OSDATETIME = today.strftime('%Y-%m-%d %H:%M:%S')
  camera.exif_tags['IFD0.Copyright'] = 'Copyright (c) 2013 Paul Hamilton'
  camera.exif_tags['IFD0.ImageDescription'] = 'PiPole-Cam Photo'
  camera.exif_tags['GPS.GPSDOP'] = DOP
  camera.exif_tags['GPS.GPSAltitude'] = ALT
  camera.exif_tags['GPS.GPSMeasureMode'] = str(GPSMODE)
  camera.exif_tags['GPS.GPSLatitudeRef'] = exif_lat_ref(GPSLAT)
  camera.exif_tags['GPS.GPSLatitude'] = LAT
  camera.exif_tags['GPS.GPSLongitudeRef'] = exif_long_ref(GPSLONG)
  camera.exif_tags['GPS.GPSLongitude'] = LONG
  # now store the info in the logfile
  f=open(LOGFILE, 'a')
  cpu_temp = get_cpu_temperature()  # go get the RPi CPU temperature
  DATA1=OSDATETIME,str(GPSLAT),str(GPSLONG),str(GPSALT),str(GPSDOP),str(GPSMODE),str(cpu_temp)
  DATA=','.join(DATA1)+'\n'
  f.write(DATA)
  f.close()

#setup buzzer gpio port
wiringpi2.wiringPiSetupGpio()
wiringpi2.pinMode(10,1)

#create controller
gpsc = GpsController()

#start controller
gpsc.start()
time.sleep(3)

#do a test read latitude and longitude. sometimes I get a useless 0.0 reading
while gpsc.fix.latitude == 0.0 or gpsc.fix.longitude == 0.0:
   # Loop till we get a correct reading from gpsd
   time.sleep(1)

# finish off building the master filename
FILENAME = FILENAME+datetime.datetime.now().strftime("%Y-%m-%d_%H%M_")
LOGFILE = LOGFILE+datetime.datetime.now().strftime("%Y-%m-%d_%H%M.csv")
with picamera.PiCamera() as camera:
  # Setup variables for PiCamera
  setup_exif()  # go do the intial reading of GPS data for the 1st picture
  sound_buzzer(beep_short)
  camera.resolution = (photo_width, photo_height)

  # now start the looping and taking the requested sequence of photos
  for i, filename in enumerate(camera.capture_continuous(FILENAME+'{counter:05d}.jpg')):
     print(filename)
     time.sleep(interval_time)
     sound_buzzer(beep_short)
     # Setup variables for PiCamera
     setup_exif()  # go get another reading of GPS data for the next picture
     if i == num_photos-1:
        break

#stop controller
gpsc.stopController()

# let people know that the program has finished
sound_buzzer(beep_long)

exit()