-
Notifications
You must be signed in to change notification settings - Fork 3
/
Rx.jl
417 lines (382 loc) · 16.6 KB
/
Rx.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
function initRxUHD(pointerUSRP)
# ----------------------------------------------------
# --- Rx Streamer
# ----------------------------------------------------
# --- Create a pointer related to the Rx streamer
addressStream = Ref{uhd_rx_streamer_handle}();
# --- Cal the init
@assert_uhd uhd_rx_streamer_make(addressStream)
streamerPointer = addressStream[];
# ----------------------------------------------------
# --- Rx Metadata
# ----------------------------------------------------
# --- Create a pointer related to Metadata
addressMD = Ref{uhd_rx_metadata_handle}();
# --- Cal the init
@assert_uhd uhd_rx_metadata_make(addressMD)
# --- Get the usable object
metadataPointer = addressMD[];
# --- Pointer for counting number of samples
pointerSamples = Ref{Csize_t}(0);
# ----------------------------------------------------
# --- Create the USRP wrapper object
# ----------------------------------------------------
uhd = UHDRxWrapper(true,pointerUSRP,streamerPointer,metadataPointer,addressStream,addressMD,pointerSamples);
return uhd;
end
function openUHDRx(pointerUSRP,carrierFreq,samplingRate,gain;channels=[0],antennas=["RX2"],args="",nbAntennaRx=1,cpu_format="fc32",otw_format="sc16",subdev="",bypassStreamer=false);
# ----------------------------------------------------
# --- Parameter checks
# ----------------------------------------------------
# --- MIMO mode shortcut
(nbAntennaRx == 2) ? MIMO_MODE = true : MIMO_MODE = false
@assert (nbAntennaRx) ≤ length(antennas) "Mismatch in antenna configuration. Number of Rx antennas should match the string vector of antenna"
@assert (nbAntennaRx) ≤ length(channels) "Number of Rx antennas should be lower than number of channels"
# ----------------------------------------------------
# --- Core structure definitions
# ----------------------------------------------------
# --- Init Rx stage
uhd = initRxUHD(pointerUSRP);
# --- Create the structure to be updated
rx = UHDRx(uhd,carrierFreq,samplingRate,gain,antennas,channels,0,0,nbAntennaRx,false);
# ----------------------------------------------------
# --- Radio configuration
# ----------------------------------------------------
# --- RF Configuration
for (c,currChan) in enumerate(channels[1:nbAntennaRx])
# Set the carrier frequencies
updateCarrierFreq!(rx,carrierFreq,currChan)
# Set the sampling rate
updateSamplingRate!(rx,samplingRate,currChan)
# Set the gains
updateGain!(rx,gain,currChan)
# Set the antennas
uhd_usrp_set_rx_antenna(uhd.pointerUSRP,antennas[c],currChan)
end
# --- Custom subdev use
if !isempty(subdev)
# --- Custom Subdev use
pointerSubDev = Ref{uhd_subdev_spec_handle}()
@assert_uhd uhd_subdev_spec_make(pointerSubDev,subdev)
@assert_uhd uhd_usrp_set_rx_subdev_spec(rx.uhd.pointerUSRP,pointerSubDev[],0)
uhd_subdev_spec_free(pointerSubDev)
else
# Default configuration for subdev. It should leads to issues with multiple antenna (at least in e310)
if nbAntennaRx > 1
@warn "Multiple channels (antennas) with default subdev config may leads to recv issue. Please use subdev=\"A:0 A:1\" to configure multiple subdev"
end
end
# --- Internal streamer and buffer config
if MIMO_MODE
# --- Clock source
@assert_uhd LibUHD.uhd_usrp_set_clock_source(rx.uhd.pointerUSRP,"internal",0)
@assert_uhd LibUHD.uhd_usrp_set_time_now(rx.uhd.pointerUSRP,0,0,0)
end
# ----------------------------------------------------
# --- Set up streamer
# ----------------------------------------------------
# Streamer policy depends on MIMO mode or not
# It can be a pain to have one ssytem for every possible configurations, so one can want to define streamer policy later
if nbAntennaRx > 0 && bypassStreamer == false
if MIMO_MODE
uhd_usrp_create_stream(rx;
nbAntennaRx,
channels,
cpu_format,
otw_format,
args,
stream_mode = UHD_STREAM_MODE_NUM_SAMPS_AND_DONE,
stream_now = false,
full_sec_delay = 1,
frac_sec_delay = 0.5,
num_samps = 10_000_000
)
else
uhd_usrp_create_stream(rx;
nbAntennaRx,
channels,
cpu_format,
otw_format,
args,
stream_mode = UHD_STREAM_MODE_START_CONTINUOUS,
stream_now = true,
full_sec_delay = 0,
frac_sec_delay = 0,
num_samps = rx.packetSize
)
end
else
(nbAntennaRx > 0 ) && @warn "Streamer has not be set up as bypassStreamer has been set to true. Rx will not work without create a dedicated streamer and set up the streamer policy. See uhd_usrp_create_stream to create a streamer with custom parameters"
end
return rx
end
"""
Create and launch a streamer with dedicated parameters
"""
function uhd_usrp_create_stream(rx::UHDRx;nbAntennaRx = 1, channels=[0],cpu_format="fc32",otw_format="sc16",args="",stream_mode=UHD_STREAM_MODE_START_CONTINUOUS,stream_now=true,full_sec_delay=0,frac_sec_delay=0,num_samps=-1,check_errors=stream_mode in [UHD_STREAM_MODE_NUM_SAMPS_AND_DONE, UHD_STREAM_MODE_NUM_SAMPS_AND_MORE])
# --- Streamer arguments
a1 = Base.unsafe_convert(Cstring,cpu_format);
a2 = Base.unsafe_convert(Cstring,otw_format);
a3 = Base.unsafe_convert(Cstring,args);
channel = pointer(channels)
uhdArgs_0 = uhd_stream_args_t(a1,a2,a3,channel,nbAntennaRx);
pointerArgs = Ref{uhd_stream_args_t}(uhdArgs_0);
pointerSamples = Ref{Csize_t}(0);
# --- Get internal buffer size
@assert_uhd uhd_usrp_get_rx_stream(rx.uhd.pointerUSRP,pointerArgs,rx.uhd.pointerStreamer)
@assert_uhd uhd_rx_streamer_max_num_samps(rx.uhd.pointerStreamer,pointerSamples)
rx.packetSize= pointerSamples[]
# --- Launch the stream
restartStreamer(rx;stream_mode,num_samps,stream_now,full_sec_delay,frac_sec_delay,check_errors)
end
"""
Restart the USRP streamer. In some cases (especially macOS) we have an issue with streamer congestion and we need to restart it.
By now, we have added the restart function in recv! method.
"""
@inline function restartStreamer(rx::UHDRx;stream_mode=UHD_STREAM_MODE_START_CONTINUOUS,num_samps=-1,stream_now=true,full_sec_delay=0,frac_sec_delay=0,check_errors=stream_mode in [UHD_STREAM_MODE_NUM_SAMPS_AND_DONE, UHD_STREAM_MODE_NUM_SAMPS_AND_MORE])
# ----------------------------------------------------
# --- Start the streamer
# ----------------------------------------------------
rx.checkErrors = check_errors
(num_samps == -1) && (num_samps = rx.packetSize)
streamCmd = UHDBindings.uhd_stream_cmd_t(stream_mode,num_samps,stream_now,full_sec_delay,frac_sec_delay);
pointerCmd = Ref{uhd_stream_cmd_t}(streamCmd);
uhd_rx_streamer_issue_stream_cmd(rx.uhd.pointerStreamer,pointerCmd)
end
function updateSamplingRate!(radio::UHDRx,samplingRate,chan=0)
# ----------------------------------------------------
# --- Sampling rate configuration
# ----------------------------------------------------
# @inforx "Try to change rate from $(radio.samplingRate/1e6) MHz to $(samplingRate/1e6) MHz";
# --- Update the Rx sampling rate
uhd_usrp_set_rx_rate(radio.uhd.pointerUSRP,samplingRate,chan)
# --- Get the Rx rate from the radio
pointerRate = Ref{Cdouble}(0);
uhd_usrp_get_rx_rate(radio.uhd.pointerUSRP,chan,pointerRate)
updateRate = pointerRate[];
# --- Print a flag
if updateRate != samplingRate
@warnrx "Effective Rate is $(updateRate/1e6) MHz and not $(samplingRate/1e6) MHz\n"
else
# @inforx "Effective Rate is $(updateRate/1e6) MHz\n";
end
radio.samplingRate = updateRate;
end
function updateGain!(radio::UHDRx,gain,chan=0)
# ----------------------------------------------------
# --- Sampling rate configuration
# ----------------------------------------------------
# @inforx "Try to change gain from $(radio.gain) dB to $(gain) dB";
# Update the UHD sampling rate
uhd_usrp_set_rx_gain(radio.uhd.pointerUSRP,gain,chan,"")
# Get the updated gain from UHD
pointerGain = Ref{Cdouble}(0);
uhd_usrp_get_rx_gain(radio.uhd.pointerUSRP,chan,"",pointerGain)
updateGain = pointerGain[];
# --- Print a flag
if updateGain != gain
@warnrx "Effective gain is $(updateGain) dB and not $(gain) dB\n"
else
# @inforx "Effective gain is $(updateGain) dB\n";
end
radio.gain = updateGain;
return updateGain;
end
function updateCarrierFreq!(radio::UHDRx,carrierFreq,chan=0)
# ----------------------------------------------------
# --- Carrier Frequency configuration
# ----------------------------------------------------
# @inforx "Try to change carrier frequency from $(radio.carrierFreq/1e6) MHz to $(carrierFreq/1e6) MHz";
a5 = Base.unsafe_convert(Cstring,"");
tuneRequest = uhd_tune_request_t(carrierFreq,UHD_TUNE_REQUEST_POLICY_AUTO,carrierFreq,UHD_TUNE_REQUEST_POLICY_AUTO,200e6,a5)
# tuneRequest = uhd_tune_request_t(carrierFreq,UHD_TUNE_REQUEST_POLICY_AUTO,UHD_TUNE_REQUEST_POLICY_AUTO);
tunePointer = Ref{uhd_tune_request_t}(tuneRequest);
pointerTuneResult = Ref{uhd_tune_result_t}();
uhd_usrp_set_rx_freq(radio.uhd.pointerUSRP,tunePointer,chan,pointerTuneResult)
pointerCarrierFreq = Ref{Cdouble}(0);
# sleep(0.001);
uhd_usrp_get_rx_freq(radio.uhd.pointerUSRP,chan,pointerCarrierFreq)
updateCarrierFreq = pointerCarrierFreq[];
if updateCarrierFreq != carrierFreq
@warnrx "Effective carrier frequency is $(updateCarrierFreq/1e6) MHz and not $(carrierFreq/1e6) MHz\n"
else
# @inforx "Effective carrier frequency is $(updateCarrierFreq/1e6) MHz\n";
end
radio.carrierFreq = updateCarrierFreq;
return updateCarrierFreq;
end
"""
Get a single buffer from the USRP device, and create all the necessary ressources
# --- Syntax
sig = recv(radio,nbSamples)
# --- Input parameters
- radio : UHD object [UHDRx]
- nbSamples : Desired number of samples [Int]
# --- Output parameters
- sig : baseband signal from radio [Array{Complex{CFloat}},radio.packetSize]
"""
function recv(radio::UHDRx,nbSamples);
# --- Populate the buffer
sig = [zeros(Complex{Cfloat},nbSamples) for _ ∈ 1:radio.nbAntennaRx]
nbSamples = recv!(sig,radio);
if radio.nbAntennaRx == 1
# In SISO mode, we return a vector
return sig[1]
else
# In MIMO mode, we return a vector of vector
return sig
end
end
"""
Get a single buffer from the USRP device, using the Buffer structure
# --- Syntax
recv!(sig,radio,nbSamples)
# --- Input parameters
- sig : Complex signal to populate [Array{Complex{Cfloat}}]
- radio : UHD object [UHDRx]
- buffer : Buffer object (obtained with setBuffer(radio)) [Buffer]
# --- Output parameters
-
"""
function recv!(sig::Vector{Vector{Complex{T}}},radio::UHDRx;nbSamples=0,offset=0) where T
# restartStreamer(radio)
# --- Defined parameters for multiple buffer reception
filled = false;
# --- Fill the input buffer @ a specific offset
if offset == 0
posT = Csize_t(0);
else
posT = Csize_t(offset);
end
# --- Managing desired size and buffer size
if nbSamples == 0
# --- Fill all the buffer
# x2 as input is complex and we feed real words
nbSamples = Csize_t(length(sig[1]));
else
# --- x2 as input is complex and we feed real words
nbSamples = Csize_t(nbSamples);
# --- Ensure that the allocation is possible
@assert nbSamples < (length(sig[1])+posT) "Impossible to fill the buffer (number of samples > residual size)";
end
global SIG = sig
while !filled
# --- Get a buffer: We should have radio.packetSize or less
# radio.packetSize is the complex size, so x2
(posT+radio.packetSize> nbSamples) ? n = nbSamples - posT : n = radio.packetSize;
# --- To avoid memcopy, we direclty feed the pointer at the appropriate solution
ptr = [pointer(sig[k],1+posT) for k ∈ 1 : radio.nbAntennaRx]
# --- Populate buffer with radio samples
cSamples = populateBuffer!(radio,ptr,n);
# --- Update counters
posT += cSamples;
# @show Int(cSamples),Int(posT)
# --- Breaking flag
(posT == nbSamples) ? filled = true : filled = false;
end
return posT
end
"""
Calling UHD function wrapper to fill a buffer. It is preferable to cal this function though the use of recv or recv!
# --- Syntax
recv!(sig,radio,nbSamples)
# --- Input parameters
- radio : UHD object [UHDRx]
- ptr : Writable memory position [Ref{Ptr{Cvoid}}]
- nbSamples : Number of samples to acquire
# --- Output parameters
- nbSamp : Number of samples fill in buffer [Csize_t]
"""
function populateBuffer!(radio,ptr,nbSamples::Csize_t=0)
# --- Getting number of samples
# If not specified, we get back to radio.packetSize
if (nbSamples == Csize_t(0))
nbSamples = radio.packetSize;
end
#@assert nbSamples <= length(buffer.x) "Number of desired samples can not be greater than buffer size";
# --- Effectively recover data
pointerSamples = Ref{Csize_t}(0);
uhd_rx_streamer_recv(radio.uhd.pointerStreamer,ptr,nbSamples,radio.uhd.addressMD,0.1,false,pointerSamples)
# --- Check for errors
if radio.checkErrors && pointerSamples[] == 0
err = getError(radio)
if err != UHD_RX_METADATA_ERROR_CODE_NONE && err != UHD_RX_METADATA_ERROR_CODE_TIMEOUT
if err == UHD_RX_METADATA_ERROR_CODE_LATE_COMMAND
throw(LateCommandException())
elseif err == UHD_RX_METADATA_ERROR_CODE_BROKEN_CHAIN
throw(BrokenChainException())
elseif err == UHD_RX_METADATA_ERROR_CODE_OVERFLOW
throw(OverflowException())
elseif err == UHD_RX_METADATA_ERROR_CODE_ALIGNMENT
throw(AlignmentException())
elseif err == UHD_RX_METADATA_ERROR_CODE_BAD_PACKET
throw(BadPacketException())
else
# This should be unreachable
@warn "Unexpected UHD error code: $err"
end
end
end
# --- Pointer deferencing
return pointerSamples[];
end#
"""
Returns the Error flag of the current UHD burst
# --- Syntax
flag = getError(radio)
# --- Input parameters
- radio : UHD object [UHDRx]
# --- Output parameters
- err : Error Flag [error_code_t]
"""
function getError(radio::UHDRx)
ptrErr = Ref{uhd_rx_metadata_error_code_t}();
uhd_rx_metadata_error_code(radio.uhd.pointerMD,ptrErr)
return err = ptrErr[];
end
"""
Return the timestamp of the last UHD burst
# --- Syntax
(second,fracSecond) = getTimestamp(radio)
# --- Input parameters
- radio : UHD UHD object [UHDRx]
# --- Output parameters
- second : Second value for the flag [Int]
- fracSecond : Fractional second value [Float64]
"""
function getTimestamp(radio::UHDRx)
ptrFullSec = Ref{FORMAT_LONG}();
ptrFracSec = Ref{Cdouble}();
uhd_rx_metadata_time_spec(radio.uhd.pointerMD,ptrFullSec,ptrFracSec)
return (ptrFullSec[],ptrFracSec[]);
end
function Base.close(radio::UHDRx)
# --- Checking realease nature
# There is one flag to avoid double close (that leads to seg fault)
if radio.released == 0
@assert_uhd uhd_rx_streamer_free(radio.uhd.addressStream)
@assert_uhd uhd_rx_metadata_free(radio.uhd.addressMD)
else
# print a warning
@warn "UHD ressource was already released, abort call";
end
# --- Force flag value
radio.released = 1;
end
function Base.print(radio::UHDRx,chan=0)
# Get the gain from UHD
pointerGain = Ref{Cdouble}(0);
uhd_usrp_get_rx_gain(radio.uhd.pointerUSRP,chan,"",pointerGain)
updateGain = pointerGain[];
# Get the rate from UHD
pointerRate = Ref{Cdouble}(0);
uhd_usrp_get_rx_rate(radio.uhd.pointerUSRP,chan,pointerRate)
updateRate = pointerRate[];
# Get the freq from UHD
pointerFreq = Ref{Cdouble}(0);
uhd_usrp_get_rx_freq(radio.uhd.pointerUSRP,chan,pointerFreq)
updateFreq = pointerFreq[];
# Print message
strF = @sprintf(" Carrier Frequency: %2.3f MHz\n Sampling Frequency: %2.3f MHz\n Rx Gain: %2.2f dB\n",updateFreq/1e6,updateRate/1e6,updateGain);
@inforx "Current UHD Configuration in Rx mode\n$strF";
end