jks-prv / Beagle_SDR_GPS

KiwiSDR: BeagleBone web-accessible shortwave receiver and software-defined GPS
http://kiwisdr.com
462 stars 157 forks source link

Add GPS time-stamps to the header for audio packets #130

Closed hcab14 closed 6 years ago

hcab14 commented 6 years ago

Hi @jks-prv

I am sure you are very busy with all kinds of user requests, but it would be really great to have GPS time stamps in the audio streams.

I might be wrong, but to me it looks like all information is already there can can be put into the header for audio streams with moderate effort.

  1. in kiwi.h the gps timestamp would have to be added to the header h in snd_pkt_t

    struct snd_pkt_t {
        struct {
                char id[4];
                u4_t seq;           // waterfall syncs to this sequence number on the client-side                                                         
                char smeter[2];
                // add gps time stamp, format TBD
        } __attribute__((packed)) h;
        union {
        u1_t buf_iq[FASTFIR_OUTBUF_SIZE * 2 * sizeof(u2_t)];
        u1_t buf_real[FASTFIR_OUTBUF_SIZE * sizeof(u2_t)];
    };
    } __attribute__((packed));

    Of course such a change to the header would imply corresponding changes on all client sides.

  2. clk.c: time and ADC ticks for the GPS position solution are already set in clk.c clock_correction(double t_rx, u64_t ticks):

    clk.gps_secs = t_rx; // GPS seconds
    clk.ticks = ticks; // 64-bit ADC tick counter
  3. In rx/rx_sound.cpp the ADC tick counter is available via

                u2_t *tp = rx->ticks[rx->rd_pos];
                u64_t ticks = ((u64_t) tp[2]<<32) | (tp[1]<<16) | tp[0]; // 64-bit ADC tick

    as well as the tick counter + GPS time from the last GPS solution via clk.gps_secs and clk.gps_secs, so this would be the place to fill the gps timestamp into the header.

Unfortunately I do not own a KiwiSDR and therefore cannot make tests myself.

Let me know what you think and how I can help.

jks-prv commented 6 years ago

Yes, this is pretty much what I was planning to do.

What I wanted to understand first was how, on the client side, you would use the timestamp to figure out which sample exactly falls on a fixed time scale. Like every second or millisecond or something (at 12 kHz one sample is ~83 usec). It's a little tricky because at the point this header is generated the audio stream has already been buffered a little between the FPGA and Kiwi. So adding a timestamp here doesn't represent true real-time. Maybe this doesn't matter for most applications as long as the timestamp offset is constant. There will be some jitter considerations because this header is added by C code running on the Beagle.

I also need to add some options which allow the GPS correction of the ADC clock to be disabled per-connection. Some people want to do ionograms and not get the phase jump from the ADC clock being changed. But they still might want a timestamp. And the users on other connections still need the correction for frequency calibration.

hcab14 commented 6 years ago

Thanks a lot for your fast reply!

It's a little tricky because at the point this header is generated the audio stream has already been buffered a little between the FPGA and Kiwi. So adding a timestamp here doesn't represent true real-time. Maybe this doesn't matter for most applications as long as the timestamp offset is constant. There will be some jitter considerations because this header is added by C code running on the Beagle.

As far as I understand the 64 bit counter is coming from the FPGA. If the difference if the tick counter between the last GPS solution and the tick counter for the current audio buffer is used to obtain the GPS time of the current audio buffer

Of course there is a processing delay in the FPGA.

What I wanted to understand first was how, on the client side, you would use the timestamp to figure out which sample exactly falls on a fixed time scale. Like every second or millisecond or something (at 12 kHz one sample is ~83 usec).

I would leave this to the user. Anyway I can imagine that one probably wants to interpolate between samples to increase the time resolution, so having a precise start time for each buffer is in my opinion enough.

hcab14 commented 6 years ago

Hi @jks-prv,

yesterday I got a KiwiSDR and began working on the time stamps, see https://github.com/jks-prv/Beagle_SDR_GPS/pull/134

For testing I feed the 1PPS pulse of a ublox NEO-7M GPS mouse into the antenna input of the KiwiSDR (using a RC network with a 4.7kOhm resistor + some capacitor). This should allow to understand latency and time offsets.

jks-prv commented 6 years ago

Interesting. There is another guy here in New Zealand who was going to try the "feed pulses into the antenna" trick. So let me know how this works out.

Application of this technique for phase locking a receiver: http://www.qsl.net/zl1bpu/SOFT/click.htm

hcab14 commented 6 years ago

Hi @jks-prv,

I managed to do some test with GPS time stamps using a NEO-7M GPS where I fed the 1PPS pulse into the KiwiSDR using a 4.7kOhm resistor.

