Discussion and technical support related to USRP, UHD, RFNoC
View all threadsOn 12/09/2019 02:38 PM, Lukas Haase wrote:
Precicely.
What frequencies are involved here?
Example: Transmit 900 Mhz (USRP Sink).
Receive 1800 MHz (USRP Source).
The received signal will have arbitrary phase phi1.
To make sure we're on the same page, you're measuring the phase offset
between the two RX channels, correct?
Hi Marcus,
Von: "Marcus D. Leech" patchvonbraun@gmail.com
On 12/09/2019 02:38 PM, Lukas Haase wrote:
Precicely.
What frequencies are involved here?
Example: Transmit 900 Mhz (USRP Sink).
Receive 1800 MHz (USRP Source).
The received signal will have arbitrary phase phi1.
To make sure we're on the same page, you're measuring the phase offset
between the two RX channels, correct?
No, I only have one RX channel at the moment.
--> One TX @ f and one RX @ 2f.
The phase relation between this TX+RX should stay constant/coherent once both TX+RX tune to a different f and back.
Let me know if the setup is clear, otherwise I'll try to draw a block diagram/equations or I can also send the GRC screenshots.
Thanks,
Luke
On 12/09/2019 03:11 PM, Lukas Haase wrote:
No, I only have one RX channel at the moment.
--> One TX @ f and one RX @ 2f.
The phase relation between this TX+RX should stay constant/coherent once both TX+RX tune to a different f and back.
Let me know if the setup is clear, otherwise I'll try to draw a block diagram/equations or I can also send the GRC screenshots.
Thanks,
Luke
You code shows two RX channels:
now = self.uhd_usrp_sink_0.get_time_now()
self.uhd_usrp_sink_0.set_command_time(now + uhd.time_spec(1))
self.uhd_usrp_source_0.set_command_time(now + uhd.time_spec(1))
self.uhd_usrp_source_0.set_center_freq(2*self.fcenter, 0)
self.uhd_usrp_source_0.set_center_freq(2*self.fcenter, 1)
self.uhd_usrp_sink_0.set_center_freq(self.fcenter, 0)
self.uhd_usrp_source_0.clear_command_time()
self.uhd_usrp_sink_0.clear_command_time()
So, you're measuring the phase-offset between the TX side and the RX
side at the 2nd harmonic, and expecting that phase relationship to be
the same across re-tunes? I'm not sure that's possible.
Hi Marcus,
Von: "Marcus D. Leech" patchvonbraun@gmail.com
On 12/09/2019 03:11 PM, Lukas Haase wrote:
No, I only have one RX channel at the moment.
--> One TX @ f and one RX @ 2f.
The phase relation between this TX+RX should stay constant/coherent once both TX+RX tune to a different f and back.
Let me know if the setup is clear, otherwise I'll try to draw a block diagram/equations or I can also send the GRC screenshots.
Thanks,
Luke
You code shows two RX channels:
now = self.uhd_usrp_sink_0.get_time_now()
self.uhd_usrp_sink_0.set_command_time(now + uhd.time_spec(1))
self.uhd_usrp_source_0.set_command_time(now + uhd.time_spec(1))
self.uhd_usrp_source_0.set_center_freq(2*self.fcenter, 0)
self.uhd_usrp_source_0.set_center_freq(2*self.fcenter, 1)
self.uhd_usrp_sink_0.set_center_freq(self.fcenter, 0)
self.uhd_usrp_source_0.clear_command_time()
self.uhd_usrp_sink_0.clear_command_time()
Sorry for the confusion.
You are right, there are 2 RX channels but I only use one of them.
So, you're measuring the phase-offset between the TX side and the RX
side at the 2nd harmonic, and expecting that phase relationship to be
the same across re-tunes?
Yes, this is exactly what I want.
I'm not sure that's possible.
Why not?
Conceptually it must be possible: The phase offset is only defined by the relative phase between RX/TX-LO.
Let's assume that both RX + TX mixer are driven by the same LO but the RX side has an additional frequency doubler.
Then the phase relationship is ALWAYS constant. By construction.
The USRP just makes things complicated because RX and TX are driven by different PLLs and allow their LO to be retuned separately. But ultimately both PLLs are driven by the same reference (to which phase they lock) so there must be a way to have a constant phase relationship.
Thanks,
Luke
On 12/09/2019 03:35 PM, Lukas Haase wrote:
Hi Marcus,
Von: "Marcus D. Leech" patchvonbraun@gmail.com
On 12/09/2019 03:11 PM, Lukas Haase wrote:
No, I only have one RX channel at the moment.
--> One TX @ f and one RX @ 2f.
The phase relation between this TX+RX should stay constant/coherent once both TX+RX tune to a different f and back.
Let me know if the setup is clear, otherwise I'll try to draw a block diagram/equations or I can also send the GRC screenshots.
Thanks,
Luke
You code shows two RX channels:
now = self.uhd_usrp_sink_0.get_time_now()
self.uhd_usrp_sink_0.set_command_time(now + uhd.time_spec(1))
self.uhd_usrp_source_0.set_command_time(now + uhd.time_spec(1))
self.uhd_usrp_source_0.set_center_freq(2*self.fcenter, 0)
self.uhd_usrp_source_0.set_center_freq(2*self.fcenter, 1)
self.uhd_usrp_sink_0.set_center_freq(self.fcenter, 0)
self.uhd_usrp_source_0.clear_command_time()
self.uhd_usrp_sink_0.clear_command_time()
Sorry for the confusion.
You are right, there are 2 RX channels but I only use one of them.
So, you're measuring the phase-offset between the TX side and the RX
side at the 2nd harmonic, and expecting that phase relationship to be
the same across re-tunes?
Yes, this is exactly what I want.
I'm not sure that's possible.
Why not?
Conceptually it must be possible: The phase offset is only defined by the relative phase between RX/TX-LO.
Let's assume that both RX + TX mixer are driven by the same LO but the RX side has an additional frequency doubler.
Then the phase relationship is ALWAYS constant. By construction.
But, that's not the situation we find ourselves in with the hardware
(including FPGA) in front of us.
The USRP just makes things complicated because RX and TX are driven by different PLLs and allow their LO to be retuned separately. But ultimately both PLLs are driven by the same reference (to which phase they lock) so there must be a way to have a constant phase relationship.
Did you look at the reference I posted about Fractional-N vs Integer-N
synthesis? They behave very differently in this regard--the "phase reset"
feature helps, but in this case, the UBX was never designed to
maintain constant phase offsets between RX/TX (because this is a very very
unusual case), PARTICULARLY ACROSS RETUNES.
Quite apart from what the PLL synthesizers are doing, there's the
DDC/DUC within the FPGA, and they are driven by what amounts to a
digital oscillator, and THOSE digital oscillators aren't shared,
either. Sharing phase constancy across TX/RX was never a design goal
of the hardware.
Now, having said all that, it may be the case that there are specific
configurations in which this can be made to work, and I'm in discussions
with R&D about that. Details like what the management policy is for
the phase-accumulators in the DDC/DUC digital oscillators matters,
along with hardware details like whether the RX and TX synthesizers
shared a control bus or whether it's in parallel really matter, for example.
Thanks,
Luke
Something that MAY help here is to use integer_n tuning:
treq=uhd.tune_request(my_frequency)
treq.args=uhd.device_addr("mode_n=integer")
...
...set_center_freq(treq, 0)
This will force the PLL to use INTEGER_N tuning, which has more
predictable phase behavior with respect to the reference.
Hi Marcus,
Gesendet: Montag, 09. Dezember 2019 um 15:58 Uhr
On 12/09/2019 03:35 PM, Lukas Haase wrote:
Hi Marcus,
Von: "Marcus D. Leech" patchvonbraun@gmail.com
On 12/09/2019 03:11 PM, Lukas Haase wrote:
No, I only have one RX channel at the moment.
--> One TX @ f and one RX @ 2f.
The phase relation between this TX+RX should stay constant/coherent once both TX+RX tune to a different f and back.
Let me know if the setup is clear, otherwise I'll try to draw a block diagram/equations or I can also send the GRC screenshots.
Thanks,
Luke
You code shows two RX channels:
now = self.uhd_usrp_sink_0.get_time_now()
self.uhd_usrp_sink_0.set_command_time(now + uhd.time_spec(1))
self.uhd_usrp_source_0.set_command_time(now + uhd.time_spec(1))
self.uhd_usrp_source_0.set_center_freq(2*self.fcenter, 0)
self.uhd_usrp_source_0.set_center_freq(2*self.fcenter, 1)
self.uhd_usrp_sink_0.set_center_freq(self.fcenter, 0)
self.uhd_usrp_source_0.clear_command_time()
self.uhd_usrp_sink_0.clear_command_time()
Sorry for the confusion.
You are right, there are 2 RX channels but I only use one of them.
So, you're measuring the phase-offset between the TX side and the RX
side at the 2nd harmonic, and expecting that phase relationship to be
the same across re-tunes?
Yes, this is exactly what I want.
I'm not sure that's possible.
Why not?
Conceptually it must be possible: The phase offset is only defined by the relative phase between RX/TX-LO.
Let's assume that both RX + TX mixer are driven by the same LO but the RX side has an additional frequency doubler.
Then the phase relationship is ALWAYS constant. By construction.
But, that's not the situation we find ourselves in with the hardware
(including FPGA) in front of us.
The USRP just makes things complicated because RX and TX are driven by different PLLs and allow their LO to be retuned separately. But ultimately both PLLs are driven by the same reference (to which phase they lock) so there must be a way to have a constant phase relationship.
Did you look at the reference I posted about Fractional-N vs Integer-N
synthesis? They behave very differently in this regard--the "phase reset"
feature helps, but in this case, the UBX was never designed to
maintain constant phase offsets between RX/TX (because this is a very very
unusual case), PARTICULARLY ACROSS RETUNES.
Yes, I reviewed it. Thanks for the reference, I did not know this about fractional-N PLLs.
Do you know by any chance if the phase reset feature is implemented by the USRP or not (initially you suggest it has, above you suggest it may not).
Quite apart from what the PLL synthesizers are doing, there's the
DDC/DUC within the FPGA, and they are driven by what amounts to a
digital oscillator, and THOSE digital oscillators aren't shared,
either. Sharing phase constancy across TX/RX was never a design goal
of the hardware.
That makes sense too. I am still wondering why phase constancy betwene TX/RX would be such a "weird" goal.
Yes, in many cases this phase rotation is readily divided out but in particular for fast frequency hopping systems it becomes hard. Or for phase based ranging systems. I mean, they exist, right?
I see that the DDC/DUC are not shared. At least in principle it should be easy to synchronize them ... the beauty of a digital system (their digital clocks are shared).
While I have worked with such systems in the past I am unfortunately new to gnuradio and USRP. Gnuradio is a steep learning curve on its own, these small details add significantly to my confusion.
Now, having said all that, it may be the case that there are specific
configurations in which this can be made to work, and I'm in discussions
with R&D about that. Details like what the management policy is for
the phase-accumulators in the DDC/DUC digital oscillators matters,
along with hardware details like whether the RX and TX synthesizers
shared a control bus or whether it's in parallel really matter, for example.
Any possible help is greatly appreciated!
Something that MAY help here is to use integer_n tuning:
treq=uhd.tune_request(my_frequency)
treq.args=uhd.device_addr("mode_n=integer")
...
...set_center_freq(treq, 0)
I translated this to gnuradio:
def set_fcenter(self, fcenter):
self.fcenter = fcenter
tune_resp_tx = self.uhd_usrp_sink_0.set_center_freq(fcenter, 0)
tune_resp_rx = self.uhd_usrp_source_0.set_center_freq(2*fcenter, 0)
tune_req_tx = uhd.tune_request(rf_freq=fcenter, rf_freq_policy=uhd.tune_request.POLICY_MANUAL,
dsp_freq=tune_resp_tx.actual_dsp_freq, dsp_freq_policy=uhd.tune_request.POLICY_MANUAL)
tune_req_rx = uhd.tune_request(rf_freq=2*fcenter, rf_freq_policy=uhd.tune_request.POLICY_MANUAL,
dsp_freq=tune_resp_rx.actual_dsp_freq, dsp_freq_policy=uhd.tune_request.POLICY_MANUAL)
tune_req_tx.args = uhd.device_addr("mode_n=integer")
tune_req_rx.args = uhd.device_addr("mode_n=integer")
now = self.uhd_usrp_sink_0.get_time_now()
self.uhd_usrp_sink_0.set_command_time(now + uhd.time_spec(1))
self.uhd_usrp_source_0.set_command_time(now + uhd.time_spec(1))
self.uhd_usrp_sink_0.set_center_freq(tune_req_tx, 0)
self.uhd_usrp_source_0.set_center_freq(tune_req_rx, 0)
self.uhd_usrp_source_0.set_center_freq(tune_req_rx, 1)
self.uhd_usrp_source_0.clear_command_time()
self.uhd_usrp_sink_0.clear_command_time()
Unfortunately with these lines I loose the frequency altogether when retuning (without changing tune_req_tx.args/tune_req_rx.args things work): What I mean, I receive noise only. I do not physically have access to my testbench at the moment to verify where the frequency is tuned to, I just know that instead of receiving my signal I only receive noise.
Thanks,
Luke
On 12/09/2019 11:03 PM, Lukas Haase wrote:
Hi Marcus,
Gesendet: Montag, 09. Dezember 2019 um 15:58 Uhr
On 12/09/2019 03:35 PM, Lukas Haase wrote:
Hi Marcus,
Von: "Marcus D. Leech" patchvonbraun@gmail.com
On 12/09/2019 03:11 PM, Lukas Haase wrote:
No, I only have one RX channel at the moment.
--> One TX @ f and one RX @ 2f.
The phase relation between this TX+RX should stay constant/coherent once both TX+RX tune to a different f and back.
Let me know if the setup is clear, otherwise I'll try to draw a block diagram/equations or I can also send the GRC screenshots.
Thanks,
Luke
You code shows two RX channels:
now = self.uhd_usrp_sink_0.get_time_now()
self.uhd_usrp_sink_0.set_command_time(now + uhd.time_spec(1))
self.uhd_usrp_source_0.set_command_time(now + uhd.time_spec(1))
self.uhd_usrp_source_0.set_center_freq(2*self.fcenter, 0)
self.uhd_usrp_source_0.set_center_freq(2*self.fcenter, 1)
self.uhd_usrp_sink_0.set_center_freq(self.fcenter, 0)
self.uhd_usrp_source_0.clear_command_time()
self.uhd_usrp_sink_0.clear_command_time()
Sorry for the confusion.
You are right, there are 2 RX channels but I only use one of them.
So, you're measuring the phase-offset between the TX side and the RX
side at the 2nd harmonic, and expecting that phase relationship to be
the same across re-tunes?
Yes, this is exactly what I want.
I'm not sure that's possible.
Why not?
Conceptually it must be possible: The phase offset is only defined by the relative phase between RX/TX-LO.
Let's assume that both RX + TX mixer are driven by the same LO but the RX side has an additional frequency doubler.
Then the phase relationship is ALWAYS constant. By construction.
But, that's not the situation we find ourselves in with the hardware
(including FPGA) in front of us.
The USRP just makes things complicated because RX and TX are driven by different PLLs and allow their LO to be retuned separately. But ultimately both PLLs are driven by the same reference (to which phase they lock) so there must be a way to have a constant phase relationship.
Did you look at the reference I posted about Fractional-N vs Integer-N
synthesis? They behave very differently in this regard--the "phase reset"
feature helps, but in this case, the UBX was never designed to
maintain constant phase offsets between RX/TX (because this is a very very
unusual case), PARTICULARLY ACROSS RETUNES.
Yes, I reviewed it. Thanks for the reference, I did not know this about fractional-N PLLs.
Do you know by any chance if the phase reset feature is implemented by the USRP or not (initially you suggest it has, above you suggest it may not).
Quite apart from what the PLL synthesizers are doing, there's the
DDC/DUC within the FPGA, and they are driven by what amounts to a
digital oscillator, and THOSE digital oscillators aren't shared,
either. Sharing phase constancy across TX/RX was never a design goal
of the hardware.
That makes sense too. I am still wondering why phase constancy betwene TX/RX would be such a "weird" goal.
Yes, in many cases this phase rotation is readily divided out but in particular for fast frequency hopping systems it becomes hard. Or for phase based ranging systems. I mean, they exist, right?
I see that the DDC/DUC are not shared. At least in principle it should be easy to synchronize them ... the beauty of a digital system (their digital clocks are shared).
While I have worked with such systems in the past I am unfortunately new to gnuradio and USRP. Gnuradio is a steep learning curve on its own, these small details add significantly to my confusion.
Now, having said all that, it may be the case that there are specific
configurations in which this can be made to work, and I'm in discussions
with R&D about that. Details like what the management policy is for
the phase-accumulators in the DDC/DUC digital oscillators matters,
along with hardware details like whether the RX and TX synthesizers
shared a control bus or whether it's in parallel really matter, for example.
Any possible help is greatly appreciated!
Something that MAY help here is to use integer_n tuning:
treq=uhd.tune_request(my_frequency)
treq.args=uhd.device_addr("mode_n=integer")
...
...set_center_freq(treq, 0)
I translated this to gnuradio:
def set_fcenter(self, fcenter):
self.fcenter = fcenter
tune_resp_tx = self.uhd_usrp_sink_0.set_center_freq(fcenter, 0)
tune_resp_rx = self.uhd_usrp_source_0.set_center_freq(2*fcenter, 0)
tune_req_tx = uhd.tune_request(rf_freq=fcenter, rf_freq_policy=uhd.tune_request.POLICY_MANUAL,
dsp_freq=tune_resp_tx.actual_dsp_freq, dsp_freq_policy=uhd.tune_request.POLICY_MANUAL)
tune_req_rx = uhd.tune_request(rf_freq=2*fcenter, rf_freq_policy=uhd.tune_request.POLICY_MANUAL,
dsp_freq=tune_resp_rx.actual_dsp_freq, dsp_freq_policy=uhd.tune_request.POLICY_MANUAL)
tune_req_tx.args = uhd.device_addr("mode_n=integer")
tune_req_rx.args = uhd.device_addr("mode_n=integer")
now = self.uhd_usrp_sink_0.get_time_now()
self.uhd_usrp_sink_0.set_command_time(now + uhd.time_spec(1))
self.uhd_usrp_source_0.set_command_time(now + uhd.time_spec(1))
self.uhd_usrp_sink_0.set_center_freq(tune_req_tx, 0)
self.uhd_usrp_source_0.set_center_freq(tune_req_rx, 0)
self.uhd_usrp_source_0.set_center_freq(tune_req_rx, 1)
self.uhd_usrp_source_0.clear_command_time()
self.uhd_usrp_sink_0.clear_command_time()
Unfortunately with these lines I loose the frequency altogether when retuning (without changing tune_req_tx.args/tune_req_rx.args things work): What I mean, I receive noise only. I do not physically have access to my testbench at the moment to verify where the frequency is tuned to, I just know that instead of receiving my signal I only receive noise.
Thanks,
Luke
You're using the MANUAL policy for BOTH DSP and RF. Let the automatic
"stuff" do its thing, with the only difference being integer-N tuning.
Hi Marcus,
Von: "Marcus D. Leech" patchvonbraun@gmail.com
[...]
You're using the MANUAL policy for BOTH DSP and RF. Let the automatic
"stuff" do its thing, with the only difference being integer-N tuning.
Wow, this is all so black magic.
After a long time I figured out that I also have to supply int_n_step (I use int_n_step=100e3), as I randomly stumbled across in http://www.radio-science.net/2017/12/adventures-in-usrp-tuning.html.
The documentation does not even mention the existence of this parameter.
tune_req_tx = uhd.tune_request(target_freq=fcenter)
tune_req_rx = uhd.tune_request(target_freq=2*fcenter)
tune_req_rx.args=uhd.device_addr(','.join(["mode_n=integer", "int_n_step=100e3",]))
tune_req_tx.args=uhd.device_addr(','.join(["mode_n=integer", "int_n_step=100e3",]))
now = self.uhd_usrp_sink_0.get_time_now()
self.uhd_usrp_sink_0.set_command_time( now + uhd.time_spec(1))
self.uhd_usrp_source_0.set_command_time(now + uhd.time_spec(1))
self.uhd_usrp_sink_0.set_center_freq( tune_req_tx, 0)
self.uhd_usrp_source_0.set_center_freq(tune_req_rx, 0)
self.uhd_usrp_source_0.set_center_freq(tune_req_rx, 1)
self.uhd_usrp_source_0.clear_command_time()
self.uhd_usrp_sink_0.clear_command_time()
So now my RX/TX tune again (i.e., I get a signal other than noise). Unfortunately I run into the same issue that I had already some time at the beginning: My signal is frequency offset (*) - by an odd 2.5 kHz.
I set my clock explicitely to 200 MHz. A step size of 100kHz should be able to synthesize my even numbers (900 MHz) without issues. On the other hand, the frequency offset stays at 2.5kHz for different values of int_n_step...
Thanks,
Luke
() What I mean by that: I send a signal at "f+fif" and downconvert "2(f+fif)". In gnuradio I multiply with exp(i2pifift). I expect my signal to be a DC signal (which it is in absence of the mode_n=integer). With frequency shift I mean that I see a frequency of 2.5 kHz instead of 0Hz.
Hi Marcus,
Von: "Marcus D. Leech" patchvonbraun@gmail.com
[...]
You're using the MANUAL policy for BOTH DSP and RF. Let the automatic
"stuff" do its thing, with the only difference being integer-N tuning.
Pretty incredible, I think I found the(?) issue.
https://kb.ettus.com/UBX#Phase_Synchronization:
"If you are operating the UBX at frequencies below 1 GHz and need phase synchronization, then it is necessary to select a 20 MHz daughterboard clock rate, instead of using the default 50 MHz rate [...] If you're using GNU Radio, then you can add "dboard_clock_rate=20e6" to the "Device Arguments" field of the properties for the UHD Sink and UHD Source blocks."
I did this and lo and behold, the phase stays constant across tunes!
It even works without mode_n=integer most of the time ... I think sometimes I get the frequency shift I was mentioning in a previous message.
With mode_n=integer it works for all frequencies I have tried. I really hope I did not miss anything.
Code:
self.uhd_usrp_source_0 = uhd.usrp_source(
",".join(("", "dboard_clock_rate=20e6")),
uhd.stream_args(
cpu_format="fc32",
channels=range(2),
),
)
self.uhd_usrp_source_0.set_clock_rate(200e6, uhd.ALL_MBOARDS)
self.uhd_usrp_sink_0 = uhd.usrp_sink(
",".join(("", "dboard_clock_rate=20e6")),
uhd.stream_args(
cpu_format="fc32",
channels=range(1),
),
)
self.uhd_usrp_sink_0.set_clock_rate(200e6, uhd.ALL_MBOARDS)
...
def set_fcenter(self, fcenter):
self.fcenter = fcenter
tune_req_tx = uhd.tune_request(target_freq=fcenter, lo_offset=1e6)
tune_req_rx = uhd.tune_request(target_freq=2*fcenter, lo_offset=1e6)
tune_req_rx.args=uhd.device_addr(','.join(["mode_n=integer", "int_n_step=1000e3",]))
tune_req_tx.args=uhd.device_addr(','.join(["mode_n=integer", "int_n_step=1000e3",]))
now = self.uhd_usrp_sink_0.get_time_now()
self.uhd_usrp_sink_0.set_command_time(now + uhd.time_spec(1))
self.uhd_usrp_source_0.set_command_time(now + uhd.time_spec(1))
res1 = self.uhd_usrp_sink_0.set_center_freq(tune_req_tx, 0)
res2 = self.uhd_usrp_source_0.set_center_freq(tune_req_rx, 0)
res3 = self.uhd_usrp_source_0.set_center_freq(tune_req_rx, 1)
self.uhd_usrp_source_0.clear_command_time()
self.uhd_usrp_sink_0.clear_command_time()
Luke