Skip to content

Operation

We'll flesh out all the details on operation soon, but in the meantime here are the critical details.

Turning on the SNAP

To turn on the SNAP, SSH into the Pi (as discussed in the server setup), Then on the pi create (if it doesn't already exist) a bash script called snap.sh with the following:

Warning

This is assuming you have a V2 power supply, ON and OFF are reversed otherwise

#!/bin/env bash
# Usage: ./snap.sh <on|off>
BASE_GPIO_PATH=/sys/class/gpio
PWN_PIN=20
if [ ! -e $BASE_GPIO_PATH/gpio$PWN_PIN ]; then
  echo "20" > $BASE_GPIO_PATH/export
fi
echo "out" > $BASE_GPIO_PATH/gpio$PWN_PIN/direction
if [[ -z $1 ]];
then
    echo "Please pass `on` or `off` as an argument"
else
    case $1 in
    "on" | "ON")
    echo "0" > $BASE_GPIO_PATH/gpio$PWN_PIN/value
    ;;
    "off" | "OFF")
    echo "1" > $BASE_GPIO_PATH/gpio$PWN_PIN/value
    ;;
    *)
    echo "Please pass `on` or `off` as an argument"
    exit -1
    ;;
    esac
fi
exit 0

Make it executable is chmod +x snap.sh, and use ./snap.sh <on|off> to control the power state of the SNAP.

If you get permission errors when doing so, make sure your Pi's user is a member of the gpio group. You can add your user to the group with:

sudo usermod -a -G gpio $(whoami)

then relog and try again.

Running the Pipeline

In the grex folder, under pipeline there is the single bash script that runs the pipeline. Simply calling it ./grex.sh should start everything up. By default, it will run the normal detection pipeline. If you want to just run T0 (packet capture and first stage processing), remove the final line that calls psrdada and replace it (the whole line) with filterbank.

Triggering voltage dumps

Normally, T2 will send triggers to T0 to dump the voltage ringbuffer to disk. You can emulate this by sending a UDP packet to the trigger socket any other way. One simple way is with bash

echo " " > /dev/udp/localhost/65432

SSH Port Tunneling

In some circumstances, it may be useful to access ports on the GReX server on your local computer remotely. We can accomplish this using SSH Tunneling.

One example of why we might want to do this is to access the 10 GbE switch configuration that is located in the far-side GReX box. It runs a normal web page on a static ip of 192.168.88.1. You can access this from a web browser if you are sitting at the GReX server, but not remotely.

To access it using SSH tunneling, we can forward that IP's port 80 (standard HTTP) to our local computer at some unused, non-privaleged port.

ssh -L 8080:192.168.88.1:80 username@grex-server-address

Another example is perhaps you want to run a Jupypter Hub instance on the GReX server. In that case, the website it is hosting is on the server itself, so you would run:

ssh -L 8080:localhost:80 username@grex-server-address

Another useful one is access to the Prometheus time-series database used for monitoring. That is active on port 9090

ssh -L 9090:localhost:9090 username@grex-server-address

Pulse Injection

If you need to generate and inject fake pulses into raw voltages to test the pipeline, Liam Connor's injection codes contain all the relevant tools. In a Python Jupyter notebook, you can import this Python script and use the functions within to generate a fake pulse and write it to an output .dat file.

import simulate_frb
dt = 8.192e-6
width_sec = 2*dt # depending on your preferred pulse width
Nfreq = 2048
data, params = simulate_frb.gen_simulated_frb(NFREQ=Nfreq,
    NTIME=16384,
    sim=True,
    fluence=70, # This controls the pulse SNR
    spec_ind=0.0,
    width=width_sec,
    dm=100., # dispersion measure
    background_noise=np.zeros([2048, 16384]),
    delta_t=dt,
    plot_burst=False,
    freq=(1530.0, 1280.0),
    FREQ_REF=1405.0,
    scintillate=False,
    scat_tau_ref=0.0,
    disp_ind=2.0,
    conv_dmsmear=False,
)

Before writing to an output file, you might want to check what the pulse looks like and its histogram distribution, ensuring it’s not all zeros due to conversion to int8 (bit type of raw voltages).

from matplotlib import pyplot as plt
plt.hist(data.astype(np.int8).flatten(), bins=50)
plt.semilogy()
plt.show()
### also, visualize the pulse itself
plt.imshow(data.astype(np.int8), aspect='auto')
plt.colorbar()
plt.show()

If the pulse looks reasonable, we can convert to int8 type and write to an output .dat file.

with open('test_injpulse0.dat', 'wb') as f:
    f.write(data.astype(np.int8).tobytes())

Move this .dat file to ~/grex/pipeline/fake/, the specified directory in grex.sh, to actually inject the pulse. Then you could change the injection cadence (in seconds) in ~/grex/pipeline/.env by adding the following line.

injection_cadence=300

Now you are good to go.

Querying Historical Data

As part of the server setup, we've hooked up a time-series database (Prometheus) to T0, which is used to monitor the state of the telescope. This includes recording timestamped values for temperature, ADC counts, and integrated Stokes I data. It may be useful to query this data, which is relativly easy to do programatically using Prometheus' HTTP API.

You can explore the database by accessing http://localhost:9090 on the server (or with SSH-forwarding described above).

H1 Example

Say you want to query historical Stokes I data around the H1 frequency of 1420 MHz. This data is stored in "channels" where the index is the FPGA's channel number. If you recall, we operate in the first Nyquist zone, so the spectrum is flipped, where channel 0 represents 1530 MHz and channel 2048 represents 1280 MHz.

Using the requests library in Python, we can perform the HTTP request to the database.

import requests
import time
import numpy as np

# Get the current (UNIX) time
now = time.time()
# We want data, say two weeks back
past = now - 60 * 60* 24 * 7 * 2

# We want data around 1420 MHz, so around channel 893
# Might as well grab the block of data around it, so we can watch it change in frequency with time
channels = [889, 890, 891, 892, 893, 894, 895]
data = []
uri_base ='http://127.0.0.1:9090/api/v1/query_range'
for channel in channels:
    params = {"query": f'spectrum{{channel="{channel}"}}',
              "start": then,
              "end": now,
              "step": "10m"} # Depending on the timespan, there's a maximum size per query
    resp = requests.get(uri_base, params=params)
    # Extract the data  and convert to floats
    vals = resp.json()['data']['result'][0]['values']
    data.append(np.array(vals).astype(float))

# Restructure the data into a tensor
data = np.stack(data)

# The time axis is duplicated many times, we can extract it by looking at one chunk
timestamps = data[0,:,0]

# And then finally extract our 2D block of time/freq linear power data
# Indexed in [channel, time]
spectra = data[:,:,1]