Agri Vision - Raspberry Pi SSTV Camera

Digital Signal Processing, Web Interfacing, HAM Radio, Software Defined Radio and More...

Search

Home

 

Content View Hits : 276412
We have 6 guests online

Raspberry Pi SSTV Camera

PDF Print E-mail
Using the Raspberry Pi with PiCam camera as Slow Scan Television (SSTV) camera.


Wow! this project won the Make Raspberry Pi contest.




Introduction:

In this project the Raspberry Pi with the PiCam is used as a wireless camera which can transmit images over long distances, usually tens of kilometers. Images will be transmitted by amateur radio (ham-radio) using slow scan television (SSTV) on the 2 meter band (144.5 MHz). Since the Pi can generate the HF FM signal itself, no additional electronics are needed for low power transmissions. For a little bit more power a one or two transistor amplifier will be suitable. Furthermore a low pass filter is recommended to filter out higher harmonics of the signal. This project also contains a python script which detects movement. Using this script the Raspberry Pi can be used as a wireless security cam at distances far outside the range of normal WiFi networks. Be aware that you need a ham-radio license to use this application!

Complete portable pisstv transmitter.


Capturing the image:

First thing to do is capturing the image we want to transmit. This can easily be done with raspistill:
raspistill -t 1 --width 320 --height 256 -e png -o /tmp/image.png   
For sstv we need a small image, of 320 x 256, it is saved into the /tmp directory as png.


Converting the image to a SSTV sound file:

Next we need to convert the image to a sound file which can be transmitted over the air. There are several SSTV implementations available for the Raspberry Pi.

PySSTV

First I had a look at PySSTV, a Python implementation which can be installed using pip:
pi@rpicamera ~/sstv $ sudo apt-get install python-setuptools
pi@rpicamera ~/sstv $ sudo apt-get install python-imaging
pi@rpicamera ~/sstv $ sudo easy_install pip  
pi@rpicamera ~/sstv $ sudo pip install setuptools --no-use-wheel --upgrade  
pi@rpicamera ~/sstv $ sudo pip install PySSTV  
This works, but it is very slooooooooooooow, it takes many minutes to convert a single image. So I continued to search for another option.

C implementation

Next I found a plain C implementation here: https://sites.google.com/site/ki4mcw/Home/sstv-via-uc Unfortunately there were some errors in the preamble tones, but those were easy to fix. I also made it a little bit more flexible so that you can set the audio sample rate from the commandline line. Source of my implementation can be found on GitHub. To compile the source code:
pi@rpicamera ~/sstv $ sudo apt-get install libgd2-xpm-dev  
pi@rpicamera ~/sstv $ sudo apt-get install libmagic-dev  
pi@rpicamera ~/sstv $ gcc -lgd -lmagic -o pisstv pisstv.c
To run the program:
pi@rpicamera ~/pisstv $ ./pisstv /tmp/image.png 22050  
Constants check:
        rate = 22050
        BITS = 16
      VOLPCT = 20
       scale = 6553
     us/samp = 45.351474
     2p/rate = 0.000285
 Checking filetype for file [/tmp/image.png]
 File is a PNG image.
 Input  file is [/tmp/image.png].
 Output file is [/tmp/image.png.wav].  
 Writing audio data to file.
 Got a total of [2589556] samples.
 Done writing to audio file.
 Created soundfile in 4 seconds.
As you can see the SSTV sound file is created in just 4 seconds. So far so good, next step, how to transmit the audio over the air.


Transmitting the sound file with PiFm:

You can add a radio transmitter, like a portable radio transceiver, but its much more fun to let the Pi itself generate the high frequency signal. Thanks to Oliver Mattos and Oskar Weigl this is possible. You can find their code here: Turning the Raspberry Pi Into an FM Transmitter - Imperial College Robotics Society Wiki. Their code has evolved considerably. The first version was very simple, but used all cpu cycles, and the signal was hampered by glitches when other processes were active. The last version uses dma and works pretty good, without eating up all cpu cycles. Nevertheless the code is much more complex now. Oliver and Oskar did a very good job, but out of the box the software is not suitable for ham-radio and SSTV. There are mainly two problems. First the bandwidth is to high and secondly the timing which is very important for SSTV was a little bit off.

Reducing the bandwidth

Reducing the bandwidth appeared to be very simple. As every ham knows, for frequency modulation the bandwidth can be set with the modulation index, which is equal to the volume of the audio signal which modulates the hf carrier. In the source code it is just one value it can be found in the consume function of the Outputter class. Here is the original code:
void consume(float* data, int num) {
   for (int i=0; i<num;i++){
       float value = data[i]*8;  // modulation index (AKA volume!)
I made a command line parameter of this value, the new code looks like:
void consume(float* data, int num) {
   for (int i=0; i<num;i++){
       float value = data[i]*modulation_index;  // modulation index (AKA volume!) (original 8)
Unfortunately this does not work very well, very strong sidebands persists, so this needs some focus in future versions of the software.

This figure shows a spectral plot of the full bandwidth FM signal.

The second spectrum is the reduced bandwidth, tuning on the peak in the middle shows a nice and clean signal, but we need to get rid of the sidebands.

The last one is the reduced bandwidth signal of the first version of PiFm, nice bandwidth, but the signal is hampered by clicks due to cpu activity in other processes.

Fixing the timing

When the sample rate of audio transmitted by PiFm is slightly larger or smaller, a listener hardly notice any difference. For SSTV this is not the case, SSTV timing is very precise. A slightly off sample rate results in slanted images, as can be seen below on the left. The second image is the same sound file properly sampled.



Fixing the timing appeared to be straight forward.

//clocksPerSample = 22500.0 / rate * 1373.5;  // for timing, determined by experiment
clocksPerSample = 22050.0 / rate * timing_correction;  // for timing, determined by experiment  
As you can see I replaced the timing constant (1373.5) in the code with the variable 'timing_correction' which can be set from the command line. I expect a different value for each individual Rpi. In my case the value is 1414.0. I'm just curious which is the proper value for you, please comment your value on this blog post. For all other adaptions to the code, see the source file at GitHub.


Adding call-sign:

When you start transmitting SSTV signals using your ham-radio license, you are required to transmit your call-sign in every transmission, so we need to add this information to the image. This can easily be done either from the command line using imagick, or from python using the python image library (PIL). Both are used in this project. In sstvcam.sh mogrify which is part of imagick is used. sstvcam.sh is a simple shell script to just capture and transmit an image. In sstvcatch.py I used PIL.


Catching movement:

Now we are able to grab an image and send it properly over the air using PiFm. We now need to focus on triggering the image capture when something interesting happens in front of the camera. I have implemented this in python, using PIL. The code can be found in sstvcatch.py. It works quite straight forward, it just compares the pixels of the previous image with the current image. When the difference is to large, the current image is transmitted. Here is a code snippet:
# loop forever  while (True):
          # grab comparison image
          imgnew, bufnew = captureImage()
          # Count changed pixel
          changedPixels = 0
          for x in xrange(0, 320):
                  for y in xrange(0, 256):
                          # Just check red channel as it's dominant for PiCam NoIR
                          pixdiff = abs(buf[x,y][0] - bufnew[x,y][0])
                          if pixdiff > threshold:
                                  changedPixels += 1
          # Transmit an image if pixels changed
          if changedPixels > sensitivity:
                  # Swap comparison buffers
                  img = imgnew
                  buf = bufnew
                  transmitImage(img.copy())
The full code can be found on GitHub.


Links


Credits:

Credits to KI4MCW (sstv), Oliver Mattos and Oskar Weigl (pifm).