DLL wrapper for RTL-SDR
Re: DLL wrapper for RTL-SDR
I modified the listing above.
- vertexview
- User
- Posts: 28
- Joined: Thu Jan 25, 2024 8:33 am
- Location: France
Re: DLL wrapper for RTL-SDR
It's all good 
You have achieved the perfect conversion to Purebasic of the osmocom C rtl_eeprom utility.
I compiled your code in console mode and I don't see any difference with the application provided in the driver package.
Tests carried out: SDR key identification, menu commands, serial number change, dump file generation, restoration of configuration from the dump...

You have achieved the perfect conversion to Purebasic of the osmocom C rtl_eeprom utility.
I compiled your code in console mode and I don't see any difference with the application provided in the driver package.
Tests carried out: SDR key identification, menu commands, serial number change, dump file generation, restoration of configuration from the dump...
Code: Select all
H:\Projets\Purebasic\SDR>rtl_eeprom -w dumpv4p.dat
Found 1device(s):
0: Generic RTL2832U OEM
Using device 0: Generic RTL2832U OEM
Found Rafael Micro R828D tuner
RTL-SDR Blog V4 Detected
Current configuration:
__________________________________________
Vendor ID: $0BDA
Product ID: $2838
Manufacturer: RTLSDRBlog
Product: Blog V4
Serial number: 00000012
Serial number enabled: yes
IR endpoint enabled: yes
Remote wakeup enabled: no
__________________________________________
New configuration:
__________________________________________
Vendor ID: $0BDA
Product ID: $2838
Manufacturer: RTLSDRBlog
Product: Blog V4
Serial number: 00000011
Serial number enabled: yes
IR endpoint enabled: yes
Remote wakeup enabled: no
__________________________________________
Write new configuration to device [y/n]?
Configuration successfully written.
Please replug the device For changes to take effect.
H:\Projets\Purebasic\SDR>rtl_eeprom
Found 1device(s):
0: Generic RTL2832U OEM
Using device 0: Generic RTL2832U OEM
Found Rafael Micro R828D tuner
RTL-SDR Blog V4 Detected
Current configuration:
__________________________________________
Vendor ID: $0BDA
Product ID: $2838
Manufacturer: RTLSDRBlog
Product: Blog V4
Serial number: 00000011
Serial number enabled: yes
IR endpoint enabled: yes
Remote wakeup enabled: no
__________________________________________
H:\Projets\Purebasic\SDR>rtl_eeprom -d 0
Found 2device(s):
0: Generic RTL2832U OEM
1: Generic RTL2832U OEM
Using device 0: Generic RTL2832U OEM
Found Rafael Micro R820T tuner
Current configuration:
__________________________________________
Vendor ID: $0BDA
Product ID: $2838
Manufacturer: Realtek
Product: RTL2838UHIDIR
Serial number: 00000001
Serial number enabled: yes
IR endpoint enabled: yes
Remote wakeup enabled: no
__________________________________________
H:\Projets\Purebasic\SDR>rtl_eeprom -d 1
Found 2device(s):
0: Generic RTL2832U OEM
1: Generic RTL2832U OEM
Using device 1: Generic RTL2832U OEM
Found Rafael Micro R828D tuner
RTL-SDR Blog V4 Detected
Current configuration:
__________________________________________
Vendor ID: $0BDA
Product ID: $2838
Manufacturer: RTLSDRBlog
Product: Blog V4
Serial number: 00000011
Serial number enabled: yes
IR endpoint enabled: yes
Remote wakeup enabled: no
__________________________________________
H:\Projets\Purebasic\SDR>
- vertexview
- User
- Posts: 28
- Joined: Thu Jan 25, 2024 8:33 am
- Location: France
Re: DLL wrapper for RTL-SDR
New tests made with the code below.
I changed test condition in If rtlsdr_open(@*dev, dev_index) > 0, replacing "superior" by "superior or equal"
. Get device count -> OK
. Get device name -> OK
. Get tuner type > OK
. Set and get frequency -> OK
. Set and get tuner gain -> OK
. Set On and Off for bias tee (power transmitted by SDR to external Low Noise Amplifier) -> OK
I changed test condition in If rtlsdr_open(@*dev, dev_index) > 0, replacing "superior" by "superior or equal"
. Get device count -> OK
. Get device name -> OK
. Get tuner type > OK
. Set and get frequency -> OK
. Set and get tuner gain -> OK
. Set On and Off for bias tee (power transmitted by SDR to external Low Noise Amplifier) -> OK
Code: Select all
;-Demo
CompilerIf #PB_Compiler_IsMainFile
; ------- Variable definitions
Define.l counter, my_freq, my_gain, my_srate, my_fcor, tuner_type, dev_index, status, idx
Define sdr_Name.s
Define *dev.rtlsdr_dev_t
Define Dim manufact.a(256)
Define Dim product.a(256)
Define Dim serial.a(256)
; ------- SDR Tests access
If UseLibrtlsdr()
;dev_index = verbose_device_search("00000053")
dev_index=1
counter = rtlsdr_get_device_count()
idx = rtlsdr_get_index_by_serial("00000054")
Debug "SDR connected: " + counter + #LF$ + "Test get device Idx by serial With 00000054: "+ idx
rtlsdr_get_device_usb_strings(dev_index, @manufact(0), @product(0), @serial(0))
Debug "Check USB Strings for index = " + Str(dev_index) + " - Result: " + PeekS(@manufact(0), -1, #PB_UTF8) + ", " + PeekS(@product(0), -1, #PB_UTF8) + ", " + PeekS(@serial(0), -1, #PB_UTF8)
status= rtlsdr_open(@*dev, dev_index)
Debug "rtlsdr_open function return value: " + status
If status = 0
sdr_name = PeekS(rtlsdr_get_device_name(dev_index), -1, #PB_UTF8)
tuner_type = rtlsdr_get_tuner_type(*dev)
Debug "Device opened index: " + dev_index + #LF$ + "SDR Name: " + sdr_name + #LF$ + "SDR Type: "+ Str(tuner_type)
rtlsdr_set_center_freq(*dev, 92200000)
rtlsdr_set_tuner_gain(*dev, 372)
rtlsdr_set_sample_rate(*dev, 2048000)
rtlsdr_set_freq_correction(*dev, 2)
rtlsdr_set_bias_tee(*dev, 1)
my_freq = rtlsdr_get_center_freq(*dev)
my_gain = rtlsdr_get_tuner_gain(*dev)
my_srate = rtlsdr_get_sample_rate(*dev)
my_fcor= rtlsdr_get_freq_correction(*dev)
Debug "Get frequency (Hz): " + Str(my_freq) + #LF$ + "Get gain (tenths of dB): " + Str(my_gain) + #LF$ + "SDR Bias-tee ON" + #LF$ + "Get sample rate (bps): "+ Str(my_srate)+ #LF$ + "Get frequency correction (ppm): "+ Str(my_fcor)
; RTL-SDR Blog v4 Led indicator activated if bias-tee ON
Delay (5000)
rtlsdr_set_bias_tee(*dev, 0)
Debug "SDR Bias-tee OFF"
status=rtlsdr_close(*dev)
Debug "rtlsdr_close function return value: " + status
Else
Debug "Was not able to open a device"
EndIf
EndIf
CompilerEndIf
Last edited by vertexview on Mon Jan 29, 2024 10:21 pm, edited 1 time in total.
Re: DLL wrapper for RTL-SDR
According to the original code,
returns 0 on success
0, I think, is always the return value for success.
You have to read the include header file and if te answer is not inside, the c source code.
Code: Select all
rtlsdr_open()
0, I think, is always the return value for success.
You have to read the include header file and if te answer is not inside, the c source code.
- vertexview
- User
- Posts: 28
- Joined: Thu Jan 25, 2024 8:33 am
- Location: France
Re: DLL wrapper for RTL-SDR
You think correctly infratec.
Controls, and new tests:
. rtlsdr open function, with device index -> OK. Function returns 0 if success, -1 if not.
. get device usb strings, with device index -> OK
. rtlsdr read and write eeprom -> OK
. get index by serial -> OK. Function returns 0 if success, a negative value if not.
. Set and get sample rate -> OK
. Set and get freq correction -> OK
Test code updated in previous post.
Debug view
SDR connected: 2
Test get device Idx by serial With 00000054: 1
Check USB Strings for index = 1 - Result: RTLSDRBlog, Blog V4, 00000054
rtlsdr_open function return value: 0
Device opened index: 1
SDR Name: Generic RTL2832U OEM
SDR Type: 6
Get frequency (Hz): 92200000
Get gain (tenths of dB): 372
SDR Bias-tee ON
Get sample rate (bps): 2048000
Get frequency correction (ppm): 2
SDR Bias-tee OFF
rtlsdr_close function return value: 0
Controls, and new tests:
. rtlsdr open function, with device index -> OK. Function returns 0 if success, -1 if not.
. get device usb strings, with device index -> OK
. rtlsdr read and write eeprom -> OK
. get index by serial -> OK. Function returns 0 if success, a negative value if not.
. Set and get sample rate -> OK
. Set and get freq correction -> OK
Test code updated in previous post.
Debug view
SDR connected: 2
Test get device Idx by serial With 00000054: 1
Check USB Strings for index = 1 - Result: RTLSDRBlog, Blog V4, 00000054
rtlsdr_open function return value: 0
Device opened index: 1
SDR Name: Generic RTL2832U OEM
SDR Type: 6
Get frequency (Hz): 92200000
Get gain (tenths of dB): 372
SDR Bias-tee ON
Get sample rate (bps): 2048000
Get frequency correction (ppm): 2
SDR Bias-tee OFF
rtlsdr_close function return value: 0
- vertexview
- User
- Posts: 28
- Joined: Thu Jan 25, 2024 8:33 am
- Location: France
Re: DLL wrapper for RTL-SDR
I successfully tried to create a simple ADSB decoder with the RTL-SDR wrapper.
The SimpleADSB_sync.pb code below is derived from the original Osmocom C source rtl_adsb and its Delphi/Pascal port (Randall experimentation, rtl_syncread part).
The test was carried out to run the simplest console application, with synchronous reading and processing of the SDR IQ buffer.
Main C and Delphi sources:
https://github.com/osmocom/rtl-sdr/blob ... rtl_adsb.c
https://sourceforge.net/projects/rtl-ad ... port/files
ADSB protocol documentation:
https://github.com/junzis
https://mode-s.org/decode/
Free Junzy Sun ADSB book (PDF): https://mode-s.org/decode/book-the_1090 ... zi_sun.pdf
I/Q data processing information:
https://www.pe0sat.vgnet.nl/sdr/iq-data-explained
https://k3xec.com/packrat-processing-iq
This code is made for educational purposes.
The SimpleADSB_sync.pb code below is derived from the original Osmocom C source rtl_adsb and its Delphi/Pascal port (Randall experimentation, rtl_syncread part).
The test was carried out to run the simplest console application, with synchronous reading and processing of the SDR IQ buffer.
Main C and Delphi sources:
https://github.com/osmocom/rtl-sdr/blob ... rtl_adsb.c
https://sourceforge.net/projects/rtl-ad ... port/files
ADSB protocol documentation:
https://github.com/junzis
https://mode-s.org/decode/
Free Junzy Sun ADSB book (PDF): https://mode-s.org/decode/book-the_1090 ... zi_sun.pdf
I/Q data processing information:
https://www.pe0sat.vgnet.nl/sdr/iq-data-explained
https://k3xec.com/packrat-processing-iq
This code is made for educational purposes.
Code: Select all
EnableExplicit
IncludeFile "librtlsdr.pbi"
#SDR_SRATE =2000000
#ADSB_FREQ =1090000000
#SDR_GAIN =439
#PREAMBLE_LEN =16
#LONG_FRAME =112
#SHORT_FRAME =56
#MESSAGEGO =253
#OVERWRITE =254
#BAD_SAMPLE =255
#BUF_LENGTH =16*16384
Define.i i, r, n_read
Define.l dev_index
Define.i device_count
Define *dev.rtlsdr_dev_t
Define *buf
Define *n_read
Global length = 0
Global block_size.i=#BUF_LENGTH
Global StopReadingSamples.b=#False
Global Dim pythagore.a(128,128)
Global Dim adsb_frame.a(14)
Global Dim buffer.a(block_size)
Global allowed_errors.i = 5
Global quality.i = 20
Global short_output.i = 0
Global verbose_output.i = 1
Global lf_counter.i = 0
Global sf_counter.i = 0
Global show_stats.b = #False
Procedure.i abs8(x.i)
If x >= 128
ProcedureReturn x - 128
Else
ProcedureReturn 128 - x
EndIf
EndProcedure
Procedure pythagore_precompute()
Protected.i x, y
Protected.f scale
Protected.a value
scale = 1.408
For x = 0 To 128
For y = 0 To 128
value=Round(scale * Sqr(x*x + y*y), #PB_Round_Nearest)
pythagore.a(x, y) = value
Next y
Next x
EndProcedure
Procedure magnitude(buf.a, len)
Protected.i i,j
Protected.a sq
; takes IQ samples. Changes in place with the magnitude value (Char/uint8_t)
For i=0 To len-1 Step 2
sq.a = pythagore(abs8(buffer(i)), abs8(buffer(i + 1)))
buffer(Int(i/2))=sq.a
Next i
;returns new buffer length
ProcedureReturn len/2
EndProcedure
Procedure.a single_manchester(a.a, b.a, c.a, d.a)
Protected bit.a, bit_p.a
; takes 4 consecutive samples, return 0 or 1, #BADSAMPLE on error
If a>b
bit_p=1
Else
bit_p=0
EndIf
If c>d
bit=1
Else
bit=0
EndIf
If quality=0
; no sanity check
ProcedureReturn bit
EndIf
If quality=5
; sanity check half-bit
If ((bit=1) And (bit_p=1) And (b>c))
ProcedureReturn #BAD_SAMPLE
EndIf
If ((bit=0) And (bit_p=0) And (b<c))
ProcedureReturn #BAD_SAMPLE
EndIf
ProcedureReturn bit
EndIf
If quality=10
; sanity check one bit
If ((bit=1) And (bit_p=1) And (c>b))
ProcedureReturn 1
EndIf
If ((bit=1) And (bit_p=0) And (d<b))
ProcedureReturn 1
EndIf
If ((bit=0) And (bit_p=1) And (d>b))
ProcedureReturn 0
EndIf
If ((bit=0) And (bit_p=0) And (c<b))
ProcedureReturn 0
EndIf
ProcedureReturn #BAD_SAMPLE
EndIf
; Sanity check two bits
If ((bit=1) And (bit_p=1) And (c>b) And (d<a))
ProcedureReturn 1
EndIf
If ((bit=1) And (bit_p=0) And (c>a) And (d<b))
ProcedureReturn 1
EndIf
If ((bit=0) And (bit_p=1) And (c<a) And (d>b))
ProcedureReturn 0
EndIf
If ((bit=0) And (bit_p=0) And (c<b) And (d>a))
ProcedureReturn 0
EndIf
ProcedureReturn #BAD_SAMPLE
EndProcedure
Procedure preamble(buf.a, i.i)
Protected i2.i
Protected low.a, high.a
low.a = 0
high.a = 255
; Returns 0/1 for preamble at index i
For i2 = 0 To #PREAMBLE_LEN-1
Select i2
Case 0,2,7,9
high = buffer(i+i2)
Default
low = buffer(i+i2)
EndSelect
If high <= low
ProcedureReturn 0
EndIf
Next i2
; Preamble sequence found
ProcedureReturn 1
EndProcedure
Procedure manchester(buf.a, len)
Protected a.a, b.a, bit.a
Protected i.i, i2.i, errors.i
a = 0
b = 0
i = 0
; Overwrites magnitude buffer with valid bits (#BADSAMPLE on errors)
While (i < len)
; Search a preamble sequence
While i < len - #PREAMBLE_LEN
If preamble(@buffer.a(), i) = 0
i+1
Continue
EndIf
; preamble found at index i
a = buffer(i)
b = buffer(i + 1)
For i2 = 0 To #PREAMBLE_LEN-1
; ovewrite buffer sequence with #MESSAGEGO
buffer(i + i2) = #MESSAGEGO
Next i2
; new index after preamble sequence
i= i+#PREAMBLE_LEN
Break
Wend
i2 = i
errors = 0
; Mark bits until encoding breaks
While (i < len)
bit = single_manchester(a, b, buffer(i), buffer(i + 1))
a = buffer(i)
b = buffer(i + 1)
If bit = #BAD_SAMPLE
errors+1
If errors > allowed_errors
buffer(i2) = #BAD_SAMPLE
Break
Else
If a>b
bit = 1
Else
bit = 0
EndIf
a = 0
b = 255
EndIf
EndIf
buffer(i) = #OVERWRITE
buffer(i + 1) = #OVERWRITE
buffer(i2) = bit
i+2
i2+1
Wend
Wend
EndProcedure
Procedure display(frame.a, len.i)
Protected i.i
Protected df.a, value.a, icao.i, pcode.i, tcode.a, stype.a
Protected test.b
If (short_output = 0) And (len <= #SHORT_FRAME)
ProcedureReturn
EndIf
df = (adsb_frame(0) >> 3) & $1F
If Quality=0 And Not ((df = 11) Or (df = 17) Or (df = 18) Or (df = 19))
ProcedureReturn
EndIf
Print("#")
For i = 0 To (Int(len + 7)/8)-1
Print(RSet(Hex(adsb_frame(i),#PB_Ascii),2,"0"))
Next i
PrintN("")
If verbose_output = 1
PrintN(FormatDate(" %yyyy/%mm/%mm/%dd %hh:%ii:%ss", Date()))
value=adsb_frame(0) & $07
PrintN(" DF="+df+ " CA="+value)
icao =(adsb_frame(1) << 16) | (adsb_frame(2) << 8) | adsb_frame(3)
PrintN(" ICAO= "+RSet(Hex(icao),6,"0"))
EndIf
If len <= #SHORT_FRAME
ProcedureReturn
EndIf
If verbose_output = 1
pcode=(adsb_frame(11) << 16) | (adsb_frame(12) << 8) | adsb_frame(13)
tcode=(adsb_frame(4) >> 3) & $1F
stype=adsb_frame(4) & $07
;PrintN(" PI_code= "+pcode)
PrintN(" PI_code= 0x"+RSet(Hex(pcode),6,"0"))
PrintN(" Type_code= "+tcode)
PrintN(" S_type/ant= "+stype)
EndIf
EndProcedure
Procedure outmessages(buf.a, len)
Protected.i i, index
Protected.a shift, frame_len, val_shift, data_i
i =0
lf_counter =0
sf_counter =0
While i< len
If buffer(i) > 1
i+1
Continue
EndIf
frame_len = #LONG_FRAME
data_i = 0
For index = 0 To 13
adsb_frame(index) = 0
Next index
While (i < len) And (buffer(i) <= 1) And (data_i < frame_len)
If buffer(i) = 1
index = Int(data_i / 8)
shift = 7 - (data_i % 8)
val_shift = 1 << shift
adsb_frame(index) = adsb_frame(index) | val_shift
EndIf
If data_i = 7
If adsb_frame(0) = 0
Break
EndIf
If adsb_frame(0) & $80 <> 0
frame_len = #LONG_FRAME
lf_counter+1
Else
frame_len = #SHORT_FRAME
sf_counter+1
EndIf
EndIf
i+1
data_i+1
Wend
If data_i < frame_len-1
i+1
Continue
EndIf
display(adsb_frame.a(), frame_len)
i+1
Wend
EndProcedure
Procedure.i demodulation()
lf_counter = 0
sf_counter = 0
length = magnitude(@buffer.a(), block_size)
manchester(@buffer.a(), length)
outmessages(@buffer.a(), length)
If show_stats=#True
PrintN("ADSB frames (Long/Short): "+Str(lf_counter)+"/"+Str(sf_counter))
EndIf
EndProcedure
; ------- Main -------
pythagore_precompute()
OpenConsole()
If UseLibrtlsdr()
device_count = rtlsdr_get_device_count()
If Not device_count
PrintN("No supported devices found.")
End 1
EndIf
PrintN("Found " + Str(device_count) + " device(s)")
For i = 0 To device_count - 1
PrintN(" " + Str(i) + ": " + PeekS(rtlsdr_get_device_name(i), -1, #PB_UTF8))
Next i
PrintN("")
PrintN("Using device " + Str(dev_index) + ": " + PeekS(rtlsdr_get_device_name(dev_index), -1, #PB_UTF8))
r = rtlsdr_open(@*dev, dev_index)
If r < 0
PrintN("Failed to open rtlsdr device #" + Str(dev_index))
End 1
EndIf
PrintN("")
rtlsdr_set_center_freq(*dev, #ADSB_FREQ)
rtlsdr_set_sample_rate(*dev, #SDR_SRATE)
rtlsdr_set_tuner_gain(*dev, #SDR_GAIN)
rtlsdr_set_agc_mode(*dev, 1)
PrintN("Tuned to "+ rtlsdr_get_center_freq(*dev) + " Hz")
PrintN("Tuner gain: "+ rtlsdr_get_tuner_gain(*dev))
r = rtlsdr_reset_buffer(*dev)
If r < 0
PrintN("Failed to reset buffer")
EndIf
PrintN("Reading samples in sync mode")
PrintN("")
While #True
r = rtlsdr_read_sync(*dev, @buffer.a(), block_size, @n_read)
If r < 0
PrintN("Buffer Sync read failed\n")
EndIf
If n_read < block_size
PrintN("Short read, samples lost\n" + n_read)
Break
EndIf
If StopReadingSamples
Break
EndIf
; IQ Buffer data processing
Demodulation()
Wend
rtlsdr_close(*dev)
If r >= 0
End r
Else
End -r
EndIf
EndIf
CompilerIf #PB_Compiler_Debugger
Input()
CompilerEndIf
CloseConsole()
- vertexview
- User
- Posts: 28
- Joined: Thu Jan 25, 2024 8:33 am
- Location: France
Re: DLL wrapper for RTL-SDR
Hello,
Last year i worked on the RTL-SDR dongle Purebasic wrapper.
Again, many thanks to infratec for his great help leading to the "libsdr.pbi" file.
In my post above, you can find the working example for ADSB decoding using synchronous buffer reading function (rtlsdr_read_sync) in console mode.
The prototype defined in librtlsdr.pbi is:
PrototypeC.l proto_rtlsdr_read_sync(*dev.rtlsdr_dev_t, *buf, len.l, *n_read)
The purebasic code above is mainly derived from old Delphi code concerning both synchronous and asynchronous ADSB decoding
https://sourceforge.net/projects/rtl-ad ... -xe8-port/
I wonder if it is possible to make the same thing using asynchronous buffer reading (rtlsdr_read_async), with thread and semaphore like in Delphi code.
The prototype defined in librtlsdr.pbi for asynchronous reading is:
PrototypeC.l proto_rtlsdr_read_async(*dev.rtlsdr_dev_t, cb.rtlsdr_read_async_cb_t, *ctx, buf_num.l, buf_len.l)
Here is the Delphi source code for ADSB asynchronous decoding in console:
https://github.com/randaller/rtlsdr-ads ... d_adsb.dpr
rtlsdr_read_async function declared in rtlsdr.pas wrapper:
// RTLSDR_API int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len);
function rtlsdr_read_async(rtlsdr_dev_t: Pointer; cb: Trtlsdr_read_async_cb_t; ctx: Pointer; buf_num: UINT32; buf_len: UINT32): Integer; stdcall; external rtldll;
Here are specific Delphi parts for asynchronous decoding in rtl_asyncread_adsb.dpr:
Below is my Purebasic code. The Window console is launched without error, SDR dongle is recognized,... but no buffer data is processed.
Here are the specific asynchronous decoding parts:
Here is the whole code
Thanks for your help, if possible.
Last year i worked on the RTL-SDR dongle Purebasic wrapper.
Again, many thanks to infratec for his great help leading to the "libsdr.pbi" file.
In my post above, you can find the working example for ADSB decoding using synchronous buffer reading function (rtlsdr_read_sync) in console mode.
The prototype defined in librtlsdr.pbi is:
PrototypeC.l proto_rtlsdr_read_sync(*dev.rtlsdr_dev_t, *buf, len.l, *n_read)
The purebasic code above is mainly derived from old Delphi code concerning both synchronous and asynchronous ADSB decoding
https://sourceforge.net/projects/rtl-ad ... -xe8-port/
I wonder if it is possible to make the same thing using asynchronous buffer reading (rtlsdr_read_async), with thread and semaphore like in Delphi code.
The prototype defined in librtlsdr.pbi for asynchronous reading is:
PrototypeC.l proto_rtlsdr_read_async(*dev.rtlsdr_dev_t, cb.rtlsdr_read_async_cb_t, *ctx, buf_num.l, buf_len.l)
Here is the Delphi source code for ADSB asynchronous decoding in console:
https://github.com/randaller/rtlsdr-ads ... d_adsb.dpr
rtlsdr_read_async function declared in rtlsdr.pas wrapper:
// RTLSDR_API int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len);
function rtlsdr_read_async(rtlsdr_dev_t: Pointer; cb: Trtlsdr_read_async_cb_t; ctx: Pointer; buf_num: UINT32; buf_len: UINT32): Integer; stdcall; external rtldll;
Here are specific Delphi parts for asynchronous decoding in rtl_asyncread_adsb.dpr:
Code: Select all
var
thread_buf: array [0 .. DEFAULT_BUF_LENGTH] of Byte;
ThreadHandle: integer;
ThreadID: DWORD;
hSem: THandle = 0;
function Read(buffer: PAnsiChar; size: integer; ctx: Pointer): LongWord;
begin
if ctx = nil then
Exit(0);
// 262144 bytes in buffer
// Move(buf, ctx, size);
CopyMemory(@buf[0], ctx, size);
hSem := CreateSemaphore(nil, 1, 1, nil);
Result := 1;
end;
procedure ThreadProc;
var
len: integer;
WaitReturn: integer;
begin
while (True) do
begin
WaitReturn := WaitForSingleObject(hSem, INFINITE);
if WaitReturn = WAIT_OBJECT_0 then
begin
// writeln('thread on');
CopyMemory(@thread_buf, @buf, DEFAULT_BUF_LENGTH);
len := magnitute(thread_buf, DEFAULT_BUF_LENGTH);
manchester(thread_buf, len);
outmessages(thread_buf, len);
ReleaseSemaphore(hSem, 1, nil);
CloseHandle(hSem);
// writeln('thread off');
end;
end;
rtlsdr_cancel_async(dev);
end;
## Specific part in the Main Delphi code ##
ThreadHandle := CreateThread(nil, 0, @ThreadProc, nil, 0, ThreadID);
r := rtlsdr_read_async(dev, @read, nil, 32, DEFAULT_BUF_LENGTH);
Here are the specific asynchronous decoding parts:
Code: Select all
Global Dim thread_buf.a(block_size)
Global hThread.l
Global hSem.l
Procedure ReadBufffer(*buf, len.l, *ctx)
If *ctx=0
ProcedureReturn 0
EndIf
; CopyMemory in Delphi (Destination: Pointer; Source: Pointer; Length: DWORD)
; CopyMemory in Purebasic (*Source, *Destination, Size)
CopyMemory(*buf, @buffer.a(), len.l)
; handle semaphore
hSem=CreateSemaphore()
MessageRequester("Info","Copy realized and semaphore created", 0)
ProcedureReturn 1
EndProcedure
Procedure ThreadProc(value)
Protected len.i
Protected WaitReturn.i
Repeat
;WaitReturn=WaitSemaphore(hSem)
;If WaitReturn=0
; CopyMemory (source, destination, taille)
CopyMemory(@buffer.a(), @thread_buf.a(), block_size)
len = magnitude(@thread_buf.a(), block_size)
manchester(@thread_buf.a(), len)
outmessages(@thread_buf.a(), len)
; FreeSemaphore(hSem)
;EndIf
ForEver
;rtlsdr_cancel_async(*dev)
EndProcedure
## Specific part in the Main Purebasic code ##
hThread=CreateThread(@ThreadProc(), 0)
r = rtlsdr_read_async(*dev, ReadBuffer, 0, 32, block_size)
Code: Select all
EnableExplicit
IncludeFile "librtlsdr.pbi"
#SDR_SRATE =2000000
#ADSB_FREQ =1090000000
#SDR_GAIN =439
#PREAMBLE_LEN =16
#LONG_FRAME =112
#SHORT_FRAME =56
#MESSAGEGO =253
#OVERWRITE =254
#BAD_SAMPLE =255
#BUF_LENGTH =16*16384
Define.i i, r, n_read
Define.l dev_index
Define.i device_count
Define *dev.rtlsdr_dev_t
Define *buf
Define *n_read
Define ReadBuffer
Define cb.rtlsdr_read_async_cb_t
Global length.i = 0
Global buf_num.i = 0
Global block_size.i=#BUF_LENGTH
Global StopReadingSamples.b=#False
Global Dim pythagore.a(128,128)
Global Dim adsb_frame.a(14)
; Global Dim buf.a(block_size)
Global Dim buffer.a(block_size)
Global Dim thread_buf.a(block_size)
Global hThread.l
Global hSem.l
Global allowed_errors.i = 5
Global quality.i = 20
Global short_output.i = 0
Global verbose_output.i = 0
Global lf_counter.i = 0
Global sf_counter.i = 0
Global show_stats.b = #False
Procedure.i abs8(x.i)
If x >= 128
ProcedureReturn x - 128
Else
ProcedureReturn 128 - x
EndIf
EndProcedure
Procedure pythagore_precompute()
Protected.i x, y
Protected.f scale
Protected.a value
scale = 1.408
For x = 0 To 128
For y = 0 To 128
value=Round(scale * Sqr(x*x + y*y), #PB_Round_Nearest)
pythagore.a(x, y) = value
Next y
Next x
EndProcedure
Procedure magnitude(buf.a, len)
Protected.i i,j
Protected.a sq
; takes IQ samples. Changes in place with the magnitude value (Char/uint8_t)
For i=0 To len-1 Step 2
sq.a = pythagore(abs8(buffer(i)), abs8(buffer(i + 1)))
buffer(Int(i/2))=sq.a
Next i
;returns new buffer length
ProcedureReturn len/2
EndProcedure
Procedure.a single_manchester(a.a, b.a, c.a, d.a)
Protected bit.a, bit_p.a
; takes 4 consecutive samples, return 0 or 1, #BADSAMPLE on error
If a>b
bit_p=1
Else
bit_p=0
EndIf
If c>d
bit=1
Else
bit=0
EndIf
If quality=0
; no sanity check
ProcedureReturn bit
EndIf
If quality=5
; sanity check half-bit
If ((bit=1) And (bit_p=1) And (b>c))
ProcedureReturn #BAD_SAMPLE
EndIf
If ((bit=0) And (bit_p=0) And (b<c))
ProcedureReturn #BAD_SAMPLE
EndIf
ProcedureReturn bit
EndIf
If quality=10
; sanity check one bit
If ((bit=1) And (bit_p=1) And (c>b))
ProcedureReturn 1
EndIf
If ((bit=1) And (bit_p=0) And (d<b))
ProcedureReturn 1
EndIf
If ((bit=0) And (bit_p=1) And (d>b))
ProcedureReturn 0
EndIf
If ((bit=0) And (bit_p=0) And (c<b))
ProcedureReturn 0
EndIf
ProcedureReturn #BAD_SAMPLE
EndIf
; Sanity check two bits
If ((bit=1) And (bit_p=1) And (c>b) And (d<a))
ProcedureReturn 1
EndIf
If ((bit=1) And (bit_p=0) And (c>a) And (d<b))
ProcedureReturn 1
EndIf
If ((bit=0) And (bit_p=1) And (c<a) And (d>b))
ProcedureReturn 0
EndIf
If ((bit=0) And (bit_p=0) And (c<b) And (d>a))
ProcedureReturn 0
EndIf
ProcedureReturn #BAD_SAMPLE
EndProcedure
Procedure preamble(buf.a, i.i)
Protected i2.i
Protected low.a, high.a
low.a = 0
high.a = 255
; Returns 0/1 for preamble at index i
For i2 = 0 To #PREAMBLE_LEN-1
Select i2
Case 0,2,7,9
high = buffer(i+i2)
Default
low = buffer(i+i2)
EndSelect
If high <= low
ProcedureReturn 0
EndIf
Next i2
; Preamble sequence found
ProcedureReturn 1
EndProcedure
Procedure manchester(buf.a, len)
Protected a.a, b.a, bit.a
Protected i.i, i2.i, errors.i
a = 0
b = 0
i = 0
; Overwrites magnitude buffer with valid bits (#BADSAMPLE on errors)
While (i < len)
; Find preamble sequence
While i < len - #PREAMBLE_LEN
If preamble(@buffer.a(), i) = 0
i+1
Continue
EndIf
a = buffer(i)
b = buffer(i + 1)
For i2 = 0 To #PREAMBLE_LEN-1
buffer(i + i2) = #MESSAGEGO
Next i2
i= i+#PREAMBLE_LEN
Break
Wend
i2 = i
errors = 0
; Mark bits until encoding breaks
While (i < len)
bit = single_manchester(a, b, buffer(i), buffer(i + 1))
a = buffer(i)
b = buffer(i + 1)
If bit = #BAD_SAMPLE
errors+1
If errors > allowed_errors
buffer(i2) = #BAD_SAMPLE
Break
Else
If a>b
bit = 1
Else
bit = 0
EndIf
a = 0
b = 255
EndIf
EndIf
buffer(i) = #OVERWRITE
buffer(i + 1) = #OVERWRITE
buffer(i2) = bit
i+2
i2+1
Wend
Wend
EndProcedure
Procedure display(frame.a, len.i)
Protected i.i
Protected df.a, value.a, icao.a, pcode.a, tcode.a, stype.a
Protected test.b
If (short_output = 0) And (len <= #SHORT_FRAME)
ProcedureReturn
EndIf
df = (adsb_frame(0) >> 3) & $1F
If Quality=0 And Not ((df = 11) Or (df = 17) Or (df = 18) Or (df = 19))
ProcedureReturn
EndIf
Print("#")
For i = 0 To (Int(len + 7)/8)-1
Print(RSet(Hex(adsb_frame(i),#PB_Ascii),2,"0"))
Next i
PrintN("")
If verbose_output = 1
PrintN(FormatDate(" %yyyy/%mm/%mm/%dd %hh:%ii:%ss", Date()))
value=adsb_frame(0) & $07
PrintN(" DF="+df+ " CA="+value)
icao =(adsb_frame(1) << 16) | (adsb_frame(2) << 8) | adsb_frame(3)
PrintN(" ICAO= "+icao)
EndIf
If len <= #SHORT_FRAME
ProcedureReturn
EndIf
If verbose_output = 1
pcode=(adsb_frame(11) << 16) | (adsb_frame(12) << 8) | adsb_frame(13)
tcode=(adsb_frame(4) >> 3) & $1F
stype=adsb_frame(4) & $07
PrintN(" PI_code= "+pcode)
PrintN(" Type_code= "+tcode)
PrintN(" S_type/ant= "+stype)
EndIf
EndProcedure
Procedure outmessages(buf.a, len)
Protected.i i, index
Protected.a shift, frame_len, val_shift, data_i
i =0
lf_counter =0
sf_counter =0
While i< len
If buffer(i) > 1
i+1
Continue
EndIf
frame_len = #LONG_FRAME
data_i = 0
For index = 0 To 13
adsb_frame(index) = 0
Next index
While (i < len) And (buffer(i) <= 1) And (data_i < frame_len)
If buffer(i) = 1
index = Int(data_i / 8)
shift = 7 - (data_i % 8)
val_shift = 1 << shift
adsb_frame(index) = adsb_frame(index) | val_shift
EndIf
If data_i = 7
If adsb_frame(0) = 0
Break
EndIf
If adsb_frame(0) & $80 <> 0
frame_len = #LONG_FRAME
lf_counter+1
Else
frame_len = #SHORT_FRAME
sf_counter+1
EndIf
EndIf
i+1
data_i+1
Wend
If data_i < frame_len-1
i+1
Continue
EndIf
display(adsb_frame.a(), frame_len)
i+1
Wend
EndProcedure
Procedure.i demodulation()
lf_counter = 0
sf_counter = 0
length = magnitude(@buffer.a(), block_size)
manchester(@buffer.a(), length)
outmessages(@buffer.a(), length)
If show_stats=#True
PrintN("ADSB frames (Long/Short): "+Str(lf_counter)+"/"+Str(sf_counter))
EndIf
EndProcedure
Procedure ReadBufffer(*buf, len.l, *ctx)
If *ctx=0
ProcedureReturn 0
EndIf
; CopyMemory in Delphi (Destination: Pointer; Source: Pointer; Length: DWORD)
; CopyMemory in Purebasic (*Source, *Destination, Size)
CopyMemory(*buf, @buffer.a(), len.l)
; handle semaphore
hSem=CreateSemaphore()
MessageRequester("Info","Copy realized and semaphore created", 0)
ProcedureReturn 1
EndProcedure
Procedure ThreadProc(value)
Protected len.i
Protected WaitReturn.i
Repeat
;WaitReturn=WaitSemaphore(hSem)
;If WaitReturn=0
; CopyMemory (source, destination, taille)
CopyMemory(@buffer.a(), @thread_buf.a(), block_size)
len = magnitude(@thread_buf.a(), block_size)
manchester(@thread_buf.a(), len)
outmessages(@thread_buf.a(), len)
; FreeSemaphore(hSem)
;EndIf
ForEver
;rtlsdr_cancel_async(*dev)
EndProcedure
; ------- Main -------
pythagore_precompute()
OpenConsole()
If UseLibrtlsdr()
device_count = rtlsdr_get_device_count()
If Not device_count
PrintN("No supported devices found.")
End 1
EndIf
PrintN("Found " + Str(device_count) + " device(s)")
For i = 0 To device_count - 1
PrintN(" " + Str(i) + ": " + PeekS(rtlsdr_get_device_name(i), -1, #PB_UTF8))
Next i
PrintN("")
PrintN("Using device " + Str(dev_index) + ": " + PeekS(rtlsdr_get_device_name(dev_index), -1, #PB_UTF8))
r = rtlsdr_open(@*dev, dev_index)
If r < 0
PrintN("Failed to open rtlsdr device #" + Str(dev_index))
End 1
EndIf
PrintN("")
rtlsdr_set_center_freq(*dev, #ADSB_FREQ)
rtlsdr_set_sample_rate(*dev, #SDR_SRATE)
rtlsdr_set_tuner_gain(*dev, #SDR_GAIN)
rtlsdr_set_agc_mode(*dev, 1)
PrintN("Tuned to "+ rtlsdr_get_center_freq(*dev) + " Hz")
PrintN("Tuner gain: "+ rtlsdr_get_tuner_gain(*dev))
r = rtlsdr_reset_buffer(*dev)
If r < 0
PrintN("Failed to reset buffer")
EndIf
PrintN("Reading samples in Async mode")
PrintN("")
hThread=CreateThread(@ThreadProc(), 0)
MessageRequester("Info","ThreadProc running", 0)
r = rtlsdr_read_async(*dev, ReadBuffer, 0, 32, block_size)
If r < 0
PrintN("Buffer Async read failed\n")
EndIf
; Can't reach this point after rtlsdr_read_async
rtlsdr_close(*dev)
If r >= 0
End r
Else
End -r
EndIf
EndIf
CompilerIf #PB_Compiler_Debugger
Input()
CompilerEndIf
CloseConsole()
Re: DLL wrapper for RTL-SDR
There are so many things wrong ...
1. a thread and a semaphore needs .i and not .l, else it will fail with a x64 compiler
2. Procedure manchester(buf.a, len)
but you call it with manchester(@buffer.a(), length)
that means you call it with a pointer as first argument.
3. CopyMemory(*buf, @buffer.a(), len.l) for example needs to be:
CopyMemory(*buf, @buffer.a(0), len.l)
4. you have Procedure magnitude(buf.a, len)
but inside the procedure you never use buf.
And btw. the declaration is completely different to the pascal code.
,,,
No time to step deeper into.

1. a thread and a semaphore needs .i and not .l, else it will fail with a x64 compiler
2. Procedure manchester(buf.a, len)
but you call it with manchester(@buffer.a(), length)
that means you call it with a pointer as first argument.
3. CopyMemory(*buf, @buffer.a(), len.l) for example needs to be:
CopyMemory(*buf, @buffer.a(0), len.l)
4. you have Procedure magnitude(buf.a, len)
but inside the procedure you never use buf.
And btw. the declaration is completely different to the pascal code.
,,,
No time to step deeper into.
Re: DLL wrapper for RTL-SDR
Maybe you can try this:
Code: Select all
EnableExplicit
IncludeFile "librtlsdr.pbi"
#SDR_SRATE = 2000000
#ADSB_FREQ = 1090000000
#SDR_GAIN = 439
#PREAMBLE_LEN = 16
#LONG_FRAME = 112
#SHORT_FRAME = 56
#MESSAGEGO = 253
#OVERWRITE = 254
#BAD_SAMPLE = 255
#DEFAULT_BUF_LENGTH = 16 * 16384
Structure ThreadParameter_Structure
Thread.i
Exit.i
EndStructure
Define.i i, r
Define.l dev_index
Define.i device_count
Define *dev.rtlsdr_dev_t
Define *buf
Global length.i
Global buf_num.i
Global StopReadingSamples.i
Global Dim pythagore.a(128, 128)
Global Dim adsb_frame.a(13)
Global Dim buf.a(#DEFAULT_BUF_LENGTH)
Global Dim thread_buf.a(#DEFAULT_BUF_LENGTH)
Global hThread.i
Global hSem.i
Global allowed_errors.i = 5
Global quality.i = 20
Global short_output.i
Global verbose_output.i
Global show_stats.i
ProcedureC ReadBuffer(*buffer, size.l, *ctx)
Debug "ReadBuffer size: " + Str(size)
; CopyMemory in Delphi (Destination: Pointer; Source: Pointer; Length: DWORD)
; CopyMemory in Purebasic (*Source, *Destination, Size)
CopyMemory(*buffer, @buf(0), size)
SignalSemaphore(hSem)
EndProcedure
Procedure pythagore_precompute()
Protected.i x, y
Protected.d scale
scale = 1.408
For x = 0 To 128
For y = 0 To 128
pythagore(x, y) = Round(scale * Sqr(x*x + y*y), #PB_Round_Nearest)
Next y
Next x
EndProcedure
Procedure.a abs8(x.a)
If x >= 128
ProcedureReturn x - 128
Else
ProcedureReturn 128 - x
EndIf
EndProcedure
Structure ByteArray_Structure
a.a[0]
EndStructure
Procedure.i magnitude(*buf.ByteArray_Structure, len.i)
Protected.i i
; takes IQ samples. Changes in place with the magnitude value (Char/uint8_t)
While i < len
*buf\a[i / 2] = pythagore(abs8(*buf\a[i]), abs8(*buf\a[i + 1]))
i + 2
Wend
;returns new buffer length
ProcedureReturn len / 2
EndProcedure
Procedure.i single_manchester(a.i, b.i, c.i, d.i)
Protected bit.i, bit_p.i
; takes 4 consecutive samples, return 0 or 1, #BADSAMPLE on error
If a > b
bit_p = 1
Else
bit_p = 0
EndIf
If c > d
bit = 1
Else
bit = 0
EndIf
If quality = 0.0
; no sanity check
ProcedureReturn bit
EndIf
If quality = 0.5
; sanity check half-bit
If ((bit = 1) And (bit_p = 1) And (b > c))
ProcedureReturn #BAD_SAMPLE
EndIf
If ((bit = 0) And (bit_p = 0) And (b < c))
ProcedureReturn #BAD_SAMPLE
EndIf
ProcedureReturn bit
EndIf
If quality = 1.0
; sanity check one bit
If ((bit = 1) And (bit_p = 1) And (c > b))
ProcedureReturn 1
EndIf
If ((bit = 1) And (bit_p = 0) And (d < b))
ProcedureReturn 1
EndIf
If ((bit = 0) And (bit_p = 1) And (d > b))
ProcedureReturn 0
EndIf
If ((bit = 0) And (bit_p = 0) And (c < b))
ProcedureReturn 0
EndIf
ProcedureReturn #BAD_SAMPLE
EndIf
; Sanity check two bits
If ((bit = 1) And (bit_p = 1) And (c > b) And (d < a))
ProcedureReturn 1
EndIf
If ((bit = 1) And (bit_p = 0) And (c > a) And (d < b))
ProcedureReturn 1
EndIf
If ((bit = 0) And (bit_p = 1) And (c < a) And (d > b))
ProcedureReturn 0
EndIf
If ((bit = 0) And (bit_p = 0) And (c < b) And (d > a))
ProcedureReturn 0
EndIf
ProcedureReturn #BAD_SAMPLE
EndProcedure
Procedure.i preamble(*buf.ByteArray_Structure, len.i, i.i)
Protected i2.i
Protected low.a, high.a
low.a = 0
high.a = 255
; Returns 0/1 for preamble at index i
For i2 = 0 To #PREAMBLE_LEN - 1
Select i2
Case 0,2,7,9
high = *buf\a[i + i2]
Default
low = *buf\a[i + i2]
EndSelect
If high <= low
ProcedureReturn 0
EndIf
Next i2
; Preamble sequence found
ProcedureReturn 1
EndProcedure
Procedure manchester(*buf.ByteArray_Structure, len.i)
Protected a.a, b.a, bit.a
Protected i.i, i2.i, errors.i
; Overwrites magnitude buffer with valid bits (#BADSAMPLE on errors)
While (i < len)
; Find preamble sequence
While i < len - #PREAMBLE_LEN
If preamble(*buf, len, i) = 0
i + 1
Continue
EndIf
a = *buf\a[i]
b = *buf\a[i + 1]
For i2 = 0 To #PREAMBLE_LEN - 1
*buf\a[i + i2] = #MESSAGEGO
Next i2
i = i + #PREAMBLE_LEN
Break
i + 1
Wend
i2 = i
errors = 0
; Mark bits until encoding breaks
While i < len
bit = single_manchester(a, b, *buf\a[i], *buf\a[i + 1])
a = *buf\a[i]
b = *buf\a[i + 1]
If bit = #BAD_SAMPLE
errors + 1
If errors > allowed_errors
*buf\a[i2] = #BAD_SAMPLE
Break
Else
If a > b
bit = 1
Else
bit = 0
EndIf
a = 0
b = 255
EndIf
EndIf
*buf\a[i] = #OVERWRITE
*buf\a[i + 1] = #OVERWRITE
*buf\a[i2] = bit
i + 2
i2 + 1
Wend
Wend
EndProcedure
Procedure display(*frame.ByteArray_Structure, len.i)
Protected i.i, df.i
Protected value.a, icao.a, pcode.a, tcode.a, stype.a
Protected test.b
If (short_output = 0) And (len <= #SHORT_FRAME)
ProcedureReturn
EndIf
df = (*frame\a[0] >> 3) & $1F
If Quality = 0.0 And Not ((df = 11) Or (df = 17) Or (df = 18) Or (df = 19))
ProcedureReturn
EndIf
Print("*")
For i = 0 To ((len + 7) / 8) - 1
Print(RSet(Hex(*frame\a[i], #PB_Ascii), 2, "0"))
Next i
PrintN("")
If verbose_output = 1
PrintN(FormatDate(" %yyyy/%mm/%mm/%dd %hh:%ii:%ss", Date()))
Print(" DF=" + Str(df) + " CA=" + Str(*frame\a[0] & $07))
PrintN(", ICAO=" + RSet(Hex(*frame\a[1] << 16 | (*frame\a[2] << 8) | *frame\a[3]), 6, "0"))
EndIf
If len <= #SHORT_FRAME
ProcedureReturn
EndIf
If verbose_output = 1
PrintN(" PI_code=" + RSet(Hex(*frame\a[11] << 16 | (*frame\a[12] << 8) | *frame\a[13]), 6, "0"))
PrintN(" Type_code=" + Str((*frame\a[4] >> 3) & $1F) + " S_type/ant=" + Str(*frame\a[4] & $07))
PrintN("")
EndIf
EndProcedure
Procedure outmessages(*buf.ByteArray_Structure, len.i)
Protected.i i, data_i, index, shift, frame_len
Protected.a val_shift
While i < len
If *buf\a[i] > 1
i + 1
Continue
EndIf
frame_len = #LONG_FRAME
data_i = 0
For index = 0 To 13
adsb_frame(index) = 0
Next index
While (i < len) And (*buf\a[i] <= 1) And (data_i < frame_len)
If *buf\a[i] = 1
index = data_i / 8
shift = 7 - (data_i % 8)
adsb_frame(index) = adsb_frame(index) | 1 << shift
EndIf
If data_i = 7
If adsb_frame(0) = 0
Break
EndIf
If adsb_frame(0) & $80 <> 0
frame_len = #LONG_FRAME
Else
frame_len = #SHORT_FRAME
EndIf
EndIf
i + 1
data_i + 1
Wend
If data_i < frame_len - 1
i + 1
Continue
EndIf
display(@adsb_frame(0), frame_len)
i + 1
Wend
EndProcedure
Procedure ThreadProc(*Parameter.ThreadParameter_Structure)
Protected len.i
Protected WaitReturn.i
Repeat
WaitSemaphore(hSem)
If Not *Parameter\Exit
; CopyMemory (source, destination, taille)
CopyMemory(@buf(0), @thread_buf(0), #DEFAULT_BUF_LENGTH)
len = magnitude(@thread_buf(0), #DEFAULT_BUF_LENGTH)
manchester(@thread_buf(0), len)
outmessages(@thread_buf(0), len)
EndIf
Until *Parameter\Exit
;rtlsdr_cancel_async(*dev)
EndProcedure
; ------- Main -------
Define ThreadParameter.ThreadParameter_Structure
OpenConsole()
If UseLibrtlsdr()
; handle semaphore
hSem = CreateSemaphore()
pythagore_precompute()
device_count = rtlsdr_get_device_count()
If Not device_count
PrintN("No supported devices found.")
End 1
EndIf
PrintN("Found " + Str(device_count) + " device(s)")
For i = 0 To device_count - 1
PrintN(" " + Str(i) + ": " + PeekS(rtlsdr_get_device_name(i), -1, #PB_UTF8))
Next i
PrintN("")
PrintN("Using device " + Str(dev_index) + ": " + PeekS(rtlsdr_get_device_name(dev_index), -1, #PB_UTF8))
r = rtlsdr_open(@*dev, dev_index)
If r < 0
PrintN("Failed to open rtlsdr device #" + Str(dev_index))
End 1
EndIf
PrintN("")
rtlsdr_set_center_freq(*dev, #ADSB_FREQ)
rtlsdr_set_sample_rate(*dev, #SDR_SRATE)
rtlsdr_set_tuner_gain(*dev, #SDR_GAIN)
rtlsdr_set_agc_mode(*dev, 1)
PrintN("Tuned to "+ rtlsdr_get_center_freq(*dev) + " Hz")
PrintN("Tuner gain: "+ rtlsdr_get_tuner_gain(*dev))
r = rtlsdr_reset_buffer(*dev)
If r < 0
PrintN("Failed to reset buffer")
EndIf
PrintN("Reading samples in Async mode")
PrintN("")
ThreadParameter\Thread = CreateThread(@ThreadProc(), @ThreadParameter)
;MessageRequester("Info","ThreadProc running")
r = rtlsdr_read_async(*dev, @ReadBuffer(), #Null, 32, #DEFAULT_BUF_LENGTH)
If r < 0
PrintN("Buffer Async read failed\n")
EndIf
; Can't reach this point after rtlsdr_read_async
rtlsdr_close(*dev)
ThreadParameter\Exit = #True
SignalSemaphore(hSem)
If WaitThread(ThreadParameter\Thread, 1000) = 0
KillThread(ThreadParameter\Thread)
EndIf
If hSem
FreeSemaphore(hSem)
EndIf
If r >= 0
End r
Else
End -r
EndIf
EndIf
CompilerIf #PB_Compiler_Debugger
Input()
CompilerEndIf
CloseConsole()
Last edited by infratec on Tue Jun 17, 2025 7:51 am, edited 3 times in total.
- vertexview
- User
- Posts: 28
- Joined: Thu Jan 25, 2024 8:33 am
- Location: France
Re: DLL wrapper for RTL-SDR
Thank you very much infratec. I tried your code.
It compiled without any difficulty with two modifications:
1) Global Dim pythagore.a(127,127) is replaced with Global Dim pythagore.a(128,128)
Else, i have [Error] Array index out of bounds.
2) A conditional check is added in TreadProc(*Dummy) procedure for semaphore validity as below.
Else, i have [Error] The specified semaphore does not exist.
The console is launched without problem, dongle is recognized, and thread is created, but no data processed.
ReadBuffer procedure seems to be never called (Semaphore not created, no CopyMemory)
It compiled without any difficulty with two modifications:
1) Global Dim pythagore.a(127,127) is replaced with Global Dim pythagore.a(128,128)
Else, i have [Error] Array index out of bounds.
2) A conditional check is added in TreadProc(*Dummy) procedure for semaphore validity as below.
Else, i have [Error] The specified semaphore does not exist.
Code: Select all
Procedure ThreadProc(*Dummy)
Protected len.i
Protected WaitReturn.i
While #True
If hSem
WaitSemaphore(hSem)
; CopyMemory (source, destination, taille)
CopyMemory(@buf(0), @thread_buf(0), #DEFAULT_BUF_LENGTH)
len = magnitude(@thread_buf(0), #DEFAULT_BUF_LENGTH)
manchester(@thread_buf(0), len)
outmessages(@thread_buf(0), len)
FreeSemaphore(hSem)
EndIf
Wend
;rtlsdr_cancel_async(*dev)
EndProcedure
ReadBuffer procedure seems to be never called (Semaphore not created, no CopyMemory)
Re: DLL wrapper for RTL-SDR
You should probably read first how Semaphores in PB are supposed to work.
I see no single SignalSemaphore in your code, which means WaitSemaphore will wait forever.
It also doesn't make sense to create the semaphore and free it after first use (well, in theory, yours is never used).
I see no single SignalSemaphore in your code, which means WaitSemaphore will wait forever.
It also doesn't make sense to create the semaphore and free it after first use (well, in theory, yours is never used).
{Home}.:|:.{Dialog Design0R}.:|:.{Codes}.:|:.{History Viewer Online}.:|:.{Send a Beer}
Re: DLL wrapper for RTL-SDR
Code: Select all
r = rtlsdr_read_async(*dev, @ReadBuffer(), 0, 32, #DEFAULT_BUF_LENGTH)
Add a Debug in the first line of ReadBuffer().
The semaphore is created in ReadBuffer().
You need to reach this procedure.
- vertexview
- User
- Posts: 28
- Joined: Thu Jan 25, 2024 8:33 am
- Location: France
Re: DLL wrapper for RTL-SDR
I added two Debug lines in ReadBuffer() procedure.
. The first debug line test is periodically reached, so the Callback is working.
. The second debug line is never reached, so no CopyMemory action nor Semaphore creation.
. The first debug line test is periodically reached, so the Callback is working.
. The second debug line is never reached, so no CopyMemory action nor Semaphore creation.
Code: Select all
Procedure.i ReadBuffer(*buffer, size.i, *ctx)
Debug("ReadBuffer procedure called")
If *ctx = #Null
ProcedureReturn 0
EndIf
; CopyMemory in Delphi (Destination: Pointer; Source: Pointer; Length: DWORD)
; CopyMemory in Purebasic (*Source, *Destination, Size)
CopyMemory(*ctx, @buf(0), size)
; handle semaphore
hSem = CreateSemaphore()
Debug("MemCopy done and Semaphore created")
ProcedureReturn 1
EndProcedure
Re: DLL wrapper for RTL-SDR
Wrong place for the second debug.
Place it before ProcedureReturn 0
Place it before ProcedureReturn 0
- vertexview
- User
- Posts: 28
- Joined: Thu Jan 25, 2024 8:33 am
- Location: France
Re: DLL wrapper for RTL-SDR
New Debug line added
In Debug console I have
ReadBuffer procedure called
ProcedureReturn 0 debug
ReadBuffer procedure called
ProcedureReturn 0 debug
....
Code: Select all
Procedure.i ReadBuffer(*buffer, size.i, *ctx)
Debug("ReadBuffer procedure called")
If *ctx = #Null
Debug("ProcedureReturn 0 debug")
ProcedureReturn 0
EndIf
; CopyMemory in Delphi (Destination: Pointer; Source: Pointer; Length: DWORD)
; CopyMemory in Purebasic (*Source, *Destination, Size)
CopyMemory(*ctx, @buf(0), size)
; handle semaphore
hSem = CreateSemaphore()
Debug("MemCopy done and Semaphore created")
ProcedureReturn 1
EndProcedure
ReadBuffer procedure called
ProcedureReturn 0 debug
ReadBuffer procedure called
ProcedureReturn 0 debug
....