Then I tuned the KiwiSDR to 30 kHz and recorded IQ samples with GPS timestamps enabled.

The relevant code is here https://github.com/hcab14/Beagle_SDR_GPS and the modified version of the python recorder along with octave code to read the WAV file with 'kiwi' chunks is here https://github.com/hcab14/kiwiclient

This plot shows abs(IQ) w.r.t. mod(gpssec, 1)

gps_0

The raising and falling edge of the 1PPS pulse show up as delta-function-like peaks

Zooming into the peaks I find this: (rising edge)

gps_1

and this: (falling edge)

gps_2

This jitter might of course be due to the GPS module, but does it remind you of something in the FPGA code?

Thanks a lot Christoph

jks-prv commented 6 years ago

A couple of questions/comments: Why are there two peaks in the first graph?

Do you mean 305.5 usec, not 3.055 usec? That's what 5000 cycles at 16.368 MHz is. And it would match the x-axis time shown on your second graph (6 peak intervals shown across 2 msec). 333 usec is 4 samples at 12 kHz (the audio rate) which is interesting.

You might not know but I assume the first peak in the rising edge graph is paired with the first peak of the falling edge graph. That is, the 1PPS has a constant width, but it is showing 21 slightly different rising edge phase points for some reason.

jks-prv commented 6 years ago

Okay, I think I know what might be wrong here. Hint: 21 * 4 (samples) = 84 which is the size of the FPGA audio buffer (kiwi.config constant NRX_SAMPS).

hcab14 commented 6 years ago

Okay, I think I know what might be wrong here. Hint: 21 * 4 (samples) = 84 which is the size of the FPGA audio buffer (kiwi.config constant NRX_SAMPS).

Can you explain?

In verilog/rx/receiver.v data transfer through SPI is triggered by the rx_avail_A wire and at the end of data transfer the ADC tick counter is read out and transferred. rx_avail_A is in verilog/rx/rx.v connected to rx_cic2_strobe_i which signals that the CIC computation has finished.

Does the 333us jitter imply that the downconversion in the FPGA takes different discrete times?

You might not know but I assume the first peak in the rising edge graph is paired with the first peak of the falling edge graph. That is, the 1PPS has a constant width, but it is showing 21 slightly different rising edge phase points for some reason.

Here are the time differences between the peaks seen in IQ data. For this I have computed the weighted average around each peak of the GPS time of the samples. The 1PPS pulse is 0.1 seconds long:

1pps_dt

And here are the GPS times modulo 1 w.r.t. number of the pulses

1pps_mod1
hcab14 commented 6 years ago

I think I understand better (please correct me if I am wrong)

Would the following make sense?

jks-prv commented 6 years ago

I haven't had enough coffee this morning to understand your graphs, but will try later on.

But your second post is absolutely correct. And the second point about latching the 48-bit adc_ticks before inserting it as three 16-bit words into the audio buffer is what I was trying to do all day yesterday without success. Thanks for suggesting rx_avail_A as the latch signal. That worked great. I was trying to generate a separate one from the state machine but it never quite worked.

I have pushed v1.147 that has the new adc_ticks latching. Please try it and see if the jitter improves.

hcab14 commented 6 years ago

Now the jitter became more random:

gps_3

Why do you do this

always @ (posedge cpu_clk)
    if (rx_avail_A)
        ticks_latched_A <= ticks_A;

instead of

always @ (posedge rx_avail_A)
    ticks_latched_A <= ticks_A;

Finally I have to solder RXD,TXD lines to the BBG and check that the GPS module indeed has a good position solution (up to now I rely on the red LED which shows the 1PPS pulse).

Thanks a lot.

jks-prv commented 6 years ago

Did you mean to reverse those two pieces of Verilog wrt the text? Because I'm doing the first one now. Anyway, I don't think the second one is a good idea because you're essentially running the latch clock on a signal that is asynchronous to the adc_clk. Although it's going to be very close. Is the intent to latch one adc_clk earlier? I don't see how that is going to make a difference. ticks_A is incrementing every single adc_clk. Latching it one clock period earlier shouldn't effect the jitter at all.

hcab14 commented 6 years ago

I was asking because it is always @ (posedge cpu_clk) and not always @ (posedge adc_clk). Anyway the jitter is more than one cpu clock. Must check the state of the GPS module.

jks-prv commented 6 years ago

Ugh. How did that happen? Lack of coffee no doubt. Fixing now. Update soon..

jks-prv commented 6 years ago

Okay, pushed v1.148. Please restart to update and give that a try.

hcab14 commented 6 years ago

Okay, pushed v1.148. Please restart to update and give that a try.

Thanks a lot!

