Part 1: Introduction and Range
This is a technical article about how to use the Deep Space Network (DSN). I’m going to start high briefly and dive in deeply. You can google/wiki the top level stuff. I want to get right into how to translate from DSN formatted data types into range and range rate in kilometers/ kilometers per second.
I want to do this because it was unnecessarily hard for me. Not because of the theory but because of the lack of documentation or guidance. I’ve ranted before that the DSN’s idea of a car for sale is a bag of parts, some duplicates, some bad, no instructions.
There are reasons for this. Cynically speaking jargon promotes job security. Professionally speaking, deep space tracking is really tricky and everyone has their own idea on how to do it correctly. It’s best to provide a raw form of data and let the user determine the best way to implement it… and they can’t blame you if it doesn’t work.
OK for the high level stuff: Deep Space Network is a trio of stations that all have huge satellite dishes that communicate with and track spacecraft in deep space. That means anywhere from high Earth orbit out to beyond the solar system. The sites are in Goldstone CA, Madrid Spain, and Canberra Australia. Together they can provide continuous coverage of spacecraft in deep space.
Each dish transmits and receives from a spacecraft in some frequency, commonly S-band. Knowledge and handling of this frequency is central to modeling tracking measurements from the DSN.
The DSN can transmit with a sliding frequency. The frequency is ramped over time in an attempt to lock the frequency at the center carrier frequency of the receiving spacecraft due to Doppler shift. E.g. That firetruck that blows by you has a drop in pitch, but the DSN compensates by ramping the siren opposite so the pitch remains the same. In that analogy you are the satellite and the fire truck is a DSN station. Combination of the ramping profile and round trip light time is again central to this process.
We’re getting deeper. DSN sends its data in a binary format called TRK-2-34 (spoken “track two thirty-four”) which is a fancy dump of everything about the tracking that occurred. The TRK-2-34 data set has data types inside (called headers or CHDOs), which either tell information about the tracking or actual measurement values. For this purpose I’ll talk about data type 7 (and by extension 14) and data type 16 (next time). Those are sequential range, pseudo-noise range, and carrier frequency for Doppler i.e. range rate, respectively. In order to translate these data types into absolute range and range rate measurements, you will need the frequency ramping data, which is in data type 9.
An entire article could be written about just understanding and parsing and storing the data from TRK-2-34. I’m skipping that, I’m assuming you’ve got a TRK-2-34 file, parsed, and have access to the data fields within the data types above. Don’t worry, I’ll call out which fields from each data type you’ll need to access. Documentation on how to parse binary data files is common on the Internet. The documentation on the TRK-2-34 format is actually comprehensive, but doesn’t explain how to put the pieces together to get a usable measurement- I’ll do that below.
Data Type 7 and 14 Translation
Data type 7 (Sequential Range) and data type 14 (Pseudonoise Range) are two methods to shave the “how far away is it” cat. The range code for each method is different, but the method to translate those results into an absolute range in km is the same.
For reasons, range is returned in an ambiguous form using a partial value of range called Range Units, measured in seconds (time). There are at least two reasons I know for this. Range is ambiguous because the range code may cycle in its entirety multiple times in its travels to the target craft and back, and all you receive is what point along that code you received, not the whole multiple number of codes were behind it.
Another bad analogy: the range code can be thought of as a ruler. It’s possible you need to chain multiple rulers to measure out to the spacecraft and back, but all you receive is how many inches on the last ruler. You have to figure out the number of whole rulers and add that to the front of your measurement for the absolute value.
On top of that, the frequency could be ramping so the range code actually changes length when this happens. To counter this and receive a constant value regardless of frequency, DSN uses Range Units (RU) defined as seconds, so that a constant range under a ramping frequency still returns a constant value. Sure, whatever. Lets move on.
So basically the answer to “how far away is it” is “have a rough understanding of where you are in the first place, know the time history of transmitted frequencies, know how to estimate light time travel, and combine this knowledge to translate a partial measurement of the final ruler into an absolute range from station to spacecraft.”
You will need to combine several values across multiple DSN TRK-2-34 data fields and your spacecraft/solar system propagator of choice. I am using FreeFlyer by a.i. solutions, Inc. You can use any astrodynamics library if you know what you’re doing, which if you’ve read this far you likely don’t. Don’t worry, I didn’t, I still don’t, and most importantly no one does.
I’ll dump the fields you need like an ingredient list then the steps to synthesize them all like a recipe.
Ramped Uplink Frequency
In order to get a time history of ramped uplink frequency, you will need to collect all the instances of:
- Uplink Epoch- from year, doy, sec fields in the CHDO 134 (secondary header for derived data types).
- Ramp Frequency
- Ramp Rate
- Ramp Type – Ramp type 1 (new ramp) is really the only one you need
From there, you can get a frequency as a function of time as
f(t1) = f0 + f_rate (t1 - t_up)
Where t1 is the epoch you’re interested in, f0 is the Ramp Frequency (2), f_rate is the Ramp Rate (3), and t_up is the Uplink Epoch (1). Every new ramp entry (ramp type = 1) should have a frequency and rate, so you can interpolate between them linearly.
Like the list above states, the epoch is found in the secondary header CHDO 134, which has the epoch at which all the associated headers are anchored to. The rest of the values come from the Uplink Data Type 9 report: Ramps data.
Range Data Type
Building a range measurement in useful distance units requires knowledge of the frequency at which the ranging code is transmitted. I have some questions to the merit of this, or the notion of “ranging code” that repeats and is not unique, but I am not an expert so I assume someone’s got reasons to tell me why I’m wrong. I’ll save my critique for the end.
This time, I’ll start with the final equations and work back to the components. I’ll also show the more verbose method of manually including the corrections because in the case of PN range data there is no already-corrected form of the measured range. The final range in km is:
rangeFinal = (rCorrected + rngModulo * numRambs) * BC_over_2f - ((C/2) * (ulZHeightCorr + dlZHeightCorr))
There is a lot there, so let’s unpack it. You’ll notice some terms are bold- these are values that are read from the data and not computed. The same format applies moving forward. The components above are:
- rangeFinal: the range in km, what you want
- rCorrected: the measured range from the data after applying station calibrations
- rngModulo: the range modulo, as read from the data or calculated from other pieces of the data (this is the duplicate parts analogy, I am just reading it from the data, so I have someone to blame if it’s not right)
- numRambs: the number of whole range ambiguities (rulers) you calculate, which is shown below.
- BC_over_2f: literally B times C (speed of light) divided by 2 times uplink frequency. Notionally, it is the distance of one range unit in km. B is calculated also below.
- C: it bears repeating, C is the speed of light in km/s
- ulZHeightCorr/dlZHeightCorr: Z-height corrections are found in the data in the secondary header.
Where I state “from the data”, I mean the value lives in the tracking header for the downlink data type 7 or 14. There are three components in there that need further unpacking. rCorrected is simply:
rCorrected = measRng - (ulStnCal + dlStnCal)
Where measRng is the uncorrected range measurement in range units read straight from the type 7 or 14, and ulStnCal/dlStnCal are the uplink and downlink station calibration values, respectively, and also read from the downlink CHDO.
BC_over_2f = ((excScalarDen / (16 * excScalarNum)) * C) / (2 * freqGsXmt)
Where excScalarDen/excScalarNum are the denominator and numerator of the turnaround ratio, and are found in the CHDO 7 or 14. freqGsXmt is the groundstation transmit frequency found using the ramping formula above. If your DSN pass is not ramped, congratulations, you can likely simply read the frequency again from the CHDO 7 or 14. For the record, we don’t know if our data will be ramped, so I am assuming it is and put in the hooks to handle it. There is a possible lesson in there about communication and silos.
Finally, numRambs is the whole number of ambiguities, and is found via:
numRambs = floor( (rangeGuess - (rCorrected * BC_over_2f) - (C/2 * (dlZHeightCorr + ulZHeightCorr)))/(rngModulo * BC_over_2f) + 0.5);
Where floor is the floor function, ie remove anything after the decimal, and rangeGuess is your a priori guess at the range. This is where I said earlier you have to know where you are to find out where you are. This guess can be pretty coarse depending on the length of your range code. Sometimes the data contains round trip light time “rtlt” and that can be used for your a priori. We have a fancy astrodynamics and orbit determination tool FreeFlyer that allows me to use a one-liner
rangeGuess = sc.Range(gs)
as the range guess.
To recap: DSN does a beautiful thing, but they are not a public-facing for-profit company. If you want to use the DSN to track your precious spacecraft, you’ll have to play with the toys they give you. You’ll need a half-decent a priori state vector for your spacecraft, a simulator to propagate it and extract geometry, a binary parser, the TRK-2-34 handbook, and all the steps listed above.
This is ridiculous. This is 2016. In general, my #1 complaint with almost everything spacecraft operations related is how needlessly complicated everything is, and how difficult it is to agree on data format, interfacing and access, even units and modeling. From antiquaited Two Line Elements to vector formats, ephemeris formats, tracking data formats, the pace of data modernization in (at least NASA) space operations is stuck in thick mud. There’s a tacit reason for this: if it’s stupid and it works, it’s not stupid.
I meekly disagree. I’m able to do a lot of seamless transitioning between websites, feeds, tweets, embedded youtube videos, and so on online using simple tags. It’s not a 1:1 comparison and a one-shot $200M gamble isn’t on the line, but if NASA ran the internet there would be two humans, an ICD, several teleconferences, a sign off, some testing, and a ORR for attaching a youtube embed and a tweet onto your third party website.
That’s general griping about my own experience trying to pull the proverbial teeth on getting help with data, vectors, and know-how. I’ve been rebuffed on the phone “I’m not funded to help you”.
Specifically to tracking, I have skepticism about ranging codes that repeat and are not unique or at all tied to the physical world. It would seem to me that you could really simplify the process and also remove a lot of interdependencies if you transmitted a ranging code that itself contained uplink data. You could then look at both the data itself for context, and the shape of the data (frequency) for more information.
I’ve wondered what if instead of a pseudonoise range code, you send actual timetags whose smallest resolution is transmitted on the beat of that epoch. That means saying a date into the microphone like “2016 October 10 13:26:20.123” where that final .003 is said exactly at that time. If you calibrate your transmitter to transmit time tags ON those time tags, then you mark when they’re received and you already have the transmit time. You don’t need anything else like an a priori state, transmit time in a separate header, etc.
Furthermore, if you know how long it took you to say the timetag, you can measure how long it took to hear the timetag, and the difference is due to a Doppler shift. Now you have a range and range rate measurement just by listening to the data in the signal and not requiring any interface to the transmitting station.
I’m sure there’s reasons why they don’t do this. The first thing that comes to my mind is bandwidth. Sending simple things takes less bandwidth than complicated things, and during a DSN pass you’re already sending/receiving many other things like downlinked science data, telemetry and command status, uplinking telemetry and command codes, new nav states, etc. Having a simple range code that you manipulate offline reduces the load. However I don’t know the bandwitdh cost of a periodic time stamp code vs a long pseudonoise code. I would like someone to tell me.
The other thing that comes to my mind is the precision and consistency of the transmitter oscillator. I’m not sure this issue would be any more or less impacting to a timetag signal vs a range code signal, which also needs to be consistent. I would like someone would tell me.