AIS Channels Streaming

It is possible to stream the 4 filtered baseband AIS channels over the Ethernet interface, while the Polaris AIS receiver do the normal demodulation.

Data Format

Each stream is 16 bit complex signed integer with a sample rate of 96 Ksps. The data from the 4 AIS channels is interleaved and placed in a UDP packet with as size of 1448 and sent to port 53032. The first 8 bytes of each UDP packet is a sequence counter sent in little endian, this can be used to detect packet loss between the Polaris AIS receiver and the destination. The UDP packet format is show in UDP Packet Data Format.

UDP Packet Data Format

Byte

0-7

8-11

12-15

16-19

20-23

1444-1447

Field content

Sequence cnt.

I0,Q0

I1,Q1

I2,Q2

I3,Q3

I3,Q3

Example Setup

In the following example the Polaris AIS is setup to stream samples to a PC with a fixed IP address set to 192.168.100.22 and on the same network as the Polaris AIS. The Polaris AIS will be configured to have IP address 192.168.100.20.

../_images/iq-stream-network.svg

Example network setup

In the debug shell on the Polaris AIS:

  • Follow Getting Started to setup communication with the receiver.

  • Connect a cable to the Ethernet connector

  • prop set sys.net.ip 192.168.100.20

  • prop set sys.net.prefix 24

  • prop set sys.net.gw 192.168.100.1

  • prop set sys.net.enable

  • prop set demod.iq.dest.ip 192.168.100.22

  • prop set demod.iq.enable 1

Now the Polaris AIS will start sending UDP packets to 192.168.100.22. The code example below, written in Python, show how to receive the data from a PC.

#!/usr/bin/env python

import struct
import socket

HOST = ''                 # Symbolic name meaning all available interfaces
PORT = 53032              # Port that the Polaris AIS is sending data to

POLARIS_AIS_UDP_SIZE = 1448

with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
    s.bind((HOST, PORT))
    while True:
        data = s.recv(POLARIS_AIS_UDP_SIZE)
        seq_nr = struct.unpack("<Q", data[:8]) # Unpack sequence number
        iq_samples = struct.unpack("<" + "h" * 720, data[8:]) # Unpack signed shorts
        i0 = iq_samples[0::8] # Starting from 0 every 8 sample is from channel I0
        q0 = iq_samples[1::8] # Starting from 1 every 8 sample is from channel Q0
        i1 = iq_samples[2::8] # Starting from 2 every 8 sample is from channel I1
        q1 = iq_samples[3::8] # Starting from 3 every 8 sample is from channel Q1
        i2 = iq_samples[4::8] # Starting from 4 every 8 sample is from channel I2
        q2 = iq_samples[5::8] # Starting from 5 every 8 sample is from channel Q2
        i3 = iq_samples[6::8] # Starting from 6 every 8 sample is from channel I3
        q3 = iq_samples[7::8] # Starting from 7 every 8 sample is from channel Q3
        print(f"Seq. nr. {seq_nr[0]}, {i0[0]:+06d}, {q0[0]:+06d}, {i1[0]:+06d}, {q1[0]:+06d}, {i2[0]:+06d}, {q2[0]:+06d}, {i3[0]:+06d}, {q3[0]:+06d}")

For each UDP packet the script extracts the sequence number and I/Q from the 4 sample streams. The channel mapping are as follows:

  • CH0: 156.825 MHz

  • CH1: 156.775 MHz

  • CH2: 161.975 MHz

  • CH3: 162.025 MHz