mirror of
https://github.com/SDRSharpR/SDRSharper.git
synced 2025-12-06 04:12:02 +01:00
382 lines
11 KiB
C#
382 lines
11 KiB
C#
|
|
using SDRSharp.Radio;
|
||
|
|
using System;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using System.Drawing;
|
||
|
|
using System.Drawing.Drawing2D;
|
||
|
|
using System.Drawing.Imaging;
|
||
|
|
using System.Windows.Forms;
|
||
|
|
|
||
|
|
namespace SDRSharp.FrequencyScanner
|
||
|
|
{
|
||
|
|
public class ChannelAnalyzer : UserControl
|
||
|
|
{
|
||
|
|
private const float TrackingFontSize = 16f;
|
||
|
|
|
||
|
|
private const int CarrierPenWidth = 1;
|
||
|
|
|
||
|
|
private const int GradientAlpha = 180;
|
||
|
|
|
||
|
|
public const int AxisMargin = 2;
|
||
|
|
|
||
|
|
public const float DefaultCursorHeight = 32f;
|
||
|
|
|
||
|
|
private bool _drawBackgroundNeeded;
|
||
|
|
|
||
|
|
private Bitmap _bkgBuffer;
|
||
|
|
|
||
|
|
private Bitmap _buffer;
|
||
|
|
|
||
|
|
private Graphics _graphics;
|
||
|
|
|
||
|
|
private float _xIncrement;
|
||
|
|
|
||
|
|
private float _yIncrement;
|
||
|
|
|
||
|
|
private long _playFrequency;
|
||
|
|
|
||
|
|
private int _playFrequencyIndex;
|
||
|
|
|
||
|
|
private List<ChannelAnalizerMemoryEntry> _channelArray;
|
||
|
|
|
||
|
|
private int _zoom;
|
||
|
|
|
||
|
|
private int _zoomPosition;
|
||
|
|
|
||
|
|
private float _defXIncrement;
|
||
|
|
|
||
|
|
public long Frequency
|
||
|
|
{
|
||
|
|
set
|
||
|
|
{
|
||
|
|
this._playFrequency = value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public int Zoom
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
return this._zoom;
|
||
|
|
}
|
||
|
|
set
|
||
|
|
{
|
||
|
|
this._zoom = value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public int ZoomPosition
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
return this._zoomPosition;
|
||
|
|
}
|
||
|
|
set
|
||
|
|
{
|
||
|
|
this._zoomPosition = value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public event CustomPaintEventHandler CustomPaint;
|
||
|
|
|
||
|
|
public ChannelAnalyzer()
|
||
|
|
{
|
||
|
|
Rectangle clientRectangle = base.ClientRectangle;
|
||
|
|
int width = clientRectangle.Width;
|
||
|
|
clientRectangle = base.ClientRectangle;
|
||
|
|
this._bkgBuffer = new Bitmap(width, clientRectangle.Height, PixelFormat.Format32bppPArgb);
|
||
|
|
clientRectangle = base.ClientRectangle;
|
||
|
|
int width2 = clientRectangle.Width;
|
||
|
|
clientRectangle = base.ClientRectangle;
|
||
|
|
this._buffer = new Bitmap(width2, clientRectangle.Height, PixelFormat.Format32bppPArgb);
|
||
|
|
this._graphics = Graphics.FromImage(this._buffer);
|
||
|
|
base.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
|
||
|
|
base.SetStyle(ControlStyles.DoubleBuffer, true);
|
||
|
|
base.SetStyle(ControlStyles.UserPaint, true);
|
||
|
|
base.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
|
||
|
|
base.UpdateStyles();
|
||
|
|
}
|
||
|
|
|
||
|
|
~ChannelAnalyzer()
|
||
|
|
{
|
||
|
|
this._buffer.Dispose();
|
||
|
|
this._graphics.Dispose();
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Perform(List<ChannelAnalizerMemoryEntry> channelArray)
|
||
|
|
{
|
||
|
|
this._channelArray = channelArray;
|
||
|
|
if (this._drawBackgroundNeeded)
|
||
|
|
{
|
||
|
|
this.DrawBackground();
|
||
|
|
}
|
||
|
|
this.DrawForeground();
|
||
|
|
base.Invalidate();
|
||
|
|
this._drawBackgroundNeeded = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void DrawBackground()
|
||
|
|
{
|
||
|
|
using (SolidBrush solidBrush = new SolidBrush(Color.Silver))
|
||
|
|
{
|
||
|
|
using (Pen pen = new Pen(Color.FromArgb(80, 80, 80)))
|
||
|
|
{
|
||
|
|
using (new Pen(Color.DarkGray))
|
||
|
|
{
|
||
|
|
using (Font font = new Font("Arial", 8f))
|
||
|
|
{
|
||
|
|
using (Graphics graphics = Graphics.FromImage(this._bkgBuffer))
|
||
|
|
{
|
||
|
|
ChannelAnalyzer.ConfigureGraphics(graphics);
|
||
|
|
graphics.Clear(Color.Black);
|
||
|
|
pen.DashStyle = DashStyle.Dash;
|
||
|
|
int num = 20;
|
||
|
|
Rectangle clientRectangle = base.ClientRectangle;
|
||
|
|
int num2 = (clientRectangle.Height - 4) / num;
|
||
|
|
for (int i = 1; i <= num2; i++)
|
||
|
|
{
|
||
|
|
Graphics graphics2 = graphics;
|
||
|
|
Pen pen2 = pen;
|
||
|
|
clientRectangle = base.ClientRectangle;
|
||
|
|
int y = clientRectangle.Height - 2 - i * num;
|
||
|
|
clientRectangle = base.ClientRectangle;
|
||
|
|
int x = clientRectangle.Width - 2;
|
||
|
|
clientRectangle = base.ClientRectangle;
|
||
|
|
graphics2.DrawLine(pen2, 2, y, x, clientRectangle.Height - 2 - i * num);
|
||
|
|
}
|
||
|
|
for (int j = 1; j <= num2; j++)
|
||
|
|
{
|
||
|
|
string text = (j * num / 2 - 10).ToString();
|
||
|
|
SizeF sizeF = graphics.MeasureString(text, font);
|
||
|
|
float width = sizeF.Width;
|
||
|
|
float height = sizeF.Height;
|
||
|
|
Graphics graphics3 = graphics;
|
||
|
|
string s = text;
|
||
|
|
Font font2 = font;
|
||
|
|
SolidBrush brush = solidBrush;
|
||
|
|
clientRectangle = base.ClientRectangle;
|
||
|
|
graphics3.DrawString(s, font2, brush, 4f, (float)(clientRectangle.Height - 2 - j * num) - height + 1f);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private string GetFrequencyDisplay(long frequency)
|
||
|
|
{
|
||
|
|
if (frequency == 0L)
|
||
|
|
{
|
||
|
|
return "DC";
|
||
|
|
}
|
||
|
|
if (Math.Abs(frequency) > 1500000000)
|
||
|
|
{
|
||
|
|
return $"{(double)frequency / 1000000000.0:#,0.000 000}GHz";
|
||
|
|
}
|
||
|
|
if (Math.Abs(frequency) > 30000000)
|
||
|
|
{
|
||
|
|
return $"{(double)frequency / 1000000.0:0,0.000###}MHz";
|
||
|
|
}
|
||
|
|
if (Math.Abs(frequency) > 1000)
|
||
|
|
{
|
||
|
|
return $"{(double)frequency / 1000.0:#,#.###}kHz";
|
||
|
|
}
|
||
|
|
return $"{frequency}Hz";
|
||
|
|
}
|
||
|
|
|
||
|
|
public static void ConfigureGraphics(Graphics graphics)
|
||
|
|
{
|
||
|
|
graphics.CompositingMode = CompositingMode.SourceOver;
|
||
|
|
graphics.CompositingQuality = CompositingQuality.HighSpeed;
|
||
|
|
graphics.SmoothingMode = SmoothingMode.None;
|
||
|
|
graphics.PixelOffsetMode = PixelOffsetMode.HighSpeed;
|
||
|
|
graphics.InterpolationMode = InterpolationMode.High;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void DrawForeground()
|
||
|
|
{
|
||
|
|
Rectangle clientRectangle = base.ClientRectangle;
|
||
|
|
if (clientRectangle.Width > 2)
|
||
|
|
{
|
||
|
|
clientRectangle = base.ClientRectangle;
|
||
|
|
if (clientRectangle.Height > 2)
|
||
|
|
{
|
||
|
|
this.CopyBackground();
|
||
|
|
this.DrawSpectrum();
|
||
|
|
this.OnCustomPaint(new CustomPaintEventArgs(this._graphics));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private unsafe void CopyBackground()
|
||
|
|
{
|
||
|
|
BitmapData bitmapData = this._buffer.LockBits(base.ClientRectangle, ImageLockMode.WriteOnly, this._buffer.PixelFormat);
|
||
|
|
BitmapData bitmapData2 = this._bkgBuffer.LockBits(base.ClientRectangle, ImageLockMode.ReadOnly, this._bkgBuffer.PixelFormat);
|
||
|
|
Utils.Memcpy((void*)bitmapData.Scan0, (void*)bitmapData2.Scan0, Math.Abs(bitmapData.Stride) * bitmapData.Height);
|
||
|
|
this._buffer.UnlockBits(bitmapData);
|
||
|
|
this._bkgBuffer.UnlockBits(bitmapData2);
|
||
|
|
}
|
||
|
|
|
||
|
|
private void DrawSpectrum()
|
||
|
|
{
|
||
|
|
if (this._channelArray != null && this._channelArray.Count != 0)
|
||
|
|
{
|
||
|
|
Rectangle clientRectangle = base.ClientRectangle;
|
||
|
|
this._xIncrement = (float)(clientRectangle.Width - 4) / (float)this._channelArray.Count * (float)(this._zoom + 1);
|
||
|
|
this._yIncrement = 2f;
|
||
|
|
clientRectangle = base.ClientRectangle;
|
||
|
|
this._defXIncrement = (float)(clientRectangle.Width - 4) / (float)this._channelArray.Count;
|
||
|
|
float num = 0f;
|
||
|
|
float num2 = 0f;
|
||
|
|
this._playFrequencyIndex = -1;
|
||
|
|
using (Pen pen = new Pen(Color.SkyBlue))
|
||
|
|
{
|
||
|
|
using (Pen pen4 = new Pen(Color.DarkRed))
|
||
|
|
{
|
||
|
|
using (Pen pen5 = new Pen(Color.Yellow))
|
||
|
|
{
|
||
|
|
using (Pen pen3 = new Pen(Color.Green))
|
||
|
|
{
|
||
|
|
for (int i = 0; i < this._channelArray.Count; i++)
|
||
|
|
{
|
||
|
|
float num3 = (float)Math.Max((int)this._channelArray[i].Level, 4) * this._yIncrement;
|
||
|
|
float num4 = this._xIncrement - 1f;
|
||
|
|
if (this._xIncrement < 3f)
|
||
|
|
{
|
||
|
|
num4 = this._xIncrement;
|
||
|
|
}
|
||
|
|
float num5 = 2f + (float)i * this._xIncrement - (float)(this._zoomPosition * (this._zoom + 1)) + (float)this._zoomPosition + num4 * 0.5f;
|
||
|
|
clientRectangle = base.ClientRectangle;
|
||
|
|
float num6 = (float)Math.Max(2, (int)((float)(clientRectangle.Height - 2) - num3));
|
||
|
|
float val = num3;
|
||
|
|
clientRectangle = base.ClientRectangle;
|
||
|
|
num3 = Math.Min(val, (float)(clientRectangle.Height - 4));
|
||
|
|
if (!(num5 < 2f))
|
||
|
|
{
|
||
|
|
float num7 = num5;
|
||
|
|
clientRectangle = base.ClientRectangle;
|
||
|
|
if (!(num7 > (float)(clientRectangle.Width - 2)))
|
||
|
|
{
|
||
|
|
Pen pen2 = pen;
|
||
|
|
if (this._playFrequency > this._channelArray[i].Frequency - this._channelArray[i].StepSize / 2 && this._playFrequency < this._channelArray[i].Frequency + this._channelArray[i].StepSize / 2)
|
||
|
|
{
|
||
|
|
num = num5;
|
||
|
|
num2 = num6;
|
||
|
|
this._playFrequencyIndex = i;
|
||
|
|
}
|
||
|
|
if (this._channelArray[i].IsStore)
|
||
|
|
{
|
||
|
|
pen2 = pen3;
|
||
|
|
}
|
||
|
|
if (this._channelArray[i].Skipped)
|
||
|
|
{
|
||
|
|
pen2 = ((!this._channelArray[i].IsStore) ? pen4 : pen5);
|
||
|
|
}
|
||
|
|
pen2.Width = num4;
|
||
|
|
this._graphics.DrawLine(pen2, num5, num6, num5, num6 + num3);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
using (SolidBrush brush = new SolidBrush(Color.White))
|
||
|
|
{
|
||
|
|
using (Pen pen6 = new Pen(Color.White))
|
||
|
|
{
|
||
|
|
using (Font font = new Font("Arial", 8f))
|
||
|
|
{
|
||
|
|
if (this._playFrequencyIndex >= 0 && this._playFrequencyIndex < this._channelArray.Count)
|
||
|
|
{
|
||
|
|
string str = this.GetFrequencyDisplay(this._channelArray[this._playFrequencyIndex].Frequency) + " ";
|
||
|
|
str = ((!this._channelArray[this._playFrequencyIndex].IsStore) ? (str + "Unknown") : (str + this._channelArray[this._playFrequencyIndex].StoreName));
|
||
|
|
SizeF sizeF = this._graphics.MeasureString(str, font);
|
||
|
|
float width = sizeF.Width;
|
||
|
|
float height = sizeF.Height;
|
||
|
|
float num8 = 0f;
|
||
|
|
int num9 = 62;
|
||
|
|
this._graphics.DrawLine(pen6, num, num2 - 10f, num, (float)num9 + height + 4f);
|
||
|
|
float num10 = num + width + 10f;
|
||
|
|
clientRectangle = base.ClientRectangle;
|
||
|
|
num8 = ((!(num10 > (float)clientRectangle.Width)) ? num : (num - width));
|
||
|
|
this._graphics.DrawLine(pen6, num8, (float)num9 + height + 4f, num8 + width, (float)num9 + height + 4f);
|
||
|
|
this._graphics.DrawString(str, font, brush, num8, (float)num9);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override void OnPaint(PaintEventArgs e)
|
||
|
|
{
|
||
|
|
ChannelAnalyzer.ConfigureGraphics(e.Graphics);
|
||
|
|
e.Graphics.DrawImageUnscaled(this._buffer, 0, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override void OnResize(EventArgs e)
|
||
|
|
{
|
||
|
|
base.OnResize(e);
|
||
|
|
Rectangle clientRectangle = base.ClientRectangle;
|
||
|
|
if (clientRectangle.Width > 0)
|
||
|
|
{
|
||
|
|
clientRectangle = base.ClientRectangle;
|
||
|
|
if (clientRectangle.Height > 0)
|
||
|
|
{
|
||
|
|
this._buffer.Dispose();
|
||
|
|
this._graphics.Dispose();
|
||
|
|
this._bkgBuffer.Dispose();
|
||
|
|
clientRectangle = base.ClientRectangle;
|
||
|
|
int width = clientRectangle.Width;
|
||
|
|
clientRectangle = base.ClientRectangle;
|
||
|
|
this._buffer = new Bitmap(width, clientRectangle.Height, PixelFormat.Format32bppPArgb);
|
||
|
|
this._graphics = Graphics.FromImage(this._buffer);
|
||
|
|
ChannelAnalyzer.ConfigureGraphics(this._graphics);
|
||
|
|
clientRectangle = base.ClientRectangle;
|
||
|
|
int width2 = clientRectangle.Width;
|
||
|
|
clientRectangle = base.ClientRectangle;
|
||
|
|
this._bkgBuffer = new Bitmap(width2, clientRectangle.Height, PixelFormat.Format32bppPArgb);
|
||
|
|
this._drawBackgroundNeeded = true;
|
||
|
|
this.Perform(this._channelArray);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
protected virtual void OnCustomPaint(CustomPaintEventArgs e)
|
||
|
|
{
|
||
|
|
CustomPaintEventHandler customPaint = this.CustomPaint;
|
||
|
|
customPaint?.Invoke(this, e);
|
||
|
|
}
|
||
|
|
|
||
|
|
public int CurrentChannelIndex()
|
||
|
|
{
|
||
|
|
return this._playFrequencyIndex;
|
||
|
|
}
|
||
|
|
|
||
|
|
public int PointToChannel(float point)
|
||
|
|
{
|
||
|
|
int num = 0;
|
||
|
|
num = (int)(((float)(this._zoomPosition * (this._zoom + 1)) + point - (float)this._zoomPosition - 2f) / this._xIncrement);
|
||
|
|
if (num < 0)
|
||
|
|
{
|
||
|
|
num = 0;
|
||
|
|
}
|
||
|
|
if (num >= this._channelArray.Count)
|
||
|
|
{
|
||
|
|
num = this._channelArray.Count - 1;
|
||
|
|
}
|
||
|
|
return num;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void InitializeComponent()
|
||
|
|
{
|
||
|
|
base.SuspendLayout();
|
||
|
|
this.AutoSize = true;
|
||
|
|
this.DoubleBuffered = true;
|
||
|
|
base.Name = "ChannelAnalyzer";
|
||
|
|
base.ResumeLayout(false);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|