Sunday, 27 November 2016

ADC -- Jupiter Modular Receiver


Click for the Master Index to this project

Radio astronomy requires a receiver output data stream to make sense of what you receive. Computer programs may convert the data into graphs, FFTs, spectrograms, or pool it with other data in hopes of digging the signal out of the noise. At this point, I'm happy with a simple X-Y graph and hopefully my methods will evolve over time.

After much reading + thinking and some hardware failures, I settled on a commercial product to stream data into my computer via a USB cable and then use homebrew + commercial programs to massage my data.

Above — A 4 analog channel, 15-bit ADC from Electronic Energy Control, in Mildford Center, Ohio, USA. The product is called the ADC-4U15. Click for the EECI Website
EECI provides amazing customer service plus innovative solutions for data streaming. 

The on-board USB-to-Serial Bridge Controller = a Prolific PL-2303 HX.  Click for Prolific's web site. The PL-2303 contains an on-chip clock, so I put the board in a RF shielded container fashioned with 2-sided copper clad board. Unlike some ADCs sold today, the PL-2303's drivers were MS Windows certified; they installed and worked on my first try.

Above — I carved out a hole for the USB, Type B connector with a hammer and chisel before soldering it to the rest of the enclosure. My USB cable has ferrite beads on both ends. On the RCA input port, I placed a 100n bypass capacitor.

I'm currently only using Channel 1 of the ADC, so the other 3 inputs are shorted to ground.

Data Streaming and Code  

To run the ADC--4U15,  you start a supplied application that establishes communication to the ADC and an output number between 0 and 32767 ( 15 - bits) appears for each of the 4 data channels.

Above — The ADC-4U15 controller application. Once I plug the ADC to its USB port and start this application, I normally hide it -- and it runs in the background.

Above — setup dialog box for the  ADC-4U15 controller application. Once you set the com port, sample rate and maximum voltage of your data channel(s), it's set and forget. This application provides a data logger output, but you don't have to use it. EECI provides some source code to get you going in both C# and VB. The ADC-4U15 application is a combination user interface and USB driver -- you read the 4 digital channels from a Windows memory label

In Microsoft Visual Studio ( free version) I followed Bert from EECIs instructions and created an application that read the Windows memory map and it compiled without errors!

Then it's up to you to add code to convert the 2 byte pairs of each channel into a 16 bit integer -- and then further, to convert it to some usable unit to graph. Of course, you'll need to write code to write to file -- or graph within your application. I chose the former for now.

I kept my units as DC volts since this allowed me to test and calibrate my receiver system to ensure my code was working. At some point when I get my noise figure system going, I'll plot the Y axis in degrees Kelvin.

Since my maximum input reference voltage = 2.05v, to convert a 15 bit full scale integer into 2.05V full scale float, the code goes:  inputVolts[i] = 2.05f * ((float)inputInt[i]) / 0x8000;

Then, code was added to write the data to a CSV file.

Here's my meat and potatoes source code file:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.IO.MemoryMappedFiles;

namespace WindowsFormsApplication1
    public partial class Form1 : Form
        Byte[] inputByte = new byte[8];     //create byte array
        Int16[] inputInt = new Int16[4];
        float[] inputVolts = new float[4];
        Label[] MyLabelArray = new Label[4];
        StreamWriter csvWriter;
        DateTime startTime;

        public Form1()

        private void Form1_Load(object sender, EventArgs e)

            string csvFilename = string.Format(".\\{0}.csv", DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"));
            string csvPath = Path.GetFullPath( Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),csvFilename));
            csvWriter = new StreamWriter(csvPath, false, Encoding.ASCII);
            startTime = DateTime.UtcNow;

                "UTC Internet format"
                + ", " + "UTC Excel format"
                + ", " + "Local Text format"
                + ", " + "Local Excel format"
                + ", " + "Elapsed seconds"
                + ", " + "Data");

            // update the readings every 200ms
            timer1.Interval = 200;
            timer1.Enabled = true;

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)

        private void timer1_Tick(object sender, EventArgs e)
            string csvLine = ReadInputMemoryMap();
            DateTime currentTime = DateTime.UtcNow;
            TimeSpan elapsedTime = (currentTime - startTime);
            TimeSpan excelTime = currentTime - DateTime.Parse("1900-01-01 00:00:00").ToUniversalTime();

                currentTime.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'") // UTC Internet format
                + ", " + (excelTime.TotalDays - TimeZone.CurrentTimeZone.GetUtcOffset(currentTime.ToLocalTime()).TotalDays + 2).ToString("0.000000") // UTC Excel format
                + ", " + currentTime.ToLocalTime().ToString("yyyy'-'MM'-'dd HH':'mm':'ss'.'fff") // Local Text format
                + ", " + (excelTime.TotalDays + 2).ToString("0.000000") // Local Excel format
                + ", " + elapsedTime.TotalSeconds.ToString("0.000") // Elapsed seconds
                + ", " + csvLine); // data value

            for (int i = 0; i < 4; i++)
                MyLabelArray[i].Text = inputVolts[i].ToString("0.0####");

            labelTimeStamp.Text = elapsedTime.ToString(@"d\.hh\:mm\:ss");

        private string ReadInputMemoryMap()
                MemoryMappedFile file = MemoryMappedFile.OpenExisting("EECI_ADC4U15_OUT");
                MemoryMappedViewAccessor reader = file.CreateViewAccessor();
                for (int i = 0; i < 8; i++)
                    inputByte[i] = reader.ReadByte(i);

                for(int i=0; i<4; i++)
                    // convert 2 byte pairs into 16 bit integer
                    inputInt[i] = (Int16)((((UInt16)inputByte[i]) << 8) | ((UInt16)inputByte[i + 4]));

                    // convert 15 bit full scale integer into 2.05V full scale float
                    inputVolts[i] = 2.05f * ((float)inputInt[i]) / 0x8000;
            catch (Exception ex)
                labelChannel1.Text = ex.Message;

            return string.Format("{0:0.0####}", inputVolts[0]);

        private void CreateLabellArray()
            MyLabelArray[0] = labelChannel1;
            MyLabelArray[1] = labelChannel2;
            MyLabelArray[2] = labelChannel3;
            MyLabelArray[3] = labelChannel4;

The application looks like this currently.

Above — Channel 1 is expressed as a DC voltage. All other channels are shorted to ground and read 0. Once the app starts up, elapsed time gets displayed. The only purpose of this app is to read the data from memory, massage the data bytes into something usable, and write that data to a CSV file in a format that meets my requirements. The file name = the current timestamp.csv. This allows me to keep track of the CSV files by a unique namesake.

Above —The 6 column CSV file allows me to graph in UTC or local time. Microsoft Excel seems to suffer some time stamp issues, so the source code was adjusted to provide an accurate output for my location.

Above — a test plot of my Jupiter receiver during the early evening when my local noise is louder than after midnight. I've calibrated my receiver's DC output voltage against known amplitude input signals from -121 to - 47 dBm and wrote some of them on the graph in red.

My graphing process will likely change over time. It's nice to finally have data to work with.

1 comment:

  1. Thank you for c# code &&&&Mori Mizuno