mirror of
https://github.com/dj0abr/SSB_HighSpeed_Modem.git
synced 2024-09-27 07:36:42 -04:00
431 lines
13 KiB
C#
Executable File
431 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[] 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];
|
|
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;
|
|
}
|
|
}
|