I have wanted to implement a simple APT decoder for the NOAA weather satellites in GNU Radio for quite some time now, in particular since the USRP equipped with a TVRX or WBX daughterboard and a GNU Radio FM receiver can be an extremely good receiver for this purpose. Today I have spent some time looking at the details of decoding APT and actually ended up with a working prototype implemented using the GNU Radio Companion.
The APT format is very simple and straightforward. The pixel intensity from the instruments is used to AM modulate a 2.4 kHz tone, the subcarrier, which is then used to FM modulate the 137 MHz carrier. Therefore, the receiver consists of an FM receiver followed by an amplitude detector that translates the amplitude of the 2.4 kHz audio tone to pixel intensity on an 8 bit gray scale. The details including line sync and framing are available in the NOAA KLM user's guide section 4.2.
In practice the amplitude detector is a little more complicated because the symbol rate is 4160 symbols/second, which is greater than the frequency of the 2.4 kHz subcarrier. This means that we have to measure the amplitude of the sub-carrier almost twice in each period which may seem a little weird given all this stuff about Nyquist rate and what not.
A simple way to do this is presented in Wxapt by 5B4AZ: If we convert the audio to 9600 kHz, the 2.4 kHz subcarrier will have 4 samples during each period. Therefore, two consecutive samples will have a phase difference of 90° between them. The instantaneous amplitude of the 2.4 kHz subcarrier will then be given by:
A = sqrt(s12 + s22)
where s1 and s2 are two consecutive samples. If it's not obvious, try to draw a sine wave and do the math using any two samples separated by 90°. You'll see that the above formula gives the correct amplitude regardless of where you pick the samples.
An implementation of this algorithm in GNU Radio Companion is shown below. The band pass filter is necessary to reduce the noise in the audio, which can be quite significant in particular for audio with higher rate, like 44.1 or 48 kHz. since there is no square root in GNU Radio Companion I pack S1 and S2 into a complex sample and calculate the magnitude. Finally, the output stream is resampled to 4160 samples per second which is the symbol rate of the APT data. The data is converted to unsigned char corresponding to the 8 bit resolution of APT.
To convert the output of the decoder to an image we can use ImageMagick:
convert -size 2080x1500 -depth 8 gray:input.dat output.png
The resulting image is shown below. The black bar at the bottom is because there wasn't enough data for 1500 lines.
As you can see a practical decoder will need some more tuning to produce a perfect image. The dynamic range looks quite good though, as you can see from the telemetry bars. The same data decoded and contrast enhanced using wxtoimg on Linux is available here.
I am not sure where the vertical curvature comes from. It can not be Doppler shift since the carrier is frequency modulated and any uncompensated Doppler shift would only cause DC offset of the audio. If it was a hardware clock or sample rate issue it wouldn't change direction in the middle of the pass. By the way, no soundcard was involved in the recording, only USRP. Maybe it is related to the conversion process which included recording to WAV, converting to MP3, then converting back to WAV. I will try with some original WAV files later.