I realized today that I overlooked the FIR filter in https://github.com/jks-prv/Beagle_SDR_GPS/blob/master/rx/rx_sound.cpp#L509:

Here is how the 1PPS pulse fed into the KiwiSDR looks now gps_4

As you see it looks much better now.

What remains to be understood / calibrated is the offset/processing delay of 28926.838 usec (~1928450 ADC clocks @66.666467 MHz).

Let me know if you have ideas for further testing / if I can make a pull request.

jks-prv commented 6 years ago

Okay, I see. So it's that weird pipeline delay of the FIR filter (it's one of those fast overlap-save FFT FIRs).

I'll think about the remaining delay problem. Yes, please send me a pull. Good stuff!

hcab14 commented 6 years ago

While waiting for a new version of the KiwiSDR software, I noticed that some KiwiSDRs run already the current master tag which includes the GPS timestamps in the IQ data stream.

One of them is http://hb9ryz.homeip.net:8073/ and the DCF77 signal strength plotted w.r.t. GPS seconds modulo 1 looks like this: dcf77 hb9ryz

The map below is made using https://www.movable-type.co.uk/scripts/latlong.html

dcf77-hb9ryz
hcab14 commented 6 years ago

Here is the DCF77 signal recorded today at http://kiwisdr.ddnss.de:8073 dcf77 dl6ecs The magenta line indicates the expected delay.

