Decoding SSDV from JY1SAT

JY1SAT is a Jordanian 1U Amateur cubesat that carries a FUNcube payload by AMSAT-UK. As usual, the FUNcube payload on-board JY1SAT has a linear transponder with uplink in the 435MHz band and downlink in the 145MHz band, and a 1k2 BPSK telemetry transmitter in the 145MHz band. The novelty in comparison to the older FUNcube satellites is that the BPSK transmitter is also used to send SSDV images and Codec2 digital voice data.

Here I show how to decode the SSDV images using gr-satellites.

The SSDV images can be decoded with the FUNcube dashboard software, but this software is closed-source and there is no public description about the format in which the images are sent. Scott Chapman K4KDR and I have been exchanging emails with some people in the FUNcube team to get some details about the format. With the help of this information I have been able to figure out how the SSDV data is embedded into the telemetry. However, I would still be grateful for a complete telemetry description. In particular, I don’t know what is the format of the realtime telemetry for JY1SAT or how Codec2 digital voice recordings are sent.

The JY1SAT frames, as in the case of any other FUNcube satellite, are 256 bytes long. This size comes determined by the AO-40 FEC protocol they use. As we can see in the telemetry description in gr-satellites, a frame is composed of a 2 byte header, which encodes the type of satellite and frame, 54 bytes of real-time data, and 200 bytes of payload. Normally, the payload is used to send whole orbit or high resolution telemetry data, or fitter messages.

In the case of JY1SAT SSDV frames, the 200 bytes of payload are used to embed an SSDV frame, with a custom header, so as to save some space.

To test my decoder, I have been using this recording made by Scott. The SSDV frames in that recording have a header indicating SatID = extended, FrameType = 32 or 33, and extheader = 0x10. I don’t know why the SSDV data is sent in two different frame types. A complete telemetry description would clarify this. Some other data (perhaps Codec2 data) is also set with the same frame types and extended header as SSDV.

An ad-hoc header is used for the SSDV frame contained in the 200 byte payload. The differences between the JY1SAT SSDV frame and the usual frame format are the following:

  • The callsign field is omitted
  • The payload data is only 189 bytes long
  • The checksum and FEC fields are omitted

With these details in mind, I have adapted the SSDV decoder to support this ad-hoc format. The adapted SSDV decoder can be found in my ssdv fork. This decoder now supports the DSLWP-B SSDV format, the JY1SAT format, and the standard format.

To decode SSDV data using gr-satellites, the instructions are as follows. You can use the recording by Scott to test the decoder. Note that you need to use a BFO parameter of 2000Hz in the gr-satellites decoder with this recording, rather than the default of 1500Hz. You can edit this value in the GNU Radio flowgraph. You also need to have installed my ssdv fork.

The JY1SAT decoder in gr-satellites saves all the received frames to a file called jy1sat_frames.bin in the current directory. You can change the path of this file by editing the GNU Radio flowgraph of the decoder. This file is appended to when the decoder runs, since an image can be completed by using data from several passes. You can download a sample jy1sat_frames.bin file in this gist. This sample file is taken from Scott’s recording.

The jy1sat_ssdv.py script in the apps folder of gr-satellites needs to be run on the jy1sat_frames.bin to detect the different images, classify and sort the SSDV frames corresponding to each image, and call the SSDV decoder for each image. It can be run as

jy1sat_ssdv.py jy1sat_frames.bin jy1sat_output

This will produce files jy1sat_output_n.ssdv and jy1sat_output_n.jpg for each of the images, where n is the image number. Running this script on the jy1sat_frames.bin sample file given above, we obtain the following partial image.

JY1SAT partial SSDV image decoded from Scott’s recording

