SSB_HighSpeed_Modem/oscardata/oscardata/udp.cs

434 lines
13 KiB
C#
Executable File

/*
* 9/2020 (c) DJ0ABR, Kurt Moraw
* License: GPL 3.0
*
* udp.cs
* ------
*
* Creates a new thread which handles all incoming and outgoing UDP traffic.
* Communication to other threads is done via a thread-safe pipe.
* The UDP transmitter handles the correct datarate according to the modem speed.
*/
using System;
using System.Collections;
using System.Drawing;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace oscardata
{
public static class Udp
{
// this thread handles udp RX
static Thread udprx_thread;
static Thread udptx_thread;
// Pipes for data transferred via UDP ports
static UdpQueue uq_rx = new UdpQueue();
static UdpQueue uq_tx = new UdpQueue();
static UdpQueue uq_ctrl = new UdpQueue();
static UdpQueue uq_fft = new UdpQueue();
static UdpQueue uq_iq = new UdpQueue();
public static int searchtimeout = 0;
static String last_audiodevstring = "";
// Constructor
// called when Udp is created by the main program
public static void InitUdp()
{
// create thread for UDP RX
udprx_thread = new Thread(new ThreadStart(Udprxloop));
udprx_thread.Name = "Thread: oscardata UDP-RX";
udprx_thread.Start();
// create thread for UDP TX
udptx_thread = new Thread(new ThreadStart(Udptxloop));
udptx_thread.Name = "Thread: oscardata UDP-TX";
udptx_thread.Start();
}
public static void Close()
{
try
{
udprx_thread.Abort();
udptx_thread.Abort();
}
catch { }
}
// Udp RX Loop runs in its own thread
static void Udprxloop()
{
// define UDP port
UdpClient udpc = new UdpClient(statics.UdpRXport);
udpc.Client.ReceiveTimeout = 100;
while (statics.running)
{
try
{
// receive data from UDP port
IPEndPoint RemoteEndpoint = new IPEndPoint(IPAddress.Any, 0);
Byte[] rxarr = udpc.Receive(ref RemoteEndpoint);
if (rxarr != null)
{
// Data received:
// RemoteEndpoint.Address ... IP address of the sender
// RemoteEndpoint.Port ... port
// b[0] ... Type of data
// b+1 ... Byte array containing the data
int rxtype = rxarr[0];
Byte[] b = new byte[rxarr.Length - 1];
Array.Copy(rxarr, 1, b, 0, b.Length);
// payload
if (rxtype == statics.udp_payload)
{
//Console.WriteLine("payload");
uq_rx.Add(b);
}
// Broadcast response
if (rxtype == statics.udp_bc)
{
statics.ModemIP = RemoteEndpoint.Address.ToString();
searchtimeout = 0;
// message b contains audio devices and init status
statics.initAudioStatus = b[0];
statics.initVoiceStatus = b[1];
//String s = statics.ByteArrayToString(b,2);
String s = statics.ByteArrayToStringUtf8(b, 2);
String[] sa1 = s.Split(new char[] { '^' });
statics.AudioPBdevs = sa1[0].Split(new char[] { '~' });
statics.AudioCAPdevs = sa1[1].Split(new char[] { '~' });
// has the device list changed ?
if(s != last_audiodevstring)
{
statics.GotAudioDevices = 1;
last_audiodevstring = s;
}
if(statics.GotAudioDevices == 0)
statics.GotAudioDevices = 1;
}
// FFT data
if (rxtype == statics.udp_fft)
{
statics.PBfifousage = b[0];
statics.CAPfifousage = b[1];
statics.RXlevelDetected = b[2];
statics.RXinSync = b[3];
statics.maxRXlevel = b[4];
statics.maxTXlevel = b[5];
Byte[] b1 = new byte[b.Length - 4];
Array.Copy(b, 4, b1, 0, b1.Length);
uq_fft.Add(b1);
}
// IQ data
if (rxtype == statics.udp_iq)
{
//Console.WriteLine("IQ");
for (int i = 0; i < b.Length; i++)
{
// insert new byte in lastb
for (int sh = 12 - 1; sh > 0; sh--)
lastb[sh] = lastb[sh - 1];
lastb[0] = b[i];
// test if aligned
Int32 re = 0, im = 0;
if (lastb[0] == 0xe8 && lastb[1] == 3 && lastb[2] == 0 && lastb[3] == 0)
{
// we are aligned
re = lastb[7];
re <<= 8;
re += lastb[6];
re <<= 8;
re += lastb[5];
re <<= 8;
re += lastb[4];
im = lastb[11];
im <<= 8;
im += lastb[10];
im <<= 8;
im += lastb[9];
im <<= 8;
im += lastb[8];
}
drawBitmap(re, im);
}
}
}
}
catch { }
}
}
static int panelw = 75, panelh = 75;
static int maxdrawanz = 160;
static int drawanz = 0;
static Bitmap bm;
static void drawBitmap(Int32 re, Int32 im)
{
if (re == 0 && im == 0) return;
if (++drawanz >= maxdrawanz && uq_iq.Count() <= 1)
{
drawanz = 0;
uq_iq.Add(bm);
bm = new Bitmap(75, 75);
}
using (Graphics gr = Graphics.FromImage(bm))
{
// re and im are in the range of +/- 2^24 (16777216)
// scale it to +/- 128
double fre = re;
double fim = im;
fre = fre * panelw / 2 / 16777216.0;
fim = fim * panelh / 2 / 16777216.0;
// scale it to the picture
int x = panelw / 2 + (int)fre;
int y = panelh / 2 + (int)fim;
int et = 2;
gr.FillEllipse(Brushes.Blue, x - et, y - et, et * 2, et * 2);
}
}
static AutoResetEvent autoEvent = new AutoResetEvent(false);
// Udp TX Loop runs in its own thread
static void Udptxloop()
{
UdpClient udpc = new UdpClient();
// calculate cycle time for the requested data rate
// time in ms for one bit: 1000/statics.datarate
//int actdatarate = statics.getDatarate();
//int wait_datarate = (int)(((double)statics.UdpBlocklen * 8.0 * 1000.0 / (double)(statics.getDatarate())));
//Timer TTimer = new Timer(new TimerCallback(TXTickTimer), autoEvent, 0, wait_datarate);
while (statics.running)
{
bool wait = true;
if(uq_ctrl.Count() > 0)
{
// Control Message: send immediately
Byte[] b = uq_ctrl.Getarr();
udpc.Send(b, b.Length, statics.ModemIP, statics.UdpTXport);
wait = false;
}
if(statics.PBfifousage < 2)
{
// we need to send more payload data
if (uq_tx.Count() > 0)
{
Byte[] b = uq_tx.Getarr();
udpc.Send(b, b.Length, statics.ModemIP, statics.UdpTXport);
wait = false;
}
}
if (wait) Thread.Sleep(1);
}
}
public static void UdpBCsend(Byte[] b, String ip, int port)
{
UdpClient udpc = new UdpClient();
udpc.EnableBroadcast = true;
udpc.Send(b, b.Length, ip, port);
}
static void TXTickTimer(object stateInfo)
{
autoEvent = (AutoResetEvent)stateInfo;
autoEvent.Set();
}
// send a Byte array via UDP
// this function can be called from anywhere in the program
// it transfers the data to the udp-tx thread via a thread-safe pipe
public static void UdpSendData(Byte[] b)
{
uq_tx.Add(b);
}
public static void UdpSendCtrl(Byte[] b)
{
uq_ctrl.Add(b);
}
public static int GetBufferCount()
{
return uq_tx.Count();
}
public static int GetBufferCountCtrl()
{
return uq_ctrl.Count();
}
public static Byte[] UdpReceive()
{
if (uq_rx.Count() == 0) return null;
return uq_rx.Getarr();
}
public static UInt16[] UdpGetFFT()
{
if (uq_fft.Count() == 0) return null;
Byte[] d = uq_fft.Getarr();
UInt16[] varr = new UInt16[d.Length / 2];
int j = 0;
for (int i = 0; i < d.Length; i += 2)
{
if ((i + 1) >= d.Length) break;
UInt16 us = d[i];
us <<= 8;
us += d[i + 1];
if (j >= (varr.Length)) break;
varr[j++] = us;
}
return varr;
}
static Byte[] lastb = new Byte[12];
public static qpskitem UdpGetIQ()
{
if (uq_iq.Count() == 0) return null;
return uq_iq.GetQPSKitem();
}
public static Bitmap UdpBitmap()
{
if (uq_iq.Count() == 0) return null;
return uq_iq.GetBitmap();
}
public static bool IQavail()
{
if (uq_iq.Count() == 0) return false;
return true;
}
}
// this class is a thread safe queue wich is used
// to exchange data with the UDP RX/TX threads
public class UdpQueue
{
Queue myQ = new Queue();
public void Add(Byte [] b)
{
lock (myQ.SyncRoot)
{
myQ.Enqueue(b);
}
}
public void Add(qpskitem b)
{
lock (myQ.SyncRoot)
{
myQ.Enqueue(b);
}
}
public void Add(Bitmap bm)
{
lock (myQ.SyncRoot)
{
myQ.Enqueue(bm);
}
}
public Bitmap GetBitmap()
{
Bitmap b;
lock (myQ.SyncRoot)
{
b = (Bitmap)myQ.Dequeue();
}
return b;
}
public Byte[] Getarr()
{
Byte[] b;
lock (myQ.SyncRoot)
{
b = (Byte[])myQ.Dequeue();
}
return b;
}
public qpskitem GetQPSKitem()
{
qpskitem b;
lock (myQ.SyncRoot)
{
b = (qpskitem)myQ.Dequeue();
}
return b;
}
public qpskitem GetItem()
{
qpskitem b;
lock (myQ.SyncRoot)
{
b = (qpskitem)myQ.Dequeue();
}
return b;
}
public int Count()
{
int result;
lock (myQ.SyncRoot)
{
result = myQ.Count;
}
return result;
}
public void Clear()
{
lock (myQ.SyncRoot)
{
myQ.Clear();
}
}
}
public class qpskitem
{
public int re;
public int im;
}
}