What can be seen is the pulse shape of DCF77 (see http://tycho.usno.navy.mil/ptti/2011papers/Paper24.pdf):

dcf77_pulse

So it looks like the GPS time stamps are correct to at least 20 usec (6 km), possibly much better but for this one would have to use the PRN phase modulation: dcf77 dl6ecs_prn

jks-prv commented 6 years ago

In their paper I couldn't find the value of t0 (or equivalently the interval tau sub s). Looks like it must be less than 200 usec though. I understand the part about reducing the transmitter power to zero for 250 usec to get a faster fall-time. Really fantastic that you're getting the propagation delay numbers you expect. Thank you for all your work on this. I'm not sure I ever would have figured this out in such detail.

I think 20 usec is not too bad for an amateur GPS receiver written without any fancy optimizations. There is certainly no "survey mode" in effect here where the receiver position is known and can be fixed in the position/time solution calculations. And also no use of the second order corrections like the ionospheric delay contained in the GPS nav data. Andrew started to write code to consider this but it was never finished. Then there is stuff like antenna feed line delay correction etc. I'm not sure how important these factors are. I have an hp z3801a GPSDO here. I should set that up with a splitter to the same GPS antenna as the Kiwi and do some 1PPS comparisons (work needed to get the Kiwi to generate a 1PPS output of course).

hcab14 commented 6 years ago

In their paper I couldn't find the value of t0 (or equivalently the interval tau sub s). Looks like it must be less than 200 usec though. I understand the part about reducing the transmitter power to zero for 250 usec to get a faster fall-time. Really fantastic that you're getting the propagation delay numbers you expect. Thank you for all your work on this. I'm not sure I ever would have figured this out in such detail.

This is what I am used to do at work...

See here for more information (page 10) https://www.ptb.de/cms/fileadmin/internet/fachabteilungen/abteilung_4/4.4_zeit_und_frequenz/pdf/2011_PTBMitt_50a_DCF77_engl.pdf It seems that the delay by the feedline+antenna is corrected for.

I think 20 usec is not too bad for an amateur GPS receiver written without any fancy optimizations.

I believe that the true accuracy of the KiwiSDR time stamps is much better than 20 µs: (note that the people from DCF77 also say that their measurements in the near field of the antenna are accurate to ± 25 µs) These are the sources of errors I can think of:

There is certainly no "survey mode" in effect here where the receiver position is known and can be fixed in the position/time solution calculations. And also no use of the second order corrections like the ionospheric delay contained in the GPS nav data. Andrew started to write code to consider this but it was never finished.

I realized that the smoothing and outlier removal you apply to the clock correction could be transferred to the GPS position solution, e.g., using a Kalman filter, see https://www.dst.defence.gov.au/sites/default/files/publications/documents/DST-Group-TR-3260.pdf (there must be many more other references)

I have an hp z3801a GPSDO here. I should set that up with a splitter to the same GPS antenna as the Kiwi and do some 1PPS comparisons (work needed to get the Kiwi to generate a 1PPS output of course).

It would be great if you and/or anyone else could check the time alignment. As having a 1PPS output from the KiwiSDR can be nontrivial, in my opinion it would be easiest to feed the 1PPS pulse into the KiwiSDR. For comparison it would be also good to couple the 1PPS pulse into the antenna.

hcab14 commented 6 years ago

This is the position of DCF77 estimated from time differences using three KiwiSDRs dcf77_toa

jks-prv commented 6 years ago

This is what I used to do at work...

Okay, that explains why you're making such great progress so quickly, lol.

As having a 1PPS output from the KiwiSDR can be nontrivial, in my opinion it would be easiest to feed the 1PPS pulse into the KiwiSDR. For comparison it would be also good to couple the 1PPS pulse into the antenna.

Ah, of course. I forgot that's what you had done.

Really interesting that the TDoA for DCF77 worked so well. I hope I can understand your exact method at some point.

G8JNJ commented 6 years ago

Hi Christoph,

This is excellent work.

If you wish you can use my second 'Private' Kiwi for more experiments.

http://southwest.ddns.net:8077/

If it would be of further use for development purposes, it has a spare but currently unused antenna switch extension, so I could modify this to switch a 1PPS marker on when required.

I have a separate GPS with a 1PPS output that I could pipe in from my workshop, but maybe John could indicate if there is a suitable point on the KiWi that I could sniff an output from ?

Regards,

Martin - G8JNJ

hcab14 commented 6 years ago

Hi @G8JNJ,

If it would be of further use for development purposes, it has a spare but currently unused antenna switch extension, so I could modify this to switch a 1PPS marker on when required.

It would be great if you could connect the 1PPS signal to the antenna switch. What kind of GPS receiver would provide the 1PPS signal?

Best regards Christoph

jks-prv commented 6 years ago

Martin, are you asking if the Kiwi produces a 1PPS output? It doesn't currently. But the point is to supply 1PPS from an independent GPS into the antenna input so it can be compared against the Kiwi GPS-derived timestamp to judge its correctness / accuracy. Christoph used a 4.7kOhm resistor to couple the 1PPS to the input (see above).

I would do the same here, but I'm in the middle of a move and everything is packed up.

G8JNJ commented 6 years ago

Hi Christoph & John,

John - thanks for the info that's as I suspected but I just wanted to ask first.

Christoph - It's a Trimble Thunderbolt.

It's not next to the KiWi as it's in my workshop about 20m away. But I can pipe the 1PPS signal to the KiWi and connect a switch if required. It will take me a couple of days to sort out the hardware.

I have use the Trimble previously for some other experiments with Peter G3PLX so I know that it can provide a suitable signal.

I mentioned this work to Peter and he was surprised by the accuracy of your results.

He commented that it's difficult to get figures so accurate for DCF77 using only the falling edge. This apparently is problematic as it doesn't make allowance for the time-constant of the antenna resonance. The radiated RF ourput of the antenna takes quite a long time to decay after the transmitter is turned off because the antenna Q factor is so high. The p-n code on DCF77 is a much better bet.

If it is desired to be able to locate pirates and jammers, it requires some method of time-correlating arbitrary modulation waveforms as voice is too periodic and multipath doesn't help.

Regards,

Martin - G8JNJ

G8JNJ commented 6 years ago

Hi All,

Actually I just remembered that I have a U-Blox module with a PPS output that I may be able to use instead. This would be better as I could connect it much closer to the KiWi.

I'll see if I can find it sometime tomorrow and connect it in.

Do you want it to be connected whilst the antenna is connected or just as a separate clean PPS source (or both).

Regards,

Martin - G8JNJ

ka7u commented 6 years ago

Is this thing suitable for what is being experimented with? https://www.ebay.com/itm/Trimble-Resolution-T-12-channel-GPS-Timing-Receiver-Module-3-3V-1pps-Ver-1-17-0/302531276883?hash=item467044f053:g:G6AAAOSwaeRZJGCY Ron - KA7U

G8JNJ commented 6 years ago

Hi Christoph,

I have quickly added a 1PPS marker onto my Private KiWi

http://southwest.ddns.net:8077

You can switch between Antenna, 1PPS marker and Antenna & Marker by using the antenna switch extension.

If you wish to change the antenna in use. Then login to my public KiWi and change the antenna using the antenna switch extension. Use the Trask active whip for the LF bands MSF & DCF77.

http://southwest.ddns.net:8073/

The 1PPS pulse is being taken from a U-Blox M5 with built in antenna, so I've coupled the feed from my outdoor GPS antenna into it with a short antenna taped to the side of the module.

I'm not sure if the pulse sync is 100% correct but try it and let me know and try and I'll adjust it as required.

If you are taking recordings or don't want the setup on the Private KiWi to be disturbed just leave a note in the Callsign entry box to warn other users.

Regards,

Martin - G8JNJ

jks-prv commented 6 years ago

@ka7u

Is this thing suitable for what is being experimented with?

Definitely.