14 comments

  1. Thanks for adding another fun decoder!

    I found it helpful to add the location of where the ssdv fork was installed to my PATH so that jy1sat_ssdv.py could find it.

    -Scott, K4KDR

  2. Thanks! It works fine.

    I found your jy1sat_frames.bin and dashboard’s capture.funcubebin have the same format.

    So I did not have to start from scratch.

  3. Not definitely a JY1SAT question, but rather the gnuradio-companion and gqrx hoping you could help. So here’s my setup, GQRX streaming audio UDP at port 7355, I could monitor the audio is there by using nc -l -u 7355.

    Openned jy1sat.grc from gnuradio-companion, parameters are all set (ip localhost, udp port 7355, even my callsign and auth code), when I execute, there’s a terminal popping out which is good, but I don’t see anything happening in that terminal even though I already have a good reception of the sat telemetry. Interesting, if I activate the “rec” in the gqrx beside the “udp” button, let it record for a couple of minutes or so, then afterwards pressing the “play”. I could see in that terminal that popped out I mentioned, that it’s really decoding frames as well as I could see the it’s being uploaded to the amsat-uk warehouse.

    I wonder why I can’t decode in real time using GQRX with UDP 7355 is active, it need me to record the audio and play it directly from gqrx for me to decode something.

    If there’s nothing coming out from gqrx, then what’s that gibberish characters I see when i run nc -l -u 7355? and if there’s really nothing comin out from gqrx, why i can decode from a recorded wav using gqrx’s built wav player?

    Btw, amazing work with these decoders!

    1. Hi, that seems a weird problem indeed. I guess you have indeed data coming out of GQRX if you’re able to see it with nc. I don’t know what to make out of the fact that it worked when you first recorded and the played it back. Maybe it was a lucky coincidence or some other hidden factor of some sort.

      Not that it will help ruling out any weird possibilities, but in order to have another test case you can try playing the ao73.wav file in satellite-recordings/ (which the JY1SAT decoder should decode fine) into the decoder using UDP. The easiest way to do this is with wav_48kHz.py in gr-frontends, but you could also use something like aplay | nc.

      1. Thanks for the quick reply! Yes, I already did that too. And it decodes fine as well. So actually going here asking for help was really my last resort.

        Is there any logging setting I could set so that once the terminal opened after executing the flow graph, I could see if the decoder sees something from the port 7355? It seems that the terminal stays blank until it started to decode and hear something.

        1. Maybe change the flowgraph type to QT GUI and put a QT GUI Frequency Sink block near the input to see the spectrum of the samples that arrive into the decoder?

          1. Oh! that’s a very good idea! Why i haven’t thought that! Thanks, Daniel. I will report back, hopefully I could sort this out soon. I’m pretty stoked using your decoders!! It’s to the next level of decoding satellite.

            Thanks for always helping!

  4. Hi Daniel,
    So I recorded EO-88 pass using i/q of gqrx, the sat pass was almost overhead, SNR was very good. This is what i am using to test the UDP stream feature of gqrx. So that I’ll not be waiting for a satellite to decode its telemetry in real time from gqrx’s waterfall.

    When I run gnuradio-companion, execute nayif1.grc with ip 127.0.0.1 port 7355. I found out that sometimes your decoder works but mostly not.

    I did the your suggestion about QT GUI frequency sink on a new a flow graph, so I could verify if streamed audio is there indeed I could see the audio comin from gqrx.

    Running the wav_48khz.grc using the wav recording from this same recorded i/q, it works completely fine. I could see decoded messages

    It looks like as soon I press the execute from gnuradio-companion, something is happening at gqrx udp port. Not sure.

    Might be possible that gqrx 2.11.5 i am using is purely based from gnuradio 3.7 while the gnuradio I am using to run your decoders is 3.8?

    1. The fact that GQRX uses GNU Radio 3.7 while gr-satellites uses 3.8 shouldn’t make any difference. Both are kept “well-separated” and only communicate by an UDP socket.

      The fact that everything works fine with a recording but not in real time is starting to make me think that maybe the problem is CPU power. If your CPU is not powerful enough to run both GQRX and gr-satellites then you might get dropped samples in the UDP socket or elsewhere.

  5. btw, I’m on ubuntu 18.04 LTS running on imac 27in. Its CPU is 3.8 GHz Core i5 (I5-7600K) with 24Gb of ram. I don’t think my issue is with the cpu processing power, but rather something with ubuntu itself.

    Interestingly, sometimes but not most of the time, gnuradio (while the decoding is running live) intermitently can receive udp packets coming from gqrx. This happens when I reload or refresh my internet browser, or if I open an app that requires internet connection.. It’s like kick starting the UDP at 7355 to work.

    I already lost the battle of finding the issue. So for now, I’m just recording the pass to a wav file and decode it afterwards.

  6. Hi Daniel,

    Got some good news, i was able to get your decoder listening on port UDP 7355, decoding in real time now.

    I think it boils down to the my sdr Airspy R2, I’ve been using this dongle at 10MSPS. Today I have tried setting it only 2.5MSPS and viola it worked!

    1. So maybe it was a problem with lost samples, after all. Streaming 10Msps through the USB without losing samples is not always an easy task. It’s not only related to CPU power, but also to having large buffers to handle jitter, but this sacrifices latency.

      I’m glad this is working now.

Leave a Reply to DV2JB Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.