mirror of
https://github.com/SDRSharpR/SDRSharper.git
synced 2025-12-06 04:12:02 +01:00
358 lines
6.2 KiB
C#
358 lines
6.2 KiB
C#
|
|
using SDRSharp.Radio;
|
||
|
|
using System;
|
||
|
|
using System.Net.Sockets;
|
||
|
|
using System.Threading;
|
||
|
|
using System.Timers;
|
||
|
|
using System.Windows.Forms;
|
||
|
|
|
||
|
|
namespace SDRSharp.RTLTCP
|
||
|
|
{
|
||
|
|
public class RtlTcpIO : IFrontendController, IDisposable
|
||
|
|
{
|
||
|
|
private const double DEFAULT_SAMPLE_RATE = 2048000.0;
|
||
|
|
|
||
|
|
private const long DEFAULT_FREQUENCY = 100000000L;
|
||
|
|
|
||
|
|
private const string DEFAULT_HOSTNAME = "192.168.0.11";
|
||
|
|
|
||
|
|
private const int DEFAULT_PORT = 1234;
|
||
|
|
|
||
|
|
private const byte CMD_SET_FREQ = 1;
|
||
|
|
|
||
|
|
private const byte CMD_SET_SAMPLE_RATE = 2;
|
||
|
|
|
||
|
|
private const byte CMD_SET_GAIN_MODE = 3;
|
||
|
|
|
||
|
|
private const byte CMD_SET_GAIN = 4;
|
||
|
|
|
||
|
|
private const byte CMD_SET_FREQ_COR = 5;
|
||
|
|
|
||
|
|
public const uint GAIN_MODE_AUTO = 0u;
|
||
|
|
|
||
|
|
public const uint GAIN_MODE_MANUAL = 1u;
|
||
|
|
|
||
|
|
private const int BUFFER_SIZE = 16384;
|
||
|
|
|
||
|
|
private const int MAX_TUNE_RATE = 20;
|
||
|
|
|
||
|
|
private RTLTcpSettings _gui;
|
||
|
|
|
||
|
|
private volatile SamplesAvailableDelegate _callback;
|
||
|
|
|
||
|
|
private Thread _sampleThread;
|
||
|
|
|
||
|
|
private long _freq;
|
||
|
|
|
||
|
|
private double _sr;
|
||
|
|
|
||
|
|
private Socket _s;
|
||
|
|
|
||
|
|
private string _host;
|
||
|
|
|
||
|
|
private int _port;
|
||
|
|
|
||
|
|
private uint _gainMode;
|
||
|
|
|
||
|
|
private int _gainVal;
|
||
|
|
|
||
|
|
private int _fCor;
|
||
|
|
|
||
|
|
private UnsafeBuffer _b;
|
||
|
|
|
||
|
|
private bool _tunePlease;
|
||
|
|
|
||
|
|
private System.Timers.Timer _retuneTimer = new System.Timers.Timer(50.0);
|
||
|
|
|
||
|
|
public string hostName
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
return this._host;
|
||
|
|
}
|
||
|
|
set
|
||
|
|
{
|
||
|
|
this._host = value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public int port
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
return this._port;
|
||
|
|
}
|
||
|
|
set
|
||
|
|
{
|
||
|
|
this._port = value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public bool IsSoundCardBased => false;
|
||
|
|
|
||
|
|
public string SoundCardHint => string.Empty;
|
||
|
|
|
||
|
|
public double Samplerate
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
return this._sr;
|
||
|
|
}
|
||
|
|
set
|
||
|
|
{
|
||
|
|
this._sr = value;
|
||
|
|
this.sendCommand(2, (uint)this._sr);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public long Frequency
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
return this._freq;
|
||
|
|
}
|
||
|
|
set
|
||
|
|
{
|
||
|
|
this._freq = value;
|
||
|
|
lock (this._retuneTimer)
|
||
|
|
{
|
||
|
|
this._tunePlease = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public int Gain
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
return this._gainVal;
|
||
|
|
}
|
||
|
|
set
|
||
|
|
{
|
||
|
|
this._gainVal = value;
|
||
|
|
this.sendCommand(4, this._gainVal);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public uint GainMode
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
return this._gainMode;
|
||
|
|
}
|
||
|
|
set
|
||
|
|
{
|
||
|
|
this._gainMode = value;
|
||
|
|
this.sendCommand(3, this._gainMode);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public int FreqCorrection
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
return this._fCor;
|
||
|
|
}
|
||
|
|
set
|
||
|
|
{
|
||
|
|
this._fCor = value;
|
||
|
|
this.sendCommand(5, this._fCor);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private bool sendCommand(byte cmd, byte[] val)
|
||
|
|
{
|
||
|
|
if (this._s == null)
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (val.Length < 4)
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
byte[] buffer = new byte[5]
|
||
|
|
{
|
||
|
|
cmd,
|
||
|
|
val[3],
|
||
|
|
val[2],
|
||
|
|
val[1],
|
||
|
|
val[0]
|
||
|
|
};
|
||
|
|
try
|
||
|
|
{
|
||
|
|
this._s.Send(buffer);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
Console.WriteLine(ex.ToString());
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private bool sendCommand(byte cmd, uint val)
|
||
|
|
{
|
||
|
|
byte[] bytes = BitConverter.GetBytes(val);
|
||
|
|
return this.sendCommand(cmd, bytes);
|
||
|
|
}
|
||
|
|
|
||
|
|
private bool sendCommand(byte cmd, int val)
|
||
|
|
{
|
||
|
|
byte[] bytes = BitConverter.GetBytes(val);
|
||
|
|
return this.sendCommand(cmd, bytes);
|
||
|
|
}
|
||
|
|
|
||
|
|
public RtlTcpIO()
|
||
|
|
{
|
||
|
|
this._freq = 100000000L;
|
||
|
|
this._sr = 2048000.0;
|
||
|
|
this._gainVal = 0;
|
||
|
|
this._gainMode = 0u;
|
||
|
|
this._host = "192.168.0.11";
|
||
|
|
this._port = 1234;
|
||
|
|
this._retuneTimer.Elapsed += this.retuneNow;
|
||
|
|
this._retuneTimer.Start();
|
||
|
|
}
|
||
|
|
|
||
|
|
~RtlTcpIO()
|
||
|
|
{
|
||
|
|
this.Dispose();
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Dispose()
|
||
|
|
{
|
||
|
|
if (this._gui != null)
|
||
|
|
{
|
||
|
|
this._gui.Dispose();
|
||
|
|
this._gui = null;
|
||
|
|
}
|
||
|
|
GC.SuppressFinalize(this);
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Open()
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Close()
|
||
|
|
{
|
||
|
|
if (this._s != null)
|
||
|
|
{
|
||
|
|
this._s.Close();
|
||
|
|
this._s = null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public unsafe void Start(SamplesAvailableDelegate callback)
|
||
|
|
{
|
||
|
|
lock (this)
|
||
|
|
{
|
||
|
|
this._callback = callback;
|
||
|
|
}
|
||
|
|
this._s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||
|
|
this._s.Connect(this._host, this._port);
|
||
|
|
this._sampleThread = new Thread((ThreadStart)delegate
|
||
|
|
{
|
||
|
|
this.receiveSamples();
|
||
|
|
});
|
||
|
|
this._sampleThread.Start();
|
||
|
|
this.sendCommand(3, this._gainMode);
|
||
|
|
this.sendCommand(2, (uint)this._sr);
|
||
|
|
this.sendCommand(1, (uint)this._freq);
|
||
|
|
this.sendCommand(5, this._fCor);
|
||
|
|
}
|
||
|
|
|
||
|
|
public unsafe void Stop()
|
||
|
|
{
|
||
|
|
lock (this)
|
||
|
|
{
|
||
|
|
this._callback = null;
|
||
|
|
}
|
||
|
|
this.Close();
|
||
|
|
if (this._sampleThread != null)
|
||
|
|
{
|
||
|
|
this._sampleThread.Join();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public void ShowSettingGUI(IWin32Window parent)
|
||
|
|
{
|
||
|
|
if (this._gui == null || this._gui.IsDisposed)
|
||
|
|
{
|
||
|
|
this._gui = new RTLTcpSettings(this);
|
||
|
|
}
|
||
|
|
this._gui.Show();
|
||
|
|
}
|
||
|
|
|
||
|
|
public void HideSettingGUI()
|
||
|
|
{
|
||
|
|
if (this._gui != null && !this._gui.IsDisposed)
|
||
|
|
{
|
||
|
|
this._gui.Hide();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private unsafe void receiveSamples()
|
||
|
|
{
|
||
|
|
byte[] buffer = new byte[17408];
|
||
|
|
int num = 0;
|
||
|
|
ulong num2 = 0uL;
|
||
|
|
DateTime now = DateTime.Now;
|
||
|
|
while (this._callback != null && this._s != null && this._s.Connected)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
int num3 = this._s.Receive(buffer, num, 16384, SocketFlags.None);
|
||
|
|
num2 = (ulong)((long)num2 + (long)num3);
|
||
|
|
int num4 = num + num3;
|
||
|
|
num = num4 % 2;
|
||
|
|
this.beamUpThemSamples(buffer, num4 - num);
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
Console.WriteLine(ex.ToString());
|
||
|
|
this.Close();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
DateTime now2 = DateTime.Now;
|
||
|
|
TimeSpan timeSpan = now2 - now;
|
||
|
|
double num5 = (double)num2 / timeSpan.TotalSeconds;
|
||
|
|
double num6 = num5 / 2.0;
|
||
|
|
Console.WriteLine($"Received {num2} bytes over {timeSpan.TotalSeconds} seconds, which is {num5} bps or {num6} sps");
|
||
|
|
}
|
||
|
|
|
||
|
|
private unsafe void beamUpThemSamples(byte[] buffer, int len)
|
||
|
|
{
|
||
|
|
int num = len / 2;
|
||
|
|
if (this._b == null || this._b.Length < num)
|
||
|
|
{
|
||
|
|
this._b = UnsafeBuffer.Create(num, sizeof(Complex));
|
||
|
|
}
|
||
|
|
Complex* ptr = (Complex*)(void*)this._b;
|
||
|
|
for (int i = 0; i < num; i++)
|
||
|
|
{
|
||
|
|
ptr[i].Real = (float)(buffer[i * 2 + 1] - 128) * 0.0078125f;
|
||
|
|
ptr[i].Imag = (float)(buffer[i * 2] - 128) * 0.0078125f;
|
||
|
|
}
|
||
|
|
lock (this)
|
||
|
|
{
|
||
|
|
if (this._callback != null)
|
||
|
|
{
|
||
|
|
this._callback(this, ptr, num);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void retuneNow(object source, ElapsedEventArgs e)
|
||
|
|
{
|
||
|
|
lock (this._retuneTimer)
|
||
|
|
{
|
||
|
|
if (this._tunePlease)
|
||
|
|
{
|
||
|
|
this.sendCommand(1, (uint)this._freq);
|
||
|
|
this._tunePlease = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|