The Leuschner spectrometer is a FPGA design running on a ROACH, along with Python code communicating with it using KATCP and reading data to FITS files.

## IDL Interface¶

Astro 121 students can use IDL to collect spectra and save them to a FITS file with the IDL function leuschner_rx, which is installed on the Leuschner Radio server onsite at the dish. Python users can use the python function read_spec(), which has the same functionality. The inputs of leuschner_rx are as follows:

• filename: Name of the output FITS file.
• nspec: Number of spectra to collect.
• lonra: Either galacic longitude or right ascension, in degrees.
• latdec: Either galactic latitude or declination, in degrees.
• system: Coordinate system of coordinates. Either ga or eq.

The leuschner_rx function returns the status of the accumulation. If the status is 0, then everything worked as intended. If not, then there was an error. Here is an example of how leuschner_rx is run in IDL:

IDL> status = leuschner_rx('spectra.fits', 100, 210.0, 0.0, 'ga')
IDL> print, status
0


Alternatively, one can do the same thing in python easily:

>>> import leuschner
>>> spec = leuschner.Spectrometer('10.0.1.2')
>>> spec.read_spec('spectra.fits', 100, (210.0, 0), 'ga')


## Installation¶

All you need to do is run python setup.py install and the software will be installed. The .bof file to be run on the ROACH is provided in the fpga directory of the rpoco8 git repo. It must be placed in the /boffiles directory on the ROACH so that KATCP knows that it exists. The spectrometer code refers to the basename of the bof file, spec_ds8_8192.bof as is, so that shouldn’t be changed. The IDL file leuschner_rx.pro, which can be found in the idl directory of the spectrometer git repo, can be placed anywhere, and the name of the directory that it’s placed needs to be added to the environment variable IDL_PATH in order for IDL to know about it. If there are lots messages stating WARNING: Cannot reach the ROACH. Skipping integration., then place a 1 GiB network switch between the ROACH and the computer communicating with it and those messages should hopefully go away.

## Clocking the spectrometer¶

The input clock to the iADC on the ROACH board needs to be set to 768 MHz using an external local oscillator. The reason for this frequency is because the iADC needs to be clocked four times the speed of the FPGA on the ROACH. Due to hardware constraints, the ROACH is running eight times the speed of the desired sampling rate and the data is downsampled by a factor of eight in the time domain. The actual sampling rate of the spectrometer is 24 MHz, giving a 12 MHz bandwidth. The spectrometer has 8192 frequency channels, giving a resolution of about 1.4 kHz.

## Dependencies¶

This software requires the modules numpy, pyephem, pyfits, corr, and whatever those modules depend on.

# Leuschner Spectrometer Python API¶

class leuschner.Spectrometer(*args, **kwargs)[source]

KATCP interface to the ROACH spectrometer.

check_connected(timeout=10)[source]

This function checks if the ROACH is connected, and raises an IOError if the client can’t reach the ROACH.

Input:

• timeout: The amount of seconds to wait before IOError.
check_running()[source]

This function checks to see if the bof process for the spectrometer has been initialized on the ROACH. I’ve put a constant in the design that translates to the mode of the spectrometer, and if it can be read, this indicates that the bof process has been started.

fits_create(nspec, coords, system='ga')[source]

Open a fits file for reading and create a primary HDU with the observation attributes.

Inputs:

• nspec: Number of spectra to collect.
• coords: Coordinates of the target of observation. Format: (lon/ra, lat/dec)
• system: Coordinate system of coords (eq, ga).

Return:

• This function returns a primary HDU with a header containing the attributes of the observation.
init_spec(scale=False, force_restart=False)[source]

This function starts the bof process on the ROACH. First, it checks to see if the spectrometer is already running, then initializes it if it isn’t.

Input:

• scale: Whether or not to scale down each integration by the total number of spectra per integration time.
• force_restart: Restart the bof process even if it is already running.
poll()[source]

This function waits until the integration count has been incrimented and returns the date of the integration in seconds since Jan 1, 1970 UTC.

Return:

• obs_date: Unix time of the integration.

This function reads out data from a ROACH BRAM. The data is stored in the ROACH as 32-bit fixed point numbers with the binary point at the 30th bit.

Input:

• bram: The name of the BRAM to read data from.

Output:

• bram_fp: Array of floats of the ROACH BRAM values.

This function recieves data from the Leuschner spectrometer and saves it to a FITS file. The first HDU of the FITS file contains information about the observation, such as the coordinates, the number of integrations accumulated, and attributes about the spectrometer used to collect the data. Each set of spectra is stored in its own FITS table in the FITS file. The columns in each FITS table are auto0_real, auto1_real, cross_real, and cross_imag, and all of the columns contain double-precision floating-point numbers.

Inputs:

• filename: Name of the output FITS file.
• nspec: Number of spectra to collect.
• coords: Coordinates of the target of observation. Format: (lon/ra, lat/dec). Units: degrees.
• system: Coordinate system of coords (eq, ga).
reconnect()[source]

This function can be run if the spectrometer can’t be reached in the middle of data collection. This should only be run if the bof process for the spectrometer has been already started.

set_fft_shift(fft_shift)[source]

This function allows the user to change the FFT shifting instructions on the ROACH.

Input:

• fft_shift: FFT shifting instructions for the ROACH.
set_scale(scale)[source]

This function allows the user to change whether or not to scale spectra by the number of spectra integrated per accumulation.

Input:

• scale: Whether or not to downscale the spectra.

This function unpacks coordinates from a tuple and converts them from degrees to radians.

Input:

• coords: Coordinate pair in degrees.
leuschner.ephem2deg(angle)[source]

This function converts an ephem angle into degrees.

Input:

• angle: Ephem angle object.
leuschner.ephem_time(date=None)[source]

This function creates and ephem date object out of a unix time.

Input:

• date: Date in seconds since the Epoch (Jan 1, 1970 UTC).
leuschner.get_epoch(date=None)[source]

This function converts a unix-time into a string in a format that can be used as an ephem epoch.

Input:

• date: Date in seconds since the Epoch (Jan 1, 1970 UTC).
leuschner.julian_date(date=None)[source]

This function converts seconds since the epoch to a julian date.

Input:

• date: Date in seconds since the Epoch (Jan 1, 1970 UTC).
leuschner.mk_flt_col(name, data)[source]

Create a FITS column of double-precision floating data.

Input:

• name: Name of the FITS column.
• data: Array of data for the FITS column.
leuschner.mode_int2str(num)[source]

This converts a mode number into 4-letter human readable string.

Input:

• num: Number less than 2^32 encoding the spectrometer mode.

Return:

• mode: String containing the mode information.
leuschner.mode_str2int(mode)[source]

This converts a 4-digit mode string into an integer encoding it.

Input:

• mode: String containing the mode information.

Return:

• num: Number less than 2^32 encoding the spectrometer mode.
leuschner.utc_string(date=None)[source]

Convert unix time to UTC string.

Input:

• date: Date in seconds since the Epoch (Jan 1, 1970 UTC).