Using the Raspberry Pi with PiCam camera as Slow Scan Television (SSTV) camera.
Updates on this project can be found here: Agri Vision - Raspberry Pi SSTV Camera
Introduction
In this project the Raspberry Pi with the PiCam is used as a wireless camera which can transmit images over long distances, usually tenths 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!
Originally this blog was part of the Raspberry Pi Santa Catcher with Pi NoIR and PiFace CAD road test (http://www.element14.com/community/roadTests/1220).
I already did a review, mainly on the spectral and spatial resolution aspects of the camera (http://www.element14.com/community/roadTestReviews/1660).
Latest info on the project: Agri Vision - Raspberry Pi SSTV Camera
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 smallmimage, 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 -lm -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]. FILE ptrs opened. Image ptr opened. Adding VIS header to audio data. Done adding VIS header to audio data. Adding image to audio data. Done adding image to audio data. Adding VIS trailer to audio data. Done adding VIS trailer to audio data. 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.
This 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.
This 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 right 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 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 pixe 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.
Of course this is rather simple, and I'm thinking of using opencv to do some fancy image processing, for instance to detect the hat of Sinterclaas, or the face of one of his servants. But that is future work which can't be done before the deadline of this challenge.
Video of the camera in action
GitHub
Sources can be found on github: https://github.com/AgriVision/pisstv.
Credits
Credits to KI4MCW (sstv), Oliver Mattos and Oskar Weigl (pifm).
Top Comments