Page 2 of 3

Re: DLL wrapper for RTL-SDR

Posted: Sat Jan 27, 2024 7:19 pm
by infratec
I modified the listing above.

Re: DLL wrapper for RTL-SDR

Posted: Sat Jan 27, 2024 10:38 pm
by vertexview
It's all good :D

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>

Re: DLL wrapper for RTL-SDR

Posted: Sun Jan 28, 2024 3:56 pm
by vertexview
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

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

Re: DLL wrapper for RTL-SDR

Posted: Sun Jan 28, 2024 7:55 pm
by infratec
According to the original code,

Code: Select all

rtlsdr_open() 
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.

Re: DLL wrapper for RTL-SDR

Posted: Mon Jan 29, 2024 7:55 pm
by vertexview
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

Re: DLL wrapper for RTL-SDR

Posted: Mon Mar 11, 2024 3:42 pm
by vertexview
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.

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()

Re: DLL wrapper for RTL-SDR

Posted: Sun Jun 15, 2025 6:09 pm
by vertexview
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:

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);
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:

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)
Here is the whole code

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()  
Thanks for your help, if possible.

Re: DLL wrapper for RTL-SDR

Posted: Sun Jun 15, 2025 7:36 pm
by infratec
There are so many things wrong ... :cry:

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

Posted: Sun Jun 15, 2025 8:55 pm
by infratec
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()

Re: DLL wrapper for RTL-SDR

Posted: Mon Jun 16, 2025 9:35 am
by vertexview
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.

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
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)

Re: DLL wrapper for RTL-SDR

Posted: Mon Jun 16, 2025 9:48 am
by HeX0R
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).

Re: DLL wrapper for RTL-SDR

Posted: Mon Jun 16, 2025 1:45 pm
by infratec

Code: Select all

r = rtlsdr_read_async(*dev, @ReadBuffer(), 0, 32, #DEFAULT_BUF_LENGTH)
Should add the callback.
Add a Debug in the first line of ReadBuffer().

The semaphore is created in ReadBuffer().
You need to reach this procedure.

Re: DLL wrapper for RTL-SDR

Posted: Mon Jun 16, 2025 2:55 pm
by vertexview
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.

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

Posted: Mon Jun 16, 2025 2:58 pm
by infratec
Wrong place for the second debug.

Place it before ProcedureReturn 0

Re: DLL wrapper for RTL-SDR

Posted: Mon Jun 16, 2025 3:29 pm
by vertexview
New Debug line added

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
In Debug console I have
ReadBuffer procedure called
ProcedureReturn 0 debug
ReadBuffer procedure called
ProcedureReturn 0 debug
....