diff --git a/Build Order.txt b/Build Order.txt
new file mode 100644
index 0000000..2be7d58
--- /dev/null
+++ b/Build Order.txt
@@ -0,0 +1,6 @@
+Radio
+PanView
+Common
+CollaspiblePanel
+FrequencyEdit
+SDRSharp
\ No newline at end of file
diff --git a/Core Includes/BandPlan.xml b/Core Includes/BandPlan.xml
new file mode 100644
index 0000000..eed4739
--- /dev/null
+++ b/Core Includes/BandPlan.xml
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Long Wave
+ Medium Wave
+ Shortwave Broadcast
+ Shortwave Broadcast
+ Shortwave Broadcast
+ Shortwave Broadcast
+ Shortwave Broadcast
+ Shortwave Broadcast
+ Shortwave Broadcast
+ Shortwave Broadcast
+ Shortwave Broadcast
+ Shortwave Broadcast
+ Shortwave Broadcast
+ Shortwave Broadcast
+ Shortwave Broadcast
+ Shortwave Broadcast
+ Shortwave Broadcast
+ FM Broadcast
+ Air Band VOR/ILS
+ Air Band Voice
+ 160m Ham Band
+ 80m Ham Band
+ 60m Ham Band
+ 40m Ham Band
+
+ 30m Ham Band
+ 20m Ham Band
+ 17m Ham Band
+ 15m Ham Band
+ 12m Ham Band
+ CB
+ 10m Ham Band
+ 6m Ham Band
+ 2m Ham Band
+
+
+
+
+
+ Marine
+ 1.25m Ham Band
+ Military Air
+ Mil Sat
+ 70cm Ham Band
+
+
+
+ PMR446
+ 33cm Ham Band
+
+
+
+
+ 23cm Ham Band
+
+
+ 13cm Ham Band
+ 13cm Ham Band
+
\ No newline at end of file
diff --git a/Core Includes/FrontEnds.xml b/Core Includes/FrontEnds.xml
new file mode 100644
index 0000000..388b916
--- /dev/null
+++ b/Core Includes/FrontEnds.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Core Includes/NDde.dll b/Core Includes/NDde.dll
new file mode 100644
index 0000000..1cd6ddd
Binary files /dev/null and b/Core Includes/NDde.dll differ
diff --git a/Core Includes/Plugins.xml b/Core Includes/Plugins.xml
new file mode 100644
index 0000000..e5d98b1
--- /dev/null
+++ b/Core Includes/Plugins.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Core Includes/PortAudio.dll b/Core Includes/PortAudio.dll
new file mode 100644
index 0000000..4588ded
Binary files /dev/null and b/Core Includes/PortAudio.dll differ
diff --git a/Core Includes/SDRSharp.AfedriSDRNet.dll b/Core Includes/SDRSharp.AfedriSDRNet.dll
new file mode 100644
index 0000000..ca76364
Binary files /dev/null and b/Core Includes/SDRSharp.AfedriSDRNet.dll differ
diff --git a/Core Includes/SDRSharp.FUNcube.dll b/Core Includes/SDRSharp.FUNcube.dll
new file mode 100644
index 0000000..4e7ce39
Binary files /dev/null and b/Core Includes/SDRSharp.FUNcube.dll differ
diff --git a/Core Includes/SDRSharp.FUNcubeProPlus.dll b/Core Includes/SDRSharp.FUNcubeProPlus.dll
new file mode 100644
index 0000000..d15394d
Binary files /dev/null and b/Core Includes/SDRSharp.FUNcubeProPlus.dll differ
diff --git a/Core Includes/SDRSharp.HackRF.dll b/Core Includes/SDRSharp.HackRF.dll
new file mode 100644
index 0000000..5a589e0
Binary files /dev/null and b/Core Includes/SDRSharp.HackRF.dll differ
diff --git a/Core Includes/SDRSharp.RTLSDR.dll b/Core Includes/SDRSharp.RTLSDR.dll
new file mode 100644
index 0000000..fd3007c
Binary files /dev/null and b/Core Includes/SDRSharp.RTLSDR.dll differ
diff --git a/Core Includes/SDRSharp.RTLTCP.dll b/Core Includes/SDRSharp.RTLTCP.dll
new file mode 100644
index 0000000..e2a17e1
Binary files /dev/null and b/Core Includes/SDRSharp.RTLTCP.dll differ
diff --git a/Core Includes/SDRSharp.SDRIP.dll b/Core Includes/SDRSharp.SDRIP.dll
new file mode 100644
index 0000000..0c8cedd
Binary files /dev/null and b/Core Includes/SDRSharp.SDRIP.dll differ
diff --git a/Core Includes/SDRSharp.SDRIQ.dll b/Core Includes/SDRSharp.SDRIQ.dll
new file mode 100644
index 0000000..c19a368
Binary files /dev/null and b/Core Includes/SDRSharp.SDRIQ.dll differ
diff --git a/Core Includes/SDRSharp.SoftRock.dll b/Core Includes/SDRSharp.SoftRock.dll
new file mode 100644
index 0000000..1d58883
Binary files /dev/null and b/Core Includes/SDRSharp.SoftRock.dll differ
diff --git a/Core Includes/SRDLL.dll b/Core Includes/SRDLL.dll
new file mode 100644
index 0000000..fe5ba82
Binary files /dev/null and b/Core Includes/SRDLL.dll differ
diff --git a/Core Includes/airspy.dll b/Core Includes/airspy.dll
new file mode 100644
index 0000000..52b3806
Binary files /dev/null and b/Core Includes/airspy.dll differ
diff --git a/Core Includes/airspyhf.dll b/Core Includes/airspyhf.dll
new file mode 100644
index 0000000..6c0b89e
Binary files /dev/null and b/Core Includes/airspyhf.dll differ
diff --git a/Core Includes/digital_frequencies.xml b/Core Includes/digital_frequencies.xml
new file mode 100644
index 0000000..316a313
--- /dev/null
+++ b/Core Includes/digital_frequencies.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/Core Includes/frequencies.xml b/Core Includes/frequencies.xml
new file mode 100644
index 0000000..cbdc913
--- /dev/null
+++ b/Core Includes/frequencies.xml
@@ -0,0 +1,57 @@
+
+
+
+ true
+ Santa Ana
+ CHP
+ 39440000
+ WFM
+ 0
+ 10
+
+
+ true
+ NPR Radio
+ FM Radio
+ 89300000
+ WFM
+ 0
+ 75330
+
+
+ true
+ KFI AM
+ Misc
+ 115840000
+ AM
+ 0
+ 11130
+
+
+ true
+ Air
+ CHP
+ 122850000
+ AM
+ 0
+ 4240
+
+
+ true
+ NOAA - Oxnard
+ Weather
+ 162550000
+ NFM
+ 0
+ 24390
+
+
+ true
+ Conv. Center Security
+ Anaheim
+ 453225000
+ NFM
+ 0
+ 5000
+
+
\ No newline at end of file
diff --git a/Core Includes/hackrf.dll b/Core Includes/hackrf.dll
new file mode 100644
index 0000000..1eafa51
Binary files /dev/null and b/Core Includes/hackrf.dll differ
diff --git a/Core Includes/install-rtlsdr.bat b/Core Includes/install-rtlsdr.bat
new file mode 100644
index 0000000..039c1e0
--- /dev/null
+++ b/Core Includes/install-rtlsdr.bat
@@ -0,0 +1,17 @@
+@echo off
+
+mkdir tmp
+
+echo Downloading RTLSDR Driver
+httpget http://osmocom.org/attachments/download/2242/RelWithDebInfo.zip tmp\RelWithDebInfo.zip
+
+echo Downloading Zadig
+set zadig_exe=zadig.exe
+ver | findstr /l "5.1." > NUL
+if %errorlevel% equ 0 set zadig_exe=zadig_xp.exe
+httpget http://zadig.akeo.ie/downloads/%zadig_exe% %zadig_exe%
+
+unzip -o tmp\RelWithDebInfo.zip -d tmp
+move tmp\rtl-sdr-release\x32\rtlsdr.dll .
+
+rmdir tmp /S /Q
\ No newline at end of file
diff --git a/Core Includes/inverted_frequencies.xml b/Core Includes/inverted_frequencies.xml
new file mode 100644
index 0000000..71de7e8
--- /dev/null
+++ b/Core Includes/inverted_frequencies.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/Core Includes/libusb-1.0.dll b/Core Includes/libusb-1.0.dll
new file mode 100644
index 0000000..2f98c8c
Binary files /dev/null and b/Core Includes/libusb-1.0.dll differ
diff --git a/Core Includes/modesparser.dll b/Core Includes/modesparser.dll
new file mode 100644
index 0000000..37a2370
Binary files /dev/null and b/Core Includes/modesparser.dll differ
diff --git a/Core Includes/msvcr100.dll b/Core Includes/msvcr100.dll
new file mode 100644
index 0000000..fd91c89
Binary files /dev/null and b/Core Includes/msvcr100.dll differ
diff --git a/Core Includes/pthreadVCE2.dll b/Core Includes/pthreadVCE2.dll
new file mode 100644
index 0000000..9d148cc
Binary files /dev/null and b/Core Includes/pthreadVCE2.dll differ
diff --git a/Core Includes/rtlsdr.dll b/Core Includes/rtlsdr.dll
new file mode 100644
index 0000000..40cff2b
Binary files /dev/null and b/Core Includes/rtlsdr.dll differ
diff --git a/Core Includes/sdriq.dll b/Core Includes/sdriq.dll
new file mode 100644
index 0000000..08303c1
Binary files /dev/null and b/Core Includes/sdriq.dll differ
diff --git a/Core Includes/shark.dll b/Core Includes/shark.dll
new file mode 100644
index 0000000..60cdbee
Binary files /dev/null and b/Core Includes/shark.dll differ
diff --git a/SDRSharp.CollapsiblePanel/Properties/AssemblyInfo.cs b/SDRSharp.CollapsiblePanel/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..0bc2254
--- /dev/null
+++ b/SDRSharp.CollapsiblePanel/Properties/AssemblyInfo.cs
@@ -0,0 +1,15 @@
+using System.Diagnostics;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.Versioning;
+using System.Security.Permissions;
+
+[assembly: CompilationRelaxations(8)]
+[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
+[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
+[assembly: AssemblyTitle("Collapsible Panel")]
+[assembly: AssemblyDescription("Collapsible Panel Component")]
+[assembly: AssemblyProduct("SDR#")]
+[assembly: AssemblyCopyright("Copyright © Youssef TOUIL 2012")]
+[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
+[assembly: AssemblyVersion("0.0.0.0")]
diff --git a/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel.Properties/Resources.cs b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel.Properties/Resources.cs
new file mode 100644
index 0000000..34ed262
--- /dev/null
+++ b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel.Properties/Resources.cs
@@ -0,0 +1,82 @@
+using System.CodeDom.Compiler;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Drawing;
+using System.Globalization;
+using System.Resources;
+using System.Runtime.CompilerServices;
+
+namespace SDRSharp.CollapsiblePanel.Properties
+{
+ [GeneratedCode("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [DebuggerNonUserCode]
+ [CompilerGenerated]
+ internal class Resources
+ {
+ private static ResourceManager resourceMan;
+
+ private static CultureInfo resourceCulture;
+
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ internal static ResourceManager ResourceManager
+ {
+ get
+ {
+ if (Resources.resourceMan == null)
+ {
+ Resources.resourceMan = new ResourceManager("SDRSharp.CollapsiblePanel.Properties.Resources", typeof(Resources).Assembly);
+ }
+ return Resources.resourceMan;
+ }
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ internal static CultureInfo Culture
+ {
+ get
+ {
+ return Resources.resourceCulture;
+ }
+ set
+ {
+ Resources.resourceCulture = value;
+ }
+ }
+
+ internal static Bitmap CollapsedIcon
+ {
+ get
+ {
+ return (Bitmap)Resources.ResourceManager.GetObject("CollapsedIcon", Resources.resourceCulture);
+ }
+ }
+
+ internal static string ExpandedHeigth
+ {
+ get
+ {
+ return Resources.ResourceManager.GetString("ExpandedHeigth", Resources.resourceCulture);
+ }
+ }
+
+ internal static Bitmap ExpandedIcon
+ {
+ get
+ {
+ return (Bitmap)Resources.ResourceManager.GetObject("ExpandedIcon", Resources.resourceCulture);
+ }
+ }
+
+ internal static Bitmap titleBackground
+ {
+ get
+ {
+ return (Bitmap)Resources.ResourceManager.GetObject("titleBackground", Resources.resourceCulture);
+ }
+ }
+
+ internal Resources()
+ {
+ }
+ }
+}
diff --git a/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel.Properties/Resources.resource b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel.Properties/Resources.resource
new file mode 100644
index 0000000..5c113e8
--- /dev/null
+++ b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel.Properties/Resources.resource
@@ -0,0 +1,166 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAAA8AAAAMCAYAAAC9QufkAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAN
+ 1gAADdYBkG95nAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAACdSURBVChTlZGx
+ DcIwEEUtalZIBmEYZiGLULMAtUsGYICQNi0VEpL5D9nWRbYs50tP1t29X9mFELxYxGsH+J7yKGaxJ/gj
+ ZZjEV/QED9+l8kE8RE/w8HMZTmIVrXDH+3dsGa6iFe7Zt0U4iqeohT337Nti4izewoaZ/cbdDIa7sGEu
+ vGIRGUT6e17mwisWhov4xLd2b5b5y1t8K/fgfhf9ch0fZsVEAAAAAElFTkSuQmCC
+
+
+
+
+ /9j/4AAQSkZJRgABAQEAYABgAAD/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAEAAAAAAAD/2wBD
+ AAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0M
+ DgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM
+ DAwMDAwMDAwMDAwMDAz/wAARCAARALkDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQF
+ BgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAk
+ M2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWG
+ h4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx
+ 8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQA
+ AQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5
+ OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmq
+ srO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDz
+ f/hd6/3ovzH+FH/C71/vRfmP8K+Sv+F4v/z0lo/4Xi//AD0lrp9mZ859a/8AC71/vRfmP8KP+F3r/ei/
+ Mf4V8lf8Lxf/AJ6S0f8AC8X/AOektHsw5z61/wCF3r/ei/Mf4Uf8LvX+9F+Y/wAK+Sv+F4v/AM9JaP8A
+ heL/APPSWj2Yc59a/wDC71/vRfmP8KP+F3r/AHovzH+FfJX/AAvF/wDnpLR/wvF/+ektHsw5z61/4Xev
+ 96L8x/hR/wALvX+9F+Y/wr5K/wCF4v8A89JaP+F4v/z0lo9mHOfWv/C71/vRfmP8KP8Ahd6/3ovzH+Ff
+ JX/C8X/56S0f8Lxf/npLR7MOc+tf+F3r/ei/Mf4Uf8LvX+9F+Y/wr5K/4Xi//PSWj/heL/8APSWj2Yc5
+ 9a/8LvX+9F+Y/wAKP+F3r/ei/Mf4V8lf8Lxf/npLR/wvF/8AnpLR7MOc+tf+F3r/AHovzH+FH/C71/vR
+ fmP8K+Sv+F4v/wA9JaP+F4v/AM9JaPZhzn1r/wALvX+9F+Y/wo/4Xev96L8x/hXyV/wvF/8AnpLR/wAL
+ xf8A56S0ezDnPrX/AIXev96L8x/hR/wu9f70X5j/AAr5K/4Xi/8Az0lo/wCF4v8A89JaPZhzn1r/AMLv
+ X+9F+Y/wo/4Xev8Aei/Mf4V8lf8AC8X/AOektH/C8X/56S0ezDnPJKKKK5jQKKKKACiiigAooooAKKKK
+ ACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/2Q==
+
+
+
+ 0
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAAAwAAAAPCAYAAADQ4S5JAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAN
+ 1gAADdYBkG95nAAAAHRJREFUKFNj+P///z4GBgaZAwcOMBCDQRoeA/F9IG7CpgAdgzSAFIPAbyA+BsSW
+ 2BTCMLIGGHgNxPOAzuQhVgMMXALiWFI0gMBnIN6GHCiENMAASA04UIjVAAKgQFlFdRtI8gPRoUR0PBAd
+ 0ySnJRJS6wEGAICmKADl6upOAAAAAElFTkSuQmCC
+
+
+
\ No newline at end of file
diff --git a/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel.Properties/Resources.resx b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel.Properties/Resources.resx
new file mode 100644
index 0000000..5c113e8
--- /dev/null
+++ b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel.Properties/Resources.resx
@@ -0,0 +1,166 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAAA8AAAAMCAYAAAC9QufkAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAN
+ 1gAADdYBkG95nAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAACdSURBVChTlZGx
+ DcIwEEUtalZIBmEYZiGLULMAtUsGYICQNi0VEpL5D9nWRbYs50tP1t29X9mFELxYxGsH+J7yKGaxJ/gj
+ ZZjEV/QED9+l8kE8RE/w8HMZTmIVrXDH+3dsGa6iFe7Zt0U4iqeohT337Nti4izewoaZ/cbdDIa7sGEu
+ vGIRGUT6e17mwisWhov4xLd2b5b5y1t8K/fgfhf9ch0fZsVEAAAAAElFTkSuQmCC
+
+
+
+
+ /9j/4AAQSkZJRgABAQEAYABgAAD/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAEAAAAAAAD/2wBD
+ AAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0M
+ DgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM
+ DAwMDAwMDAwMDAwMDAz/wAARCAARALkDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQF
+ BgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAk
+ M2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWG
+ h4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx
+ 8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQA
+ AQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5
+ OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmq
+ srO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDz
+ f/hd6/3ovzH+FH/C71/vRfmP8K+Sv+F4v/z0lo/4Xi//AD0lrp9mZ859a/8AC71/vRfmP8KP+F3r/ei/
+ Mf4V8lf8Lxf/AJ6S0f8AC8X/AOektHsw5z61/wCF3r/ei/Mf4Uf8LvX+9F+Y/wAK+Sv+F4v/AM9JaP8A
+ heL/APPSWj2Yc59a/wDC71/vRfmP8KP+F3r/AHovzH+FfJX/AAvF/wDnpLR/wvF/+ektHsw5z61/4Xev
+ 96L8x/hR/wALvX+9F+Y/wr5K/wCF4v8A89JaP+F4v/z0lo9mHOfWv/C71/vRfmP8KP8Ahd6/3ovzH+Ff
+ JX/C8X/56S0f8Lxf/npLR7MOc+tf+F3r/ei/Mf4Uf8LvX+9F+Y/wr5K/4Xi//PSWj/heL/8APSWj2Yc5
+ 9a/8LvX+9F+Y/wAKP+F3r/ei/Mf4V8lf8Lxf/npLR/wvF/8AnpLR7MOc+tf+F3r/AHovzH+FH/C71/vR
+ fmP8K+Sv+F4v/wA9JaP+F4v/AM9JaPZhzn1r/wALvX+9F+Y/wo/4Xev96L8x/hXyV/wvF/8AnpLR/wAL
+ xf8A56S0ezDnPrX/AIXev96L8x/hR/wu9f70X5j/AAr5K/4Xi/8Az0lo/wCF4v8A89JaPZhzn1r/AMLv
+ X+9F+Y/wo/4Xev8Aei/Mf4V8lf8AC8X/AOektH/C8X/56S0ezDnPJKKKK5jQKKKKACiiigAooooAKKKK
+ ACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/2Q==
+
+
+
+ 0
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAAAwAAAAPCAYAAADQ4S5JAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAN
+ 1gAADdYBkG95nAAAAHRJREFUKFNj+P///z4GBgaZAwcOMBCDQRoeA/F9IG7CpgAdgzSAFIPAbyA+BsSW
+ 2BTCMLIGGHgNxPOAzuQhVgMMXALiWFI0gMBnIN6GHCiENMAASA04UIjVAAKgQFlFdRtI8gPRoUR0PBAd
+ 0ySnJRJS6wEGAICmKADl6upOAAAAAElFTkSuQmCC
+
+
+
\ No newline at end of file
diff --git a/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel.csproj b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel.csproj
new file mode 100644
index 0000000..b12e1db
--- /dev/null
+++ b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel.csproj
@@ -0,0 +1,65 @@
+
+
+
+ {A5EFB9F9-6F99-4EBD-B5C9-3F68EFEFC823}
+ Debug
+ AnyCPU
+ Library
+ SDRSharp.CollapsiblePanel
+ .NETFramework
+ v4.6
+ 4
+ True
+
+
+ AnyCPU
+
+
+ bin\Debug\
+ true
+ full
+ false
+
+
+ bin\Release\
+ true
+ pdbonly
+ true
+
+
+
+ C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll
+
+
+ C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089\System.Windows.Forms.dll
+
+
+ C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Drawing\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll
+
+
+ C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Design\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Design.dll
+
+
+
+
+
+
+ UserControl
+
+
+
+ Component
+
+
+
+
+
+
+ Resources.cs
+
+
+ CollapsiblePanel.cs
+
+
+
+
\ No newline at end of file
diff --git a/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel.sln b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel.sln
new file mode 100644
index 0000000..ede6d5a
--- /dev/null
+++ b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27428.2015
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SDRSharp.CollapsiblePanel", "SDRSharp.CollapsiblePanel.csproj", "{A5EFB9F9-6F99-4EBD-B5C9-3F68EFEFC823}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A5EFB9F9-6F99-4EBD-B5C9-3F68EFEFC823}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A5EFB9F9-6F99-4EBD-B5C9-3F68EFEFC823}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A5EFB9F9-6F99-4EBD-B5C9-3F68EFEFC823}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A5EFB9F9-6F99-4EBD-B5C9-3F68EFEFC823}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {AF25AAAB-B38C-48F7-A7D7-0D815C0D1E47}
+ EndGlobalSection
+EndGlobal
diff --git a/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/CollapsiblePanel.cs b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/CollapsiblePanel.cs
new file mode 100644
index 0000000..45ae156
--- /dev/null
+++ b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/CollapsiblePanel.cs
@@ -0,0 +1,271 @@
+using SDRSharp.CollapsiblePanel.Properties;
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace SDRSharp.CollapsiblePanel
+{
+ [DesignTimeVisible(true)]
+ [Category("Containers")]
+ [Description("Visual Studio like Collapsible Panel")]
+ [Designer(typeof(CollapsiblePanelDesigner))]
+ public class CollapsiblePanel : UserControl
+ {
+ private bool _autoHeight;
+
+ private PanelStateOptions _panelState = PanelStateOptions.Expanded;
+
+ private CollapsiblePanel _nextPanel;
+
+ private IContainer components;
+
+ private Panel titlePanel;
+
+ private PictureBox togglingImage;
+
+ private ImageList collapsiblePanelImageList;
+
+ private Label lblPanelTitle;
+
+ private ContentPanel contentPanel;
+
+ private TableLayoutPanel titleTableLayoutPanel;
+
+ [Description("Gets or sets panel title")]
+ [DisplayName("Panel Title")]
+ [Category("Collapsible Panel")]
+ public string PanelTitle
+ {
+ get
+ {
+ return this.lblPanelTitle.Text;
+ }
+ set
+ {
+ this.lblPanelTitle.Text = value;
+ }
+ }
+
+ [DefaultValue(typeof(PanelStateOptions), "Expanded")]
+ [Description("Gets or sets current panel state")]
+ [DisplayName("Panel State")]
+ [Category("Collapsible Panel")]
+ public PanelStateOptions PanelState
+ {
+ get
+ {
+ return this._panelState;
+ }
+ set
+ {
+ this._panelState = value;
+ this.UpdateState();
+ }
+ }
+
+ [Category("Collapsible Panel")]
+ [Description("Gets or sets the panel to be located beneath this panel")]
+ public CollapsiblePanel NextPanel
+ {
+ get
+ {
+ return this._nextPanel;
+ }
+ set
+ {
+ this._nextPanel = value;
+ this.MoveNextPanel();
+ }
+ }
+
+ [Category("Appearance")]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
+ public ContentPanel Content
+ {
+ get
+ {
+ return this.contentPanel;
+ }
+ }
+
+ public bool AutoHeight
+ {
+ get
+ {
+ return this._autoHeight;
+ }
+ set
+ {
+ if (this._autoHeight != value)
+ {
+ this._autoHeight = value;
+ this.UpdateState();
+ }
+ }
+ }
+
+ public CollapsiblePanel()
+ {
+ this.InitializeComponent();
+ base.Load += this.CollapsiblePanel_Load;
+ base.SizeChanged += this.CollapsiblePanel_SizeChanged;
+ base.LocationChanged += this.CollapsiblePanel_LocationChanged;
+ }
+
+ private void CollapsiblePanel_Load(object sender, EventArgs e)
+ {
+ if (this._panelState == PanelStateOptions.Collapsed)
+ {
+ this.togglingImage.Image = Resources.CollapsedIcon;
+ }
+ else
+ {
+ this.togglingImage.Image = Resources.ExpandedIcon;
+ }
+ }
+
+ private void CollapsiblePanel_SizeChanged(object sender, EventArgs e)
+ {
+ this.MoveNextPanel();
+ }
+
+ private void CollapsiblePanel_LocationChanged(object sender, EventArgs e)
+ {
+ this.MoveNextPanel();
+ }
+
+ private void ToggleState(object sender, EventArgs e)
+ {
+ this._panelState = ((this._panelState == PanelStateOptions.Collapsed) ? PanelStateOptions.Expanded : PanelStateOptions.Collapsed);
+ this.UpdateState();
+ }
+
+ internal void UpdateState()
+ {
+ if (this._panelState == PanelStateOptions.Collapsed)
+ {
+ this.contentPanel.Visible = false;
+ base.Height = this.titlePanel.Height;
+ this.togglingImage.Image = Resources.CollapsedIcon;
+ }
+ else
+ {
+ int num = (this.contentPanel.Controls.Count != 1 || !this._autoHeight) ? this.contentPanel.Height : this.contentPanel.Controls[0].Height;
+ base.Height = this.titlePanel.Height + num;
+ this.contentPanel.Visible = true;
+ this.togglingImage.Image = Resources.ExpandedIcon;
+ Panel panel = base.Parent as Panel;
+ while (panel != null && panel.AutoSize)
+ {
+ panel = (panel.Parent as Panel);
+ }
+ if (panel != null)
+ {
+ panel.ScrollControlIntoView(this);
+ }
+ }
+ }
+
+ private void MoveNextPanel()
+ {
+ if (this._nextPanel != null)
+ {
+ CollapsiblePanel nextPanel = this._nextPanel;
+ Point location = base.Location;
+ int x = location.X;
+ location = base.Location;
+ nextPanel.Location = new Point(x, location.Y + base.Size.Height);
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && this.components != null)
+ {
+ this.components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ private void InitializeComponent()
+ {
+ this.components = new Container();
+ ComponentResourceManager componentResourceManager = new ComponentResourceManager(typeof(CollapsiblePanel));
+ this.collapsiblePanelImageList = new ImageList(this.components);
+ this.titlePanel = new Panel();
+ this.titleTableLayoutPanel = new TableLayoutPanel();
+ this.togglingImage = new PictureBox();
+ this.lblPanelTitle = new Label();
+ this.contentPanel = new ContentPanel();
+ this.titlePanel.SuspendLayout();
+ this.titleTableLayoutPanel.SuspendLayout();
+ ((ISupportInitialize)this.togglingImage).BeginInit();
+ base.SuspendLayout();
+ this.collapsiblePanelImageList.ImageStream = (ImageListStreamer)componentResourceManager.GetObject("collapsiblePanelImageList.ImageStream");
+ this.collapsiblePanelImageList.TransparentColor = Color.Transparent;
+ this.collapsiblePanelImageList.Images.SetKeyName(0, "ExpandIcon.jpg");
+ this.titlePanel.BackColor = Color.DarkGray;
+ this.titlePanel.BackgroundImage = Resources.titleBackground;
+ this.titlePanel.BackgroundImageLayout = ImageLayout.Stretch;
+ this.titlePanel.Controls.Add(this.titleTableLayoutPanel);
+ this.titlePanel.Dock = DockStyle.Top;
+ this.titlePanel.Location = new Point(0, 0);
+ this.titlePanel.Name = "titlePanel";
+ this.titlePanel.Size = new Size(150, 24);
+ this.titlePanel.TabIndex = 0;
+ this.titleTableLayoutPanel.BackColor = Color.Transparent;
+ this.titleTableLayoutPanel.ColumnCount = 2;
+ this.titleTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 24f));
+ this.titleTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
+ this.titleTableLayoutPanel.Controls.Add(this.togglingImage, 0, 0);
+ this.titleTableLayoutPanel.Controls.Add(this.lblPanelTitle, 1, 0);
+ this.titleTableLayoutPanel.Dock = DockStyle.Fill;
+ this.titleTableLayoutPanel.Location = new Point(0, 0);
+ this.titleTableLayoutPanel.Name = "titleTableLayoutPanel";
+ this.titleTableLayoutPanel.RowCount = 1;
+ this.titleTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
+ this.titleTableLayoutPanel.Size = new Size(150, 24);
+ this.titleTableLayoutPanel.TabIndex = 2;
+ this.titleTableLayoutPanel.Click += this.ToggleState;
+ this.togglingImage.Anchor = AnchorStyles.None;
+ this.togglingImage.BackColor = Color.Transparent;
+ this.togglingImage.Image = Resources.ExpandedIcon;
+ this.togglingImage.Location = new Point(7, 7);
+ this.togglingImage.Name = "togglingImage";
+ this.togglingImage.Size = new Size(10, 10);
+ this.togglingImage.SizeMode = PictureBoxSizeMode.StretchImage;
+ this.togglingImage.TabIndex = 0;
+ this.togglingImage.TabStop = false;
+ this.togglingImage.Click += this.ToggleState;
+ this.lblPanelTitle.Anchor = AnchorStyles.Left;
+ this.lblPanelTitle.AutoEllipsis = true;
+ this.lblPanelTitle.AutoSize = true;
+ this.lblPanelTitle.BackColor = Color.Transparent;
+ this.lblPanelTitle.Font = new Font("Segoe UI Semibold", 9f, FontStyle.Regular, GraphicsUnit.Point, 0);
+ this.lblPanelTitle.ForeColor = Color.WhiteSmoke;
+ this.lblPanelTitle.Location = new Point(27, 4);
+ this.lblPanelTitle.Name = "lblPanelTitle";
+ this.lblPanelTitle.Size = new Size(59, 15);
+ this.lblPanelTitle.TabIndex = 1;
+ this.lblPanelTitle.Text = "Panel title";
+ this.lblPanelTitle.Click += this.ToggleState;
+ this.contentPanel.Location = new Point(0, 24);
+ this.contentPanel.Margin = new Padding(2);
+ this.contentPanel.Name = "contentPanel";
+ this.contentPanel.Size = new Size(150, 126);
+ this.contentPanel.TabIndex = 1;
+ base.AutoScaleDimensions = new SizeF(96f, 96f);
+ base.AutoScaleMode = AutoScaleMode.Dpi;
+ base.Controls.Add(this.contentPanel);
+ base.Controls.Add(this.titlePanel);
+ base.Name = "CollapsiblePanel";
+ this.titlePanel.ResumeLayout(false);
+ this.titleTableLayoutPanel.ResumeLayout(false);
+ this.titleTableLayoutPanel.PerformLayout();
+ ((ISupportInitialize)this.togglingImage).EndInit();
+ base.ResumeLayout(false);
+ base.PerformLayout();
+ }
+ }
+}
diff --git a/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/CollapsiblePanel.resource b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/CollapsiblePanel.resource
new file mode 100644
index 0000000..fed0171
--- /dev/null
+++ b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/CollapsiblePanel.resource
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
+ LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
+ ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAABe
+ CAAAAk1TRnQBSQFMAwEBAAHEAQABxAEAARABAAEQAQAE/wEJARAI/wFCAU0BNgEEBgABNgEEAgABKAMA
+ AUADAAEQAwABAQEAAQgGAAEEGAABgAIAAYADAAKAAQABgAMAAYABAAGAAQACgAIAA8ABAAHAAdwBwAEA
+ AfABygGmAQABMwUAATMBAAEzAQABMwEAAjMCAAMWAQADHAEAAyIBAAMpAQADVQEAA00BAANCAQADOQEA
+ AYABfAH/AQACUAH/AQABkwEAAdYBAAH/AewBzAEAAcYB1gHvAQAB1gLnAQABkAGpAa0CAAH/ATMDAAFm
+ AwABmQMAAcwCAAEzAwACMwIAATMBZgIAATMBmQIAATMBzAIAATMB/wIAAWYDAAFmATMCAAJmAgABZgGZ
+ AgABZgHMAgABZgH/AgABmQMAAZkBMwIAAZkBZgIAApkCAAGZAcwCAAGZAf8CAAHMAwABzAEzAgABzAFm
+ AgABzAGZAgACzAIAAcwB/wIAAf8BZgIAAf8BmQIAAf8BzAEAATMB/wIAAf8BAAEzAQABMwEAAWYBAAEz
+ AQABmQEAATMBAAHMAQABMwEAAf8BAAH/ATMCAAMzAQACMwFmAQACMwGZAQACMwHMAQACMwH/AQABMwFm
+ AgABMwFmATMBAAEzAmYBAAEzAWYBmQEAATMBZgHMAQABMwFmAf8BAAEzAZkCAAEzAZkBMwEAATMBmQFm
+ AQABMwKZAQABMwGZAcwBAAEzAZkB/wEAATMBzAIAATMBzAEzAQABMwHMAWYBAAEzAcwBmQEAATMCzAEA
+ ATMBzAH/AQABMwH/ATMBAAEzAf8BZgEAATMB/wGZAQABMwH/AcwBAAEzAv8BAAFmAwABZgEAATMBAAFm
+ AQABZgEAAWYBAAGZAQABZgEAAcwBAAFmAQAB/wEAAWYBMwIAAWYCMwEAAWYBMwFmAQABZgEzAZkBAAFm
+ ATMBzAEAAWYBMwH/AQACZgIAAmYBMwEAA2YBAAJmAZkBAAJmAcwBAAFmAZkCAAFmAZkBMwEAAWYBmQFm
+ AQABZgKZAQABZgGZAcwBAAFmAZkB/wEAAWYBzAIAAWYBzAEzAQABZgHMAZkBAAFmAswBAAFmAcwB/wEA
+ AWYB/wIAAWYB/wEzAQABZgH/AZkBAAFmAf8BzAEAAcwBAAH/AQAB/wEAAcwBAAKZAgABmQEzAZkBAAGZ
+ AQABmQEAAZkBAAHMAQABmQMAAZkCMwEAAZkBAAFmAQABmQEzAcwBAAGZAQAB/wEAAZkBZgIAAZkBZgEz
+ AQABmQEzAWYBAAGZAWYBmQEAAZkBZgHMAQABmQEzAf8BAAKZATMBAAKZAWYBAAOZAQACmQHMAQACmQH/
+ AQABmQHMAgABmQHMATMBAAFmAcwBZgEAAZkBzAGZAQABmQLMAQABmQHMAf8BAAGZAf8CAAGZAf8BMwEA
+ AZkBzAFmAQABmQH/AZkBAAGZAf8BzAEAAZkC/wEAAcwDAAGZAQABMwEAAcwBAAFmAQABzAEAAZkBAAHM
+ AQABzAEAAZkBMwIAAcwCMwEAAcwBMwFmAQABzAEzAZkBAAHMATMBzAEAAcwBMwH/AQABzAFmAgABzAFm
+ ATMBAAGZAmYBAAHMAWYBmQEAAcwBZgHMAQABmQFmAf8BAAHMAZkCAAHMAZkBMwEAAcwBmQFmAQABzAKZ
+ AQABzAGZAcwBAAHMAZkB/wEAAswCAALMATMBAALMAWYBAALMAZkBAAPMAQACzAH/AQABzAH/AgABzAH/
+ ATMBAAGZAf8BZgEAAcwB/wGZAQABzAH/AcwBAAHMAv8BAAHMAQABMwEAAf8BAAFmAQAB/wEAAZkBAAHM
+ ATMCAAH/AjMBAAH/ATMBZgEAAf8BMwGZAQAB/wEzAcwBAAH/ATMB/wEAAf8BZgIAAf8BZgEzAQABzAJm
+ AQAB/wFmAZkBAAH/AWYBzAEAAcwBZgH/AQAB/wGZAgAB/wGZATMBAAH/AZkBZgEAAf8CmQEAAf8BmQHM
+ AQAB/wGZAf8BAAH/AcwCAAH/AcwBMwEAAf8BzAFmAQAB/wHMAZkBAAH/AswBAAH/AcwB/wEAAv8BMwEA
+ AcwB/wFmAQAC/wGZAQAC/wHMAQACZgH/AQABZgH/AWYBAAFmAv8BAAH/AmYBAAH/AWYB/wEAAv8BZgEA
+ ASEBAAGlAQADXwEAA3cBAAOGAQADlgEAA8sBAAOyAQAD1wEAA90BAAPjAQAD6gEAA/EBAAP4AQAB8AH7
+ Af8BAAGkAqABAAOAAwAB/wIAAf8DAAL/AQAB/wMAAf8BAAH/AQAC/wIAA/8BAAHyAfALvAHwAfEB9DAA
+ AQcBkgvsAe0B7wHxMAAB7QH3B+8B9wPvAfcB7QHwMAAB7AH3BAcB7wGSAe8EBwHvAewBvDAAAewB9wIH
+ Au8B7AFDAesB7wEHAbwBBwHvAewBvDAAAewB7wHxAvABvAHsAQ4BbQHwAwcB7wHsAbwwAAHsAQcD/wH0
+ AZIBDgHsAfQBvAHvAQcB7wHsAbwwAAHsAbwB8wHvAuwBEgEOARQB7AFtAesB9wHvAewBvDAAAewBBwHy
+ AeoDDgEAAg4BDwEVAe0B7wHsAbwwAAHsAQcB8wEHAvcBbQEOARMB7QH3Ae8B8AEHAewBvDAAAewBBwT/
+ AfcBDgHsAfQD/wHwAe0BvDAAAewBvAL/AfQB/wGSAQ4B7AT/AfAB7AG8MAAB7AG8Av8B9AH/AQcB6gHv
+ BP8B8AHsAbwwAAHsAQcE/wH0AfIB8wH/AfQB8wH/AfEB7QG8MAAB7QHvArwKBwH3AfAwAAEHAZIL7AHt
+ Ae8B8jAAAUIBTQE+BwABPgMAASgDAAFAAwABEAMAAQEBAAEBBQABgBcAA/+XAAs=
+
+
+
\ No newline at end of file
diff --git a/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/CollapsiblePanel.resx b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/CollapsiblePanel.resx
new file mode 100644
index 0000000..fed0171
--- /dev/null
+++ b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/CollapsiblePanel.resx
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
+ LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
+ ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAABe
+ CAAAAk1TRnQBSQFMAwEBAAHEAQABxAEAARABAAEQAQAE/wEJARAI/wFCAU0BNgEEBgABNgEEAgABKAMA
+ AUADAAEQAwABAQEAAQgGAAEEGAABgAIAAYADAAKAAQABgAMAAYABAAGAAQACgAIAA8ABAAHAAdwBwAEA
+ AfABygGmAQABMwUAATMBAAEzAQABMwEAAjMCAAMWAQADHAEAAyIBAAMpAQADVQEAA00BAANCAQADOQEA
+ AYABfAH/AQACUAH/AQABkwEAAdYBAAH/AewBzAEAAcYB1gHvAQAB1gLnAQABkAGpAa0CAAH/ATMDAAFm
+ AwABmQMAAcwCAAEzAwACMwIAATMBZgIAATMBmQIAATMBzAIAATMB/wIAAWYDAAFmATMCAAJmAgABZgGZ
+ AgABZgHMAgABZgH/AgABmQMAAZkBMwIAAZkBZgIAApkCAAGZAcwCAAGZAf8CAAHMAwABzAEzAgABzAFm
+ AgABzAGZAgACzAIAAcwB/wIAAf8BZgIAAf8BmQIAAf8BzAEAATMB/wIAAf8BAAEzAQABMwEAAWYBAAEz
+ AQABmQEAATMBAAHMAQABMwEAAf8BAAH/ATMCAAMzAQACMwFmAQACMwGZAQACMwHMAQACMwH/AQABMwFm
+ AgABMwFmATMBAAEzAmYBAAEzAWYBmQEAATMBZgHMAQABMwFmAf8BAAEzAZkCAAEzAZkBMwEAATMBmQFm
+ AQABMwKZAQABMwGZAcwBAAEzAZkB/wEAATMBzAIAATMBzAEzAQABMwHMAWYBAAEzAcwBmQEAATMCzAEA
+ ATMBzAH/AQABMwH/ATMBAAEzAf8BZgEAATMB/wGZAQABMwH/AcwBAAEzAv8BAAFmAwABZgEAATMBAAFm
+ AQABZgEAAWYBAAGZAQABZgEAAcwBAAFmAQAB/wEAAWYBMwIAAWYCMwEAAWYBMwFmAQABZgEzAZkBAAFm
+ ATMBzAEAAWYBMwH/AQACZgIAAmYBMwEAA2YBAAJmAZkBAAJmAcwBAAFmAZkCAAFmAZkBMwEAAWYBmQFm
+ AQABZgKZAQABZgGZAcwBAAFmAZkB/wEAAWYBzAIAAWYBzAEzAQABZgHMAZkBAAFmAswBAAFmAcwB/wEA
+ AWYB/wIAAWYB/wEzAQABZgH/AZkBAAFmAf8BzAEAAcwBAAH/AQAB/wEAAcwBAAKZAgABmQEzAZkBAAGZ
+ AQABmQEAAZkBAAHMAQABmQMAAZkCMwEAAZkBAAFmAQABmQEzAcwBAAGZAQAB/wEAAZkBZgIAAZkBZgEz
+ AQABmQEzAWYBAAGZAWYBmQEAAZkBZgHMAQABmQEzAf8BAAKZATMBAAKZAWYBAAOZAQACmQHMAQACmQH/
+ AQABmQHMAgABmQHMATMBAAFmAcwBZgEAAZkBzAGZAQABmQLMAQABmQHMAf8BAAGZAf8CAAGZAf8BMwEA
+ AZkBzAFmAQABmQH/AZkBAAGZAf8BzAEAAZkC/wEAAcwDAAGZAQABMwEAAcwBAAFmAQABzAEAAZkBAAHM
+ AQABzAEAAZkBMwIAAcwCMwEAAcwBMwFmAQABzAEzAZkBAAHMATMBzAEAAcwBMwH/AQABzAFmAgABzAFm
+ ATMBAAGZAmYBAAHMAWYBmQEAAcwBZgHMAQABmQFmAf8BAAHMAZkCAAHMAZkBMwEAAcwBmQFmAQABzAKZ
+ AQABzAGZAcwBAAHMAZkB/wEAAswCAALMATMBAALMAWYBAALMAZkBAAPMAQACzAH/AQABzAH/AgABzAH/
+ ATMBAAGZAf8BZgEAAcwB/wGZAQABzAH/AcwBAAHMAv8BAAHMAQABMwEAAf8BAAFmAQAB/wEAAZkBAAHM
+ ATMCAAH/AjMBAAH/ATMBZgEAAf8BMwGZAQAB/wEzAcwBAAH/ATMB/wEAAf8BZgIAAf8BZgEzAQABzAJm
+ AQAB/wFmAZkBAAH/AWYBzAEAAcwBZgH/AQAB/wGZAgAB/wGZATMBAAH/AZkBZgEAAf8CmQEAAf8BmQHM
+ AQAB/wGZAf8BAAH/AcwCAAH/AcwBMwEAAf8BzAFmAQAB/wHMAZkBAAH/AswBAAH/AcwB/wEAAv8BMwEA
+ AcwB/wFmAQAC/wGZAQAC/wHMAQACZgH/AQABZgH/AWYBAAFmAv8BAAH/AmYBAAH/AWYB/wEAAv8BZgEA
+ ASEBAAGlAQADXwEAA3cBAAOGAQADlgEAA8sBAAOyAQAD1wEAA90BAAPjAQAD6gEAA/EBAAP4AQAB8AH7
+ Af8BAAGkAqABAAOAAwAB/wIAAf8DAAL/AQAB/wMAAf8BAAH/AQAC/wIAA/8BAAHyAfALvAHwAfEB9DAA
+ AQcBkgvsAe0B7wHxMAAB7QH3B+8B9wPvAfcB7QHwMAAB7AH3BAcB7wGSAe8EBwHvAewBvDAAAewB9wIH
+ Au8B7AFDAesB7wEHAbwBBwHvAewBvDAAAewB7wHxAvABvAHsAQ4BbQHwAwcB7wHsAbwwAAHsAQcD/wH0
+ AZIBDgHsAfQBvAHvAQcB7wHsAbwwAAHsAbwB8wHvAuwBEgEOARQB7AFtAesB9wHvAewBvDAAAewBBwHy
+ AeoDDgEAAg4BDwEVAe0B7wHsAbwwAAHsAQcB8wEHAvcBbQEOARMB7QH3Ae8B8AEHAewBvDAAAewBBwT/
+ AfcBDgHsAfQD/wHwAe0BvDAAAewBvAL/AfQB/wGSAQ4B7AT/AfAB7AG8MAAB7AG8Av8B9AH/AQcB6gHv
+ BP8B8AHsAbwwAAHsAQcE/wH0AfIB8wH/AfQB8wH/AfEB7QG8MAAB7QHvArwKBwH3AfAwAAEHAZIL7AHt
+ Ae8B8jAAAUIBTQE+BwABPgMAASgDAAFAAwABEAMAAQEBAAEBBQABgBcAA/+XAAs=
+
+
+
\ No newline at end of file
diff --git a/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/CollapsiblePanelDesigner.cs b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/CollapsiblePanelDesigner.cs
new file mode 100644
index 0000000..33b6dd6
--- /dev/null
+++ b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/CollapsiblePanelDesigner.cs
@@ -0,0 +1,17 @@
+using System.ComponentModel;
+using System.Windows.Forms.Design;
+
+namespace SDRSharp.CollapsiblePanel
+{
+ public class CollapsiblePanelDesigner : ParentControlDesigner
+ {
+ public override void Initialize(IComponent component)
+ {
+ base.Initialize(component);
+ if (this.Control is CollapsiblePanel)
+ {
+ base.EnableDesignMode(((CollapsiblePanel)this.Control).Content, "Content");
+ }
+ }
+ }
+}
diff --git a/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/ContentPanel.cs b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/ContentPanel.cs
new file mode 100644
index 0000000..18894e2
--- /dev/null
+++ b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/ContentPanel.cs
@@ -0,0 +1,32 @@
+using System;
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace SDRSharp.CollapsiblePanel
+{
+ [Designer(typeof(ContentPanelDesigner))]
+ public class ContentPanel : Panel
+ {
+ public ContentPanel()
+ {
+ base.Dock = DockStyle.Fill;
+ }
+
+ protected override void OnControlAdded(ControlEventArgs e)
+ {
+ e.Control.Resize += this.sourceControlPanel_Resize;
+ base.OnControlAdded(e);
+ }
+
+ protected override void OnControlRemoved(ControlEventArgs e)
+ {
+ e.Control.Resize -= this.sourceControlPanel_Resize;
+ base.OnControlRemoved(e);
+ }
+
+ private void sourceControlPanel_Resize(object sender, EventArgs e)
+ {
+ ((CollapsiblePanel)base.Parent).UpdateState();
+ }
+ }
+}
diff --git a/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/ContentPanelDesigner.cs b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/ContentPanelDesigner.cs
new file mode 100644
index 0000000..f5e2f35
--- /dev/null
+++ b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/ContentPanelDesigner.cs
@@ -0,0 +1,16 @@
+using System.Collections;
+using System.Windows.Forms.Design;
+
+namespace SDRSharp.CollapsiblePanel
+{
+ public class ContentPanelDesigner : ScrollableControlDesigner
+ {
+ protected override void PreFilterProperties(IDictionary properties)
+ {
+ properties.Remove("Dock");
+ properties.Remove("AutoSize");
+ properties.Remove("AutoSizeMode");
+ base.PreFilterProperties(properties);
+ }
+ }
+}
diff --git a/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/PanelStateOptions.cs b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/PanelStateOptions.cs
new file mode 100644
index 0000000..a1444dd
--- /dev/null
+++ b/SDRSharp.CollapsiblePanel/SDRSharp.CollapsiblePanel/PanelStateOptions.cs
@@ -0,0 +1,8 @@
+namespace SDRSharp.CollapsiblePanel
+{
+ public enum PanelStateOptions
+ {
+ Collapsed,
+ Expanded
+ }
+}
diff --git a/SDRSharp.Common/Properties/AssemblyInfo.cs b/SDRSharp.Common/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..3d0289c
--- /dev/null
+++ b/SDRSharp.Common/Properties/AssemblyInfo.cs
@@ -0,0 +1,17 @@
+using System.Diagnostics;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.Versioning;
+using System.Security;
+using System.Security.Permissions;
+
+[assembly: CompilationRelaxations(8)]
+[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
+[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
+[assembly: SecurityRules(SecurityRuleSet.Level1)]
+[assembly: AssemblyTitle("SDR Sharp")]
+[assembly: AssemblyDescription("Software Defined Radio")]
+[assembly: AssemblyProduct("SDR#")]
+[assembly: AssemblyCopyright("Copyright © Youssef TOUIL 2012")]
+[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
+[assembly: AssemblyVersion("0.0.0.0")]
diff --git a/SDRSharp.Common/SDRSharp.Common.csproj b/SDRSharp.Common/SDRSharp.Common.csproj
new file mode 100644
index 0000000..9f0919c
--- /dev/null
+++ b/SDRSharp.Common/SDRSharp.Common.csproj
@@ -0,0 +1,59 @@
+
+
+
+ {E8BB1EAC-ED6B-4D5E-AA88-6A6CD961F37E}
+ Debug
+ AnyCPU
+ Library
+ SDRSharp.Common
+ .NETFramework
+ v4.6
+ 4
+ True
+
+
+ AnyCPU
+
+
+ bin\Debug\
+ true
+ full
+ false
+
+
+ bin\Release\
+ true
+ pdbonly
+ true
+
+
+
+ ..\SDRSharp.PanView\bin\Debug\SDRSharp.PanView.dll
+
+
+ ..\SDRSharp.Radio\bin\Debug\SDRSharp.Radio.dll
+
+
+ C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll
+
+
+ C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Drawing\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll
+
+
+ C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089\System.Windows.Forms.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SDRSharp.Common/SDRSharp.Common.sln b/SDRSharp.Common/SDRSharp.Common.sln
new file mode 100644
index 0000000..404925d
--- /dev/null
+++ b/SDRSharp.Common/SDRSharp.Common.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27428.2015
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SDRSharp.Common", "SDRSharp.Common.csproj", "{E8BB1EAC-ED6B-4D5E-AA88-6A6CD961F37E}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {E8BB1EAC-ED6B-4D5E-AA88-6A6CD961F37E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E8BB1EAC-ED6B-4D5E-AA88-6A6CD961F37E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E8BB1EAC-ED6B-4D5E-AA88-6A6CD961F37E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E8BB1EAC-ED6B-4D5E-AA88-6A6CD961F37E}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {0AE04AD9-F937-4F9E-9BEC-85735408578C}
+ EndGlobalSection
+EndGlobal
diff --git a/SDRSharp.Common/SDRSharp.Common/ByteSamplesEventArgs.cs b/SDRSharp.Common/SDRSharp.Common/ByteSamplesEventArgs.cs
new file mode 100644
index 0000000..167bff0
--- /dev/null
+++ b/SDRSharp.Common/SDRSharp.Common/ByteSamplesEventArgs.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace SDRSharp.Common
+{
+ public sealed class ByteSamplesEventArgs : EventArgs
+ {
+ public int Length
+ {
+ get;
+ set;
+ }
+
+ public unsafe byte* Buffer
+ {
+ get;
+ set;
+ }
+ }
+}
diff --git a/SDRSharp.Common/SDRSharp.Common/ComplexSamplesEventArgs.cs b/SDRSharp.Common/SDRSharp.Common/ComplexSamplesEventArgs.cs
new file mode 100644
index 0000000..f37f10f
--- /dev/null
+++ b/SDRSharp.Common/SDRSharp.Common/ComplexSamplesEventArgs.cs
@@ -0,0 +1,26 @@
+using SDRSharp.Radio;
+using System;
+
+namespace SDRSharp.Common
+{
+ public sealed class ComplexSamplesEventArgs : EventArgs
+ {
+ public int Length
+ {
+ get;
+ set;
+ }
+
+ public ulong DroppedSamples
+ {
+ get;
+ set;
+ }
+
+ public unsafe Complex* Buffer
+ {
+ get;
+ set;
+ }
+ }
+}
diff --git a/SDRSharp.Common/SDRSharp.Common/IFFTSource.cs b/SDRSharp.Common/SDRSharp.Common/IFFTSource.cs
new file mode 100644
index 0000000..76da60d
--- /dev/null
+++ b/SDRSharp.Common/SDRSharp.Common/IFFTSource.cs
@@ -0,0 +1,36 @@
+namespace SDRSharp.Common
+{
+ public interface IFFTSource
+ {
+ bool FFTEnabled
+ {
+ get;
+ set;
+ }
+
+ int FFTRange
+ {
+ get;
+ set;
+ }
+
+ int FFTOffset
+ {
+ get;
+ set;
+ }
+
+ int DisplayBandwidth
+ {
+ get;
+ }
+
+ int DisplayPixels
+ {
+ get;
+ set;
+ }
+
+ event SamplesAvailableDelegate FFTAvailable;
+ }
+}
diff --git a/SDRSharp.Common/SDRSharp.Common/ISharpControl.cs b/SDRSharp.Common/SDRSharp.Common/ISharpControl.cs
new file mode 100644
index 0000000..0017a54
--- /dev/null
+++ b/SDRSharp.Common/SDRSharp.Common/ISharpControl.cs
@@ -0,0 +1,371 @@
+using SDRSharp.PanView;
+using SDRSharp.Radio;
+using System;
+using System.ComponentModel;
+using System.Drawing.Drawing2D;
+using System.Windows.Forms;
+
+namespace SDRSharp.Common
+{
+ public interface ISharpControl
+ {
+ DetectorType DetectorType
+ {
+ get;
+ set;
+ }
+
+ WindowType FilterType
+ {
+ get;
+ set;
+ }
+
+ int AudioGain
+ {
+ get;
+ set;
+ }
+
+ long CenterFrequency
+ {
+ get;
+ set;
+ }
+
+ int CWShift
+ {
+ get;
+ set;
+ }
+
+ bool FilterAudio
+ {
+ get;
+ set;
+ }
+
+ bool UnityGain
+ {
+ get;
+ set;
+ }
+
+ int FilterBandwidth
+ {
+ get;
+ set;
+ }
+
+ int FilterOrder
+ {
+ get;
+ set;
+ }
+
+ bool FmStereo
+ {
+ get;
+ set;
+ }
+
+ long Frequency
+ {
+ get;
+ set;
+ }
+
+ long FrequencyShift
+ {
+ get;
+ set;
+ }
+
+ bool FrequencyShiftEnabled
+ {
+ get;
+ set;
+ }
+
+ bool MarkPeaks
+ {
+ get;
+ set;
+ }
+
+ bool SnapToGrid
+ {
+ get;
+ set;
+ }
+
+ bool SquelchEnabled
+ {
+ get;
+ set;
+ }
+
+ int SquelchThreshold
+ {
+ get;
+ set;
+ }
+
+ bool IsSquelchOpen
+ {
+ get;
+ }
+
+ bool SwapIq
+ {
+ get;
+ set;
+ }
+
+ bool UseAgc
+ {
+ get;
+ set;
+ }
+
+ bool AgcHang
+ {
+ get;
+ set;
+ }
+
+ int AgcThreshold
+ {
+ get;
+ set;
+ }
+
+ int AgcDecay
+ {
+ get;
+ set;
+ }
+
+ int AgcSlope
+ {
+ get;
+ set;
+ }
+
+ int FFTResolution
+ {
+ get;
+ }
+
+ float FFTRange
+ {
+ get;
+ }
+
+ float FFTOffset
+ {
+ get;
+ }
+
+ int FFTContrast
+ {
+ get;
+ }
+
+ float VisualSNR
+ {
+ get;
+ }
+
+ int IFOffset
+ {
+ get;
+ }
+
+ ColorBlend Gradient
+ {
+ get;
+ }
+
+ SpectrumStyle FFTSpectrumStyle
+ {
+ get;
+ }
+
+ int StepSize
+ {
+ get;
+ set;
+ }
+
+ int Zoom
+ {
+ get;
+ set;
+ }
+
+ bool IsPlaying
+ {
+ get;
+ }
+
+ float SAttack
+ {
+ get;
+ set;
+ }
+
+ float SDecay
+ {
+ get;
+ set;
+ }
+
+ float WAttack
+ {
+ get;
+ set;
+ }
+
+ float WDecay
+ {
+ get;
+ set;
+ }
+
+ bool UseTimeMarkers
+ {
+ get;
+ set;
+ }
+
+ string RdsProgramService
+ {
+ get;
+ }
+
+ string RdsRadioText
+ {
+ get;
+ }
+
+ bool RdsUseFEC
+ {
+ get;
+ set;
+ }
+
+ int RFBandwidth
+ {
+ get;
+ }
+
+ int RFDisplayBandwidth
+ {
+ get;
+ }
+
+ int TunableBandwidth
+ {
+ get;
+ }
+
+ float TuningLimit
+ {
+ get;
+ set;
+ }
+
+ TuningStyle TuningStyle
+ {
+ get;
+ set;
+ }
+
+ bool TuningStyleFreezed
+ {
+ get;
+ set;
+ }
+
+ bool SourceIsSoundCard
+ {
+ get;
+ }
+
+ bool SourceIsWaveFile
+ {
+ get;
+ }
+
+ bool SourceIsTunable
+ {
+ get;
+ }
+
+ object Source
+ {
+ get;
+ }
+
+ bool AudioIsMuted
+ {
+ get;
+ set;
+ }
+
+ bool BypassDemodulation
+ {
+ get;
+ set;
+ }
+
+ Type SourceType
+ {
+ get;
+ }
+
+ string SourceName
+ {
+ get;
+ }
+
+ double AudioSampleRate
+ {
+ get;
+ }
+
+ event PropertyChangedEventHandler PropertyChanged;
+
+ event CustomPaintEventHandler WaterfallCustomPaint;
+
+ event CustomPaintEventHandler SpectrumAnalyzerCustomPaint;
+
+ event CustomPaintEventHandler SpectrumAnalyzerBackgroundCustomPaint;
+
+ void SetFrequency(long frequency, bool onlyMoveCenterFrequency);
+
+ void ResetFrequency(long frequency);
+
+ void ResetFrequency(long frequency, long centerFrequency);
+
+ [Obsolete("Use GetSpectrumSnapshot(float[], float, float) instead")]
+ void GetSpectrumSnapshot(byte[] destArray);
+
+ void GetSpectrumSnapshot(float[] destArray, float scale = 1f, float offset = 0f);
+
+ void StartRadio();
+
+ void StopRadio();
+
+ void RegisterStreamHook(object streamHook, ProcessorType processorType);
+
+ void UnregisterStreamHook(object streamHook);
+
+ void RegisterFrontControl(UserControl control, PluginPosition preferredPosition);
+
+ void Perform();
+
+ void RefreshSource(bool reload);
+ }
+}
diff --git a/SDRSharp.Common/SDRSharp.Common/ISharpPlugin.cs b/SDRSharp.Common/SDRSharp.Common/ISharpPlugin.cs
new file mode 100644
index 0000000..0c66174
--- /dev/null
+++ b/SDRSharp.Common/SDRSharp.Common/ISharpPlugin.cs
@@ -0,0 +1,21 @@
+using System.Windows.Forms;
+
+namespace SDRSharp.Common
+{
+ public interface ISharpPlugin
+ {
+ UserControl Gui
+ {
+ get;
+ }
+
+ string DisplayName
+ {
+ get;
+ }
+
+ void Initialize(ISharpControl control);
+
+ void Close();
+ }
+}
diff --git a/SDRSharp.Common/SDRSharp.Common/IVFOSource.cs b/SDRSharp.Common/SDRSharp.Common/IVFOSource.cs
new file mode 100644
index 0000000..4670454
--- /dev/null
+++ b/SDRSharp.Common/SDRSharp.Common/IVFOSource.cs
@@ -0,0 +1,27 @@
+namespace SDRSharp.Common
+{
+ public interface IVFOSource
+ {
+ long VFOFrequency
+ {
+ get;
+ set;
+ }
+
+ int VFODecimation
+ {
+ get;
+ set;
+ }
+
+ int VFOMinIQDecimation
+ {
+ get;
+ }
+
+ double VFOMaxSampleRate
+ {
+ get;
+ }
+ }
+}
diff --git a/SDRSharp.Common/SDRSharp.Common/PluginPosition.cs b/SDRSharp.Common/SDRSharp.Common/PluginPosition.cs
new file mode 100644
index 0000000..26f1718
--- /dev/null
+++ b/SDRSharp.Common/SDRSharp.Common/PluginPosition.cs
@@ -0,0 +1,10 @@
+namespace SDRSharp.Common
+{
+ public enum PluginPosition
+ {
+ Top,
+ Bottom,
+ Left,
+ Right
+ }
+}
diff --git a/SDRSharp.Common/SDRSharp.Common/RealSamplesEventArgs.cs b/SDRSharp.Common/SDRSharp.Common/RealSamplesEventArgs.cs
new file mode 100644
index 0000000..9567ba9
--- /dev/null
+++ b/SDRSharp.Common/SDRSharp.Common/RealSamplesEventArgs.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace SDRSharp.Common
+{
+ public sealed class RealSamplesEventArgs : EventArgs
+ {
+ public int Length
+ {
+ get;
+ set;
+ }
+
+ public ulong DroppedSamples
+ {
+ get;
+ set;
+ }
+
+ public unsafe float* Buffer
+ {
+ get;
+ set;
+ }
+ }
+}
diff --git a/SDRSharp.Common/SDRSharp.Common/SamplesAvailableDelegate.cs b/SDRSharp.Common/SDRSharp.Common/SamplesAvailableDelegate.cs
new file mode 100644
index 0000000..b6b1d17
--- /dev/null
+++ b/SDRSharp.Common/SDRSharp.Common/SamplesAvailableDelegate.cs
@@ -0,0 +1,4 @@
+namespace SDRSharp.Common
+{
+ public delegate void SamplesAvailableDelegate(object sender, ArgsType e);
+}
diff --git a/SDRSharp.Common/refs SDRSharp.PanView and Radio.txt b/SDRSharp.Common/refs SDRSharp.PanView and Radio.txt
new file mode 100644
index 0000000..e69de29
diff --git a/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit.sln b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit.sln
new file mode 100644
index 0000000..2e2a264
--- /dev/null
+++ b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26228.4
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SDRSharp.FrequencyEdit", "SDRSharp.FrequencyEdit\SDRSharp.FrequencyEdit.csproj", "{A50690CF-7A7A-4B31-9A03-B3A2AF0AF0E0}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A50690CF-7A7A-4B31-9A03-B3A2AF0AF0E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A50690CF-7A7A-4B31-9A03-B3A2AF0AF0E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A50690CF-7A7A-4B31-9A03-B3A2AF0AF0E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A50690CF-7A7A-4B31-9A03-B3A2AF0AF0E0}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/EntryMode.cs b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/EntryMode.cs
new file mode 100644
index 0000000..352df41
--- /dev/null
+++ b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/EntryMode.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace SDRSharp.FrequencyEdit
+{
+ public enum EntryMode
+ {
+ None,
+ Direct,
+ Arrow
+ }
+}
diff --git a/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/FrequencyChangingEventArgs.cs b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/FrequencyChangingEventArgs.cs
new file mode 100644
index 0000000..6dbb358
--- /dev/null
+++ b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/FrequencyChangingEventArgs.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace SDRSharp.FrequencyEdit
+{
+ public class FrequencyChangingEventArgs : EventArgs
+ {
+ public long Frequency { get; set; }
+
+ public bool Accept { get; set; }
+
+ public FrequencyChangingEventArgs()
+ {
+ }
+ }
+}
diff --git a/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/FrequencyEdit.cs b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/FrequencyEdit.cs
new file mode 100644
index 0000000..9a9b227
--- /dev/null
+++ b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/FrequencyEdit.cs
@@ -0,0 +1,749 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+using SDRSharp.FrequencyEdit.Properties;
+
+namespace SDRSharp.FrequencyEdit
+{
+ public sealed class FrequencyEdit : UserControl
+ {
+ public event EventHandler FrequencyChanged;
+
+ public event EventHandler FrequencyChanging;
+
+ public int StepSize
+ {
+ get
+ {
+ return this._stepSize;
+ }
+ set
+ {
+ this._stepSize = value;
+ }
+ }
+
+ public bool DisableFrequencyEvents
+ {
+ get
+ {
+ return this._disableFrequencyEvents;
+ }
+ set
+ {
+ this._disableFrequencyEvents = value;
+ }
+ }
+
+ public bool EntryModeActive
+ {
+ get
+ {
+ return this._currentEntryMode > EntryMode.None;
+ }
+ }
+
+ public long Frequency
+ {
+ get
+ {
+ return this._frequency;
+ }
+ set
+ {
+ this._frequencyChangingEventArgs.Accept = true;
+ this._frequencyChangingEventArgs.Frequency = value;
+ if (!this._disableFrequencyEvents && this.FrequencyChanging != null)
+ {
+ this.FrequencyChanging(this, this._frequencyChangingEventArgs);
+ }
+ if (this._frequencyChangingEventArgs.Accept)
+ {
+ this._frequency = this._frequencyChangingEventArgs.Frequency;
+ this.UpdateDigitsValues();
+ if (!this._disableFrequencyEvents && this.FrequencyChanged != null)
+ {
+ this.FrequencyChanged(this, EventArgs.Empty);
+ }
+ }
+ }
+ }
+
+ public FrequencyEdit()
+ {
+ this.DoubleBuffered = true;
+ this.AutoSize = true;
+ base.AutoSizeMode = AutoSizeMode.GrowAndShrink;
+ this._digitImages = Resources.Numbers;
+ this._renderTimer.Interval = 30;
+ this._renderTimer.Tick += this.renderTimer_Tick;
+ this._renderTimer.Enabled = true;
+ this.ConfigureComponent();
+ }
+
+ private void renderTimer_Tick(object sender, EventArgs e)
+ {
+ for (int i = 0; i < base.Controls.Count; i++)
+ {
+ if (base.Controls[i] is IRenderable)
+ {
+ ((IRenderable)base.Controls[i]).Render();
+ }
+ }
+ }
+
+ private void ConfigureComponent()
+ {
+ this.BackColor = Color.Transparent;
+ if (this._digitImages != null)
+ {
+ for (int i = 0; i < 12; i++)
+ {
+ if (this._digitControls[i] != null && base.Controls.Contains(this._digitControls[i]))
+ {
+ base.Controls.Remove(this._digitControls[i]);
+ this._digitControls[i] = null;
+ }
+ }
+ for (int j = 0; j < 12; j++)
+ {
+ if (this._separatorControls[j] != null && base.Controls.Contains(this._separatorControls[j]))
+ {
+ base.Controls.Remove(this._separatorControls[j]);
+ this._separatorControls[j] = null;
+ }
+ }
+ this.SplitDigitImages();
+ }
+ if (this._imageList.Images.Count == 0)
+ {
+ return;
+ }
+ int num = 0;
+ int y = 0;
+ int width = this._imageList.ImageSize.Width;
+ int height = this._imageList.ImageSize.Height;
+ for (int k = 11; k >= 0; k--)
+ {
+ if ((k + 1) % 3 == 0 && k != 11)
+ {
+ FrequencyEditSeparator frequencyEditSeparator = new FrequencyEditSeparator();
+ int num2 = width / 2;
+ int num3 = k / 3;
+ frequencyEditSeparator.Image = this._imageList.Images[11];
+ frequencyEditSeparator.Width = num2;
+ frequencyEditSeparator.Height = height;
+ frequencyEditSeparator.Location = new Point(num, y);
+ base.Controls.Add(frequencyEditSeparator);
+ this._separatorControls[num3] = frequencyEditSeparator;
+ num += num2 + 2;
+ }
+ FrequencyEditDigit frequencyEditDigit = new FrequencyEditDigit(k);
+ frequencyEditDigit.Location = new Point(num, y);
+ frequencyEditDigit.OnDigitClick += this.DigitClickHandler;
+ frequencyEditDigit.MouseLeave += this.DigitMouseLeave;
+ frequencyEditDigit.Width = width;
+ frequencyEditDigit.Height = height;
+ frequencyEditDigit.ImageList = this._imageList;
+ base.Controls.Add(frequencyEditDigit);
+ this._digitControls[k] = frequencyEditDigit;
+ num += width + 2;
+ }
+ long num4 = 1L;
+ for (int l = 0; l < 12; l++)
+ {
+ this._digitControls[l].Weight = num4;
+ num4 *= 10L;
+ }
+ base.Height = height;
+ this.UpdateDigitMask();
+ }
+
+ private void SplitDigitImages()
+ {
+ int height = this._digitImages.Height;
+ int num = (int)Math.Round((double)((float)this._digitImages.Width / 11.5f));
+ this._imageList.Images.Clear();
+ this._imageList.ImageSize = new Size(num, height);
+ int num2 = 0;
+ Bitmap bitmap;
+ for (int i = 0; i < 11; i++)
+ {
+ bitmap = new Bitmap(num, height);
+ using (Graphics graphics = Graphics.FromImage(bitmap))
+ {
+ graphics.DrawImage(this._digitImages, new Rectangle(0, 0, num, height), new Rectangle(num2, 0, num, height), GraphicsUnit.Pixel);
+ }
+ num2 += num;
+ this._imageList.Images.Add(bitmap);
+ }
+ bitmap = new Bitmap(num, height);
+ using (Graphics graphics2 = Graphics.FromImage(bitmap))
+ {
+ graphics2.DrawImage(this._digitImages, new Rectangle(0, 0, num, height), new Rectangle(num2, 0, num / 2, height), GraphicsUnit.Pixel);
+ }
+ this._imageList.Images.Add(bitmap);
+ }
+
+ private void DigitClickHandler(object sender, FrequencyEditDigitClickEventArgs args)
+ {
+ if (this._currentEntryMode != EntryMode.None)
+ {
+ this.LeaveEntryMode();
+ return;
+ }
+ FrequencyEditDigit frequencyEditDigit = (FrequencyEditDigit)sender;
+ if (frequencyEditDigit != null)
+ {
+ this._newFrequency = this._frequency;
+ if (args.Button == MouseButtons.Right)
+ {
+ this.ZeroDigits(frequencyEditDigit.DigitIndex);
+ }
+ else if (args.IsUpperHalf && this._frequency >= 0L)
+ {
+ this.IncrementDigit(frequencyEditDigit.DigitIndex, true);
+ }
+ else
+ {
+ this.DecrementDigit(frequencyEditDigit.DigitIndex, true);
+ }
+ if (this._newFrequency != this._frequency)
+ {
+ this._frequencyChangingEventArgs.Accept = true;
+ this._frequencyChangingEventArgs.Frequency = this._newFrequency;
+ if (!this._disableFrequencyEvents && this.FrequencyChanging != null)
+ {
+ this.FrequencyChanging(this, this._frequencyChangingEventArgs);
+ }
+ if (this._frequencyChangingEventArgs.Accept)
+ {
+ this._frequency = this._frequencyChangingEventArgs.Frequency;
+ this.UpdateDigitsValues();
+ if (!this._disableFrequencyEvents && this.FrequencyChanged != null)
+ {
+ this.FrequencyChanged(this, EventArgs.Empty);
+ return;
+ }
+ }
+ else
+ {
+ this.UpdateDigitsValues();
+ }
+ }
+ }
+ }
+
+ private void IncrementDigit(int index, bool updateDigit)
+ {
+ FrequencyEditDigit frequencyEditDigit = this._digitControls[index];
+ if (frequencyEditDigit != null)
+ {
+ int displayedDigit = frequencyEditDigit.DisplayedDigit;
+ int num = (frequencyEditDigit.DisplayedDigit == 9) ? 0 : (frequencyEditDigit.DisplayedDigit + 1);
+ long newFrequency = this._newFrequency - (long)displayedDigit * frequencyEditDigit.Weight + (long)num * frequencyEditDigit.Weight;
+ if (updateDigit)
+ {
+ frequencyEditDigit.DisplayedDigit = num;
+ }
+ this._newFrequency = newFrequency;
+ if (displayedDigit == 9 && index < 11)
+ {
+ this.IncrementDigit(index + 1, updateDigit);
+ }
+ }
+ }
+
+ private void DecrementDigit(int index, bool updateDigit)
+ {
+ FrequencyEditDigit frequencyEditDigit = this._digitControls[index];
+ if (frequencyEditDigit != null)
+ {
+ int displayedDigit = frequencyEditDigit.DisplayedDigit;
+ int num = (frequencyEditDigit.DisplayedDigit == 0) ? 9 : (frequencyEditDigit.DisplayedDigit - 1);
+ long newFrequency = this._newFrequency - (long)displayedDigit * frequencyEditDigit.Weight + (long)num * frequencyEditDigit.Weight;
+ if (updateDigit)
+ {
+ frequencyEditDigit.DisplayedDigit = num;
+ }
+ this._newFrequency = newFrequency;
+ if (displayedDigit == 0 && index < 11 && (double)this._newFrequency > Math.Pow(10.0, (double)(index + 1)))
+ {
+ this.DecrementDigit(index + 1, updateDigit);
+ }
+ }
+ }
+
+ private void ZeroDigits(int index)
+ {
+ for (int i = 0; i <= index; i++)
+ {
+ this._digitControls[i].DisplayedDigit = 0;
+ }
+ long num = (long)Math.Pow(10.0, (double)(index + 1));
+ this._newFrequency = this._newFrequency / num * num;
+ }
+
+ private void UpdateDigitsValues()
+ {
+ if (this._digitControls[0] == null)
+ {
+ return;
+ }
+ long num = this._frequency;
+ for (int i = 11; i >= 0; i--)
+ {
+ long num2 = num / this._digitControls[i].Weight;
+ this._digitControls[i].DisplayedDigit = (int)num2;
+ num -= (long)this._digitControls[i].DisplayedDigit * this._digitControls[i].Weight;
+ }
+ this.UpdateDigitMask();
+ }
+
+ private void UpdateDigitMask()
+ {
+ long frequency = this._frequency;
+ if (frequency >= 0L)
+ {
+ for (int i = 1; i < 12; i++)
+ {
+ if ((i + 1) % 3 == 0 && i != 11)
+ {
+ int num = i / 3;
+ if (this._separatorControls[num] != null)
+ {
+ this._separatorControls[num].Masked = (this._digitControls[i + 1].Weight > frequency);
+ }
+ }
+ if (this._digitControls[i] != null)
+ {
+ this._digitControls[i].Masked = (this._digitControls[i].Weight > frequency);
+ }
+ }
+ }
+ }
+
+ private void DigitMouseLeave(object sender, EventArgs e)
+ {
+ if (!base.ClientRectangle.Contains(base.PointToClient(Control.MousePosition)) && this._currentEntryMode != EntryMode.None)
+ {
+ this.AbortEntryMode();
+ }
+ }
+
+ protected override void OnMouseLeave(EventArgs e)
+ {
+ base.OnMouseLeave(e);
+ if (!base.ClientRectangle.Contains(base.PointToClient(Control.MousePosition)) && this._currentEntryMode != EntryMode.None)
+ {
+ this.AbortEntryMode();
+ }
+ }
+
+ private long GetFrequencyValue()
+ {
+ long num = 0L;
+ for (int i = 0; i < this._digitControls.Length; i++)
+ {
+ num += this._digitControls[i].Weight * (long)this._digitControls[i].DisplayedDigit;
+ }
+ return num;
+ }
+
+ private void SetFrequencyValue(long newFrequency)
+ {
+ if (newFrequency != this._frequency)
+ {
+ this._frequencyChangingEventArgs.Accept = true;
+ this._frequencyChangingEventArgs.Frequency = newFrequency;
+ if (!this._disableFrequencyEvents && this.FrequencyChanging != null)
+ {
+ this.FrequencyChanging(this, this._frequencyChangingEventArgs);
+ }
+ if (this._frequencyChangingEventArgs.Accept)
+ {
+ this._frequency = this._frequencyChangingEventArgs.Frequency;
+ this.UpdateDigitsValues();
+ if (!this._disableFrequencyEvents && this.FrequencyChanged != null)
+ {
+ this.FrequencyChanged(this, EventArgs.Empty);
+ }
+ }
+ }
+ }
+
+ private void EnterDirectMode()
+ {
+ if (this._changingEntryMode)
+ {
+ return;
+ }
+ this._changingEntryMode = true;
+ for (int i = 0; i < this._digitControls.Length; i++)
+ {
+ if (this._digitControls[i] != null)
+ {
+ this._digitControls[i].Masked = false;
+ if (this._digitControls[i].CursorInside)
+ {
+ this._editModePosition = i;
+ this._digitControls[i].Highlight = true;
+ }
+ }
+ }
+ this.ZeroDigits(this._digitControls.Length - 1);
+ this._currentEntryMode = EntryMode.Direct;
+ this._changingEntryMode = false;
+ }
+
+ private void DirectModeHandler(KeyEventArgs args)
+ {
+ Keys keyCode = args.KeyCode;
+ if (keyCode <= Keys.Return)
+ {
+ if (keyCode != Keys.Back)
+ {
+ if (keyCode != Keys.Tab)
+ {
+ if (keyCode != Keys.Return)
+ {
+ return;
+ }
+ this.LeaveEntryMode();
+ return;
+ }
+ }
+ else
+ {
+ this._digitControls[this._editModePosition].DisplayedDigit = 0;
+ if (this._editModePosition < this._digitControls.Length - 1)
+ {
+ this._digitControls[this._editModePosition].Highlight = false;
+ this._editModePosition++;
+ this._digitControls[this._editModePosition].Highlight = true;
+ return;
+ }
+ return;
+ }
+ }
+ else
+ {
+ if (keyCode <= Keys.D9)
+ {
+ if (keyCode == Keys.Escape)
+ {
+ this.AbortEntryMode();
+ return;
+ }
+ switch (keyCode)
+ {
+ case Keys.Left:
+ if (this._editModePosition < this._digitControls.Length - 1)
+ {
+ this._digitControls[this._editModePosition].Highlight = false;
+ this._editModePosition++;
+ this._digitControls[this._editModePosition].Highlight = true;
+ return;
+ }
+ return;
+ case Keys.Up:
+ case Keys.Down:
+ case Keys.Select:
+ case Keys.Print:
+ case Keys.Execute:
+ case Keys.Snapshot:
+ case Keys.Insert:
+ case Keys.Delete:
+ case Keys.Help:
+ return;
+ case Keys.Right:
+ if (this._editModePosition > 0)
+ {
+ this._digitControls[this._editModePosition].Highlight = false;
+ this._editModePosition--;
+ this._digitControls[this._editModePosition].Highlight = true;
+ return;
+ }
+ return;
+ case Keys.D0:
+ case Keys.D1:
+ case Keys.D2:
+ case Keys.D3:
+ case Keys.D4:
+ case Keys.D5:
+ case Keys.D6:
+ case Keys.D7:
+ case Keys.D8:
+ case Keys.D9:
+ break;
+ default:
+ return;
+ }
+ }
+ else
+ {
+ switch (keyCode)
+ {
+ case Keys.NumPad0:
+ case Keys.NumPad1:
+ case Keys.NumPad2:
+ case Keys.NumPad3:
+ case Keys.NumPad4:
+ case Keys.NumPad5:
+ case Keys.NumPad6:
+ case Keys.NumPad7:
+ case Keys.NumPad8:
+ case Keys.NumPad9:
+ break;
+ case Keys.Multiply:
+ case Keys.Add:
+ case Keys.Separator:
+ case Keys.Subtract:
+ return;
+ case Keys.Decimal:
+ goto IL_249;
+ default:
+ if (keyCode != Keys.OemPeriod)
+ {
+ return;
+ }
+ goto IL_249;
+ }
+ }
+ int displayedDigit = (args.KeyCode >= Keys.D0 && args.KeyCode <= Keys.D9) ? (args.KeyCode - Keys.D0) : (args.KeyCode - Keys.NumPad0);
+ this._digitControls[this._editModePosition].DisplayedDigit = displayedDigit;
+ if (this._editModePosition > 0)
+ {
+ this._digitControls[this._editModePosition].Highlight = false;
+ this._editModePosition--;
+ this._digitControls[this._editModePosition].Highlight = true;
+ return;
+ }
+ this.LeaveEntryMode();
+ return;
+ }
+ IL_249:
+ this._digitControls[this._editModePosition].Highlight = false;
+ this._editModePosition -= this._editModePosition % 3 + 1;
+ if (this._editModePosition < 2)
+ {
+ if (args.KeyCode != Keys.Tab)
+ {
+ this._editModePosition = 0;
+ this.LeaveEntryMode();
+ return;
+ }
+ this._editModePosition = this._digitControls.Length - 1;
+ }
+ this._digitControls[this._editModePosition].Highlight = true;
+ }
+
+ private void EnterArrowMode()
+ {
+ if (this._changingEntryMode)
+ {
+ return;
+ }
+ this._changingEntryMode = true;
+ for (int i = 0; i < this._digitControls.Length; i++)
+ {
+ if (this._digitControls[i] != null)
+ {
+ this._digitControls[i].Masked = false;
+ if (this._digitControls[i].CursorInside)
+ {
+ this._editModePosition = i;
+ this._digitControls[i].Highlight = true;
+ }
+ }
+ }
+ this._currentEntryMode = EntryMode.Arrow;
+ this._changingEntryMode = false;
+ }
+
+ private void ArrowModeHandler(KeyEventArgs args)
+ {
+ Keys keyCode = args.KeyCode;
+ if (keyCode <= Keys.Return)
+ {
+ if (keyCode == Keys.Tab)
+ {
+ this._digitControls[this._editModePosition].Highlight = false;
+ this._editModePosition -= this._editModePosition % 3 + 1;
+ if (this._editModePosition < 2)
+ {
+ this._editModePosition = this._digitControls.Length - 1;
+ }
+ this._digitControls[this._editModePosition].Highlight = true;
+ return;
+ }
+ if (keyCode != Keys.Return)
+ {
+ return;
+ }
+ }
+ else if (keyCode != Keys.Escape)
+ {
+ switch (keyCode)
+ {
+ case Keys.Left:
+ if (this._editModePosition < this._digitControls.Length - 1)
+ {
+ this._digitControls[this._editModePosition].Highlight = false;
+ this._editModePosition++;
+ this._digitControls[this._editModePosition].Highlight = true;
+ return;
+ }
+ return;
+ case Keys.Up:
+ this._newFrequency = this._frequency;
+ this.IncrementDigit(this._editModePosition, false);
+ this.SetFrequencyValue(this._newFrequency);
+ return;
+ case Keys.Right:
+ if (this._editModePosition > 0)
+ {
+ this._digitControls[this._editModePosition].Highlight = false;
+ this._editModePosition--;
+ this._digitControls[this._editModePosition].Highlight = true;
+ return;
+ }
+ return;
+ case Keys.Down:
+ this._newFrequency = this._frequency;
+ this.DecrementDigit(this._editModePosition, false);
+ this.SetFrequencyValue(this._newFrequency);
+ return;
+ default:
+ return;
+ }
+ }
+ this.AbortEntryMode();
+ }
+
+ private void AbortEntryMode()
+ {
+ if (this._changingEntryMode)
+ {
+ return;
+ }
+ this._changingEntryMode = true;
+ this._digitControls[this._editModePosition].Highlight = false;
+ this.UpdateDigitsValues();
+ this._currentEntryMode = EntryMode.None;
+ this._changingEntryMode = false;
+ }
+
+ private void LeaveEntryMode()
+ {
+ if (this._changingEntryMode)
+ {
+ return;
+ }
+ this._changingEntryMode = true;
+ this._digitControls[this._editModePosition].Highlight = false;
+ if (this._currentEntryMode == EntryMode.Direct)
+ {
+ long frequencyValue = this.GetFrequencyValue();
+ this.SetFrequencyValue(frequencyValue);
+ }
+ this._currentEntryMode = EntryMode.None;
+ this._changingEntryMode = false;
+ }
+
+ private bool DigitKeyHandler(KeyEventArgs args)
+ {
+ if (!base.ClientRectangle.Contains(base.PointToClient(Control.MousePosition)) || this._changingEntryMode)
+ {
+ return false;
+ }
+ if (this._currentEntryMode != EntryMode.None)
+ {
+ EntryMode currentEntryMode = this._currentEntryMode;
+ if (currentEntryMode != EntryMode.Direct)
+ {
+ if (currentEntryMode == EntryMode.Arrow)
+ {
+ this.ArrowModeHandler(args);
+ }
+ }
+ else
+ {
+ this.DirectModeHandler(args);
+ }
+ return true;
+ }
+ if ((args.KeyCode >= Keys.D0 && args.KeyCode <= Keys.D9) || (args.KeyCode >= Keys.NumPad0 && args.KeyCode <= Keys.NumPad9))
+ {
+ this.EnterDirectMode();
+ this.DirectModeHandler(args);
+ return true;
+ }
+ if (args.KeyCode == Keys.Up || args.KeyCode == Keys.Down || args.KeyCode == Keys.Left || args.KeyCode == Keys.Right)
+ {
+ this.EnterArrowMode();
+ this.ArrowModeHandler(args);
+ return true;
+ }
+ if (args.Modifiers == Keys.Control)
+ {
+ if (args.KeyCode == Keys.C)
+ {
+ Clipboard.SetText(string.Format("{0}", this.GetFrequencyValue()), TextDataFormat.Text);
+ return true;
+ }
+ if (args.KeyCode == Keys.V)
+ {
+ long frequencyValue = 0L;
+ if (long.TryParse(Clipboard.GetText(), out frequencyValue))
+ {
+ this.SetFrequencyValue(frequencyValue);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
+ {
+ if (msg.Msg == 256 || msg.Msg == 260)
+ {
+ return this.DigitKeyHandler(new KeyEventArgs(keyData));
+ }
+ return base.ProcessCmdKey(ref msg, keyData);
+ }
+
+ private const int DigitCount = 12;
+
+ private const int DigitImageSplitCount = 12;
+
+ private const int DigitSeperatorCount = 12;
+
+ private readonly FrequencyEditDigit[] _digitControls = new FrequencyEditDigit[12];
+
+ private readonly FrequencyEditSeparator[] _separatorControls = new FrequencyEditSeparator[12];
+
+ private readonly ImageList _imageList = new ImageList();
+
+ private readonly Image _digitImages;
+
+ private readonly Timer _renderTimer = new Timer();
+
+ private readonly FrequencyChangingEventArgs _frequencyChangingEventArgs = new FrequencyChangingEventArgs();
+
+ private long _frequency;
+
+ private long _newFrequency;
+
+ private int _stepSize;
+
+ private int _editModePosition;
+
+ private bool _changingEntryMode;
+
+ private bool _disableFrequencyEvents;
+
+ private EntryMode _currentEntryMode;
+ }
+}
diff --git a/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/FrequencyEditDigit.cs b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/FrequencyEditDigit.cs
new file mode 100644
index 0000000..0d1298a
--- /dev/null
+++ b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/FrequencyEditDigit.cs
@@ -0,0 +1,271 @@
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Windows.Forms;
+
+namespace SDRSharp.FrequencyEdit
+{
+ internal sealed class FrequencyEditDigit : UserControl, IRenderable
+ {
+ public event OnDigitClickDelegate OnDigitClick;
+
+ public ImageList ImageList
+ {
+ get
+ {
+ return this._imageList;
+ }
+ set
+ {
+ this._imageList = value;
+ }
+ }
+
+ public bool Highlight
+ {
+ get
+ {
+ return this._highlight;
+ }
+ set
+ {
+ this._highlight = value;
+ this._renderNeeded = true;
+ }
+ }
+
+ public bool CursorInside
+ {
+ get
+ {
+ return this._cursorInside;
+ }
+ }
+
+ public int DisplayedDigit
+ {
+ get
+ {
+ return this._displayedDigit;
+ }
+ set
+ {
+ if (value >= 0 && value <= 9 && this._displayedDigit != value)
+ {
+ this._displayedDigit = value;
+ this._renderNeeded = true;
+ }
+ }
+ }
+
+ public int DigitIndex
+ {
+ get
+ {
+ return this._digitIndex;
+ }
+ }
+
+ public bool Masked
+ {
+ get
+ {
+ return this._masked;
+ }
+ set
+ {
+ if (this._masked != value)
+ {
+ this._masked = value;
+ this._renderNeeded = true;
+ }
+ }
+ }
+
+ public long Weight
+ {
+ get
+ {
+ return this._weight;
+ }
+ set
+ {
+ this._weight = value;
+ }
+ }
+
+ public FrequencyEditDigit(int digitIndex)
+ {
+ this.DoubleBuffered = true;
+ this.BackColor = Color.Transparent;
+ this._tickTimer.Tick += this.timer_Tick;
+ base.UpdateStyles();
+ this._digitIndex = digitIndex;
+ ColorMatrix colorMatrix = new ColorMatrix();
+ colorMatrix.Matrix33 = 0.3f;
+ this._maskedAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
+ }
+
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ if (this._imageList != null && this._displayedDigit < this._imageList.Images.Count)
+ {
+ Image image = this._imageList.Images[this._displayedDigit];
+ ImageAttributes imageAttrs = ((this._masked && !this._cursorInside) || !base.Parent.Enabled) ? this._maskedAttributes : null;
+ e.Graphics.DrawImage(image, new Rectangle(0, 0, base.Width, base.Height), 0f, 0f, (float)image.Width, (float)image.Height, GraphicsUnit.Pixel, imageAttrs);
+ }
+ if (this._cursorInside && !((FrequencyEdit)base.Parent).EntryModeActive)
+ {
+ bool flag = this._lastMouseY <= base.ClientRectangle.Height / 2;
+ using (SolidBrush solidBrush = new SolidBrush(Color.FromArgb(100, flag ? Color.Red : Color.Blue)))
+ {
+ if (flag)
+ {
+ e.Graphics.FillRectangle(solidBrush, new Rectangle(0, 0, base.ClientRectangle.Width, base.ClientRectangle.Height / 2));
+ }
+ else
+ {
+ e.Graphics.FillRectangle(solidBrush, new Rectangle(0, base.ClientRectangle.Height / 2, base.ClientRectangle.Width, base.ClientRectangle.Height));
+ }
+ }
+ }
+ if (this._highlight)
+ {
+ SolidBrush brush = new SolidBrush(Color.FromArgb(25, Color.Red));
+ e.Graphics.FillRectangle(brush, new Rectangle(0, 0, base.ClientRectangle.Width, base.ClientRectangle.Height));
+ }
+ }
+
+ protected override void OnMouseMove(MouseEventArgs e)
+ {
+ base.OnMouseMove(e);
+ this._isUpperHalf = (e.Y <= base.ClientRectangle.Height / 2);
+ this._lastMouseY = e.Y;
+ if (this._isUpperHalf != this._lastIsUpperHalf)
+ {
+ this._renderNeeded = true;
+ this._tickCount = 0;
+ }
+ this._lastIsUpperHalf = this._isUpperHalf;
+ }
+
+ protected override void OnMouseEnter(EventArgs e)
+ {
+ base.OnMouseEnter(e);
+ this._cursorInside = true;
+ this._renderNeeded = true;
+ base.Focus();
+ }
+
+ protected override void OnMouseLeave(EventArgs e)
+ {
+ base.OnMouseLeave(e);
+ this._cursorInside = false;
+ this._renderNeeded = true;
+ }
+
+ protected override void OnMouseDown(MouseEventArgs e)
+ {
+ base.OnMouseDown(e);
+ this._isUpperHalf = (e.Y <= base.ClientRectangle.Height / 2);
+ if (this.OnDigitClick != null)
+ {
+ this.OnDigitClick(this, new FrequencyEditDigitClickEventArgs(this._isUpperHalf, e.Button));
+ }
+ this._tickCount = 1;
+ this._tickTimer.Interval = 300;
+ this._tickTimer.Enabled = true;
+ }
+
+ protected override void OnMouseUp(MouseEventArgs e)
+ {
+ this._tickTimer.Enabled = false;
+ }
+
+ protected override void OnMouseWheel(MouseEventArgs e)
+ {
+ base.OnMouseWheel(e);
+ if (this.OnDigitClick != null)
+ {
+ this.OnDigitClick(this, new FrequencyEditDigitClickEventArgs(e.Delta > 0, e.Button));
+ }
+ }
+
+ public void Render()
+ {
+ if (this._renderNeeded)
+ {
+ base.Invalidate();
+ this._renderNeeded = false;
+ }
+ }
+
+ private void timer_Tick(object sender, EventArgs e)
+ {
+ if (this.OnDigitClick != null)
+ {
+ this.OnDigitClick(this, new FrequencyEditDigitClickEventArgs(this._isUpperHalf, MouseButtons.Left));
+ }
+ this._tickCount++;
+ int tickCount = this._tickCount;
+ if (tickCount <= 20)
+ {
+ if (tickCount == 10)
+ {
+ this._tickTimer.Interval = 200;
+ return;
+ }
+ if (tickCount != 20)
+ {
+ return;
+ }
+ this._tickTimer.Interval = 100;
+ return;
+ }
+ else
+ {
+ if (tickCount == 50)
+ {
+ this._tickTimer.Interval = 50;
+ return;
+ }
+ if (tickCount != 100)
+ {
+ return;
+ }
+ this._tickTimer.Interval = 20;
+ return;
+ }
+ }
+
+ private const float MaskedDigitTransparency = 0.3f;
+
+ private bool _masked;
+
+ private int _displayedDigit;
+
+ private long _weight;
+
+ private bool _renderNeeded;
+
+ private bool _cursorInside;
+
+ private bool _highlight;
+
+ private int _lastMouseY;
+
+ private bool _lastIsUpperHalf;
+
+ private bool _isUpperHalf;
+
+ private int _tickCount;
+
+ private ImageList _imageList;
+
+ private readonly int _digitIndex;
+
+ private readonly Timer _tickTimer = new Timer();
+
+ private readonly ImageAttributes _maskedAttributes = new ImageAttributes();
+ }
+}
diff --git a/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/FrequencyEditDigitClickEventArgs.cs b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/FrequencyEditDigitClickEventArgs.cs
new file mode 100644
index 0000000..f6cc3c9
--- /dev/null
+++ b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/FrequencyEditDigitClickEventArgs.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Windows.Forms;
+
+namespace SDRSharp.FrequencyEdit
+{
+ public class FrequencyEditDigitClickEventArgs
+ {
+ public FrequencyEditDigitClickEventArgs(bool isUpperHalf, MouseButtons button)
+ {
+ this.IsUpperHalf = isUpperHalf;
+ this.Button = button;
+ }
+
+ public bool IsUpperHalf;
+
+ public MouseButtons Button;
+ }
+}
diff --git a/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/FrequencyEditSeparator.cs b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/FrequencyEditSeparator.cs
new file mode 100644
index 0000000..2b7cc74
--- /dev/null
+++ b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/FrequencyEditSeparator.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Windows.Forms;
+
+namespace SDRSharp.FrequencyEdit
+{
+ internal sealed class FrequencyEditSeparator : UserControl, IRenderable
+ {
+ public Image Image
+ {
+ get
+ {
+ return this._image;
+ }
+ set
+ {
+ this._image = value;
+ }
+ }
+
+ public bool Masked
+ {
+ get
+ {
+ return this._masked;
+ }
+ set
+ {
+ if (this._masked != value)
+ {
+ this._masked = value;
+ this._renderNeeded = true;
+ }
+ }
+ }
+
+ public FrequencyEditSeparator()
+ {
+ this.DoubleBuffered = true;
+ base.UpdateStyles();
+ ColorMatrix colorMatrix = new ColorMatrix();
+ colorMatrix.Matrix33 = 0.3f;
+ this._maskedAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
+ }
+
+ public void Render()
+ {
+ if (this._renderNeeded)
+ {
+ base.Invalidate();
+ this._renderNeeded = false;
+ }
+ }
+
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ if (this._image != null)
+ {
+ ImageAttributes imageAttrs = (this._masked || !base.Parent.Enabled) ? this._maskedAttributes : null;
+ e.Graphics.DrawImage(this._image, new Rectangle(0, 0, base.Width, base.Height), 0f, 0f, (float)this._image.Width, (float)this._image.Height, GraphicsUnit.Pixel, imageAttrs);
+ }
+ }
+
+ private const float MaskedDigitTransparency = 0.3f;
+
+ private Image _image;
+
+ private bool _masked;
+
+ private bool _renderNeeded;
+
+ private readonly ImageAttributes _maskedAttributes = new ImageAttributes();
+ }
+}
diff --git a/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/IRenderable.cs b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/IRenderable.cs
new file mode 100644
index 0000000..8e7fa9c
--- /dev/null
+++ b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/IRenderable.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace SDRSharp.FrequencyEdit
+{
+ internal interface IRenderable
+ {
+ void Render();
+ }
+}
diff --git a/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/OnDigitClickDelegate.cs b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/OnDigitClickDelegate.cs
new file mode 100644
index 0000000..9fd38d5
--- /dev/null
+++ b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/OnDigitClickDelegate.cs
@@ -0,0 +1,6 @@
+using System;
+
+namespace SDRSharp.FrequencyEdit
+{
+ public delegate void OnDigitClickDelegate(object sender, FrequencyEditDigitClickEventArgs args);
+}
diff --git a/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/Properties/AssemblyInfo.cs b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..cbf8a45
--- /dev/null
+++ b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/Properties/AssemblyInfo.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Diagnostics;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+using System.Security;
+using System.Security.Permissions;
+
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: CompilationRelaxations(8)]
+[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
+[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
+[assembly: AssemblyTitle("Frequency Edit Control")]
+[assembly: AssemblyDescription("Frequency Edit Control")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("SDR#")]
+[assembly: AssemblyCopyright("Copyright © 2012 Youssef Touil, Ian Gilmour")]
+[assembly: AssemblyTrademark("")]
+[assembly: ComVisible(false)]
+[assembly: Guid("300880ef-fb6f-41ec-b607-d468751fe0ad")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
diff --git a/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/Properties/Resources.Designer.cs b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..9812f8e
--- /dev/null
+++ b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/Properties/Resources.Designer.cs
@@ -0,0 +1,59 @@
+using System;
+using System.CodeDom.Compiler;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Drawing;
+using System.Globalization;
+using System.Resources;
+using System.Runtime.CompilerServices;
+
+namespace SDRSharp.FrequencyEdit.Properties
+{
+ [GeneratedCode("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [DebuggerNonUserCode]
+ [CompilerGenerated]
+ internal class Resources
+ {
+ internal Resources()
+ {
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ internal static ResourceManager ResourceManager
+ {
+ get
+ {
+ if (Resources.resourceMan == null)
+ {
+ Resources.resourceMan = new ResourceManager("SDRSharp.FrequencyEdit.Properties.Resources", typeof(Resources).Assembly);
+ }
+ return Resources.resourceMan;
+ }
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ internal static CultureInfo Culture
+ {
+ get
+ {
+ return Resources.resourceCulture;
+ }
+ set
+ {
+ Resources.resourceCulture = value;
+ }
+ }
+
+ internal static Bitmap Numbers
+ {
+ get
+ {
+ return (Bitmap)Resources.ResourceManager.GetObject("Numbers", Resources.resourceCulture);
+ }
+ }
+
+ private static ResourceManager resourceMan;
+
+ private static CultureInfo resourceCulture;
+ }
+}
diff --git a/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/Properties/Resources.resx b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/Properties/Resources.resx
new file mode 100644
index 0000000..a3fd2aa
--- /dev/null
+++ b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/Properties/Resources.resx
@@ -0,0 +1,177 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAAM8AAAAWCAYAAABnsMi4AAAABGdBTUEAALGPC/xhBQAAABp0RVh0U29m
+ dHdhcmUAUGFpbnQuTkVUIHYzLjUuMTAw9HKhAAALqklEQVR4XtXcBawluRGF4QkzMzMzKszMzMykMEeh
+ DSqkMDMzMyjMzMzMzDA532pKcloN7vvenVWO9Gtn+7rdbttll8vut2dExwnXD08O7w179/GF8Oxwy3DK
+ cPDQq8OEk4Q7hEeG04Qeue884d7Bsz8Wqjz+7drdw+nDVHkOFy4V7hm80xvDH0Ll877wtOC9ThTW6B5B
+ Hj8Op3ahQ8cO9exenhF6dKrwqfD9cEUXFnSk8KYw9swlLhnGVP1HnarbSv+b8OLw4HDBcOgwp6OGq4dH
+ hdeGX4XK5/lBnzhDWNMPt6ZDhWuHj4a/hHrpIX8Lnw8MQcdc0sXDu8LXgvt/GM4clnSsoNJ0zHr2H8PP
+ ws/DP/ddw3fCAWGsPAxdhVda5Xe/fPCPUNc/E24UehqEEXw6uPepYakzlHRw9/wr6BBVjjkeHZZ0/PDu
+ IO/fh2uGJTEeHXrsmWOof/njEmGo84UPhLn+g5+Gx4Sp/nOm8M5Qz2vrqtoLXw63DfruQaZDhNuEKhRe
+ Hx4Y7rIPI7fRpE3zoHD4MCf5viF8JLin13hOG6T/bXhuuF+4SjhHOHdQaRqgNS4zwSFDK/l4pjyMejcO
+ 7pcPbh3MSJUHLhqWdPNQneR0LnSq3utH4cqhyjGHWXtOOs8zgwFA3jcIw3oYkzS8gLFnjvHyIH+dWAdv
+ ZTAxiPkd+s/9Q/UfbfPE0LbXvcKw48uHl1NpDKDuvVBQBu1lMGnzuVU4yGag8wajgYJ8NVw1GEkPFlqp
+ bB2vDMHorxPNyT3yulxwT6/x6DA3CUZUeQzLQozezGK0kzd3xVTeinEfLchjqoLlww35SZCPmfLoYUre
+ 58NB2teFsbJNSSdw39fDCVzYoXS+Owaj85/DfcM2OtJJgzIr+9MDl7rkeZ7rNwZskBrrP+r5bOGzQdpv
+ BTNx6bDhhcFvZp07hbl8PhGk5dXoB/tdCvzmUC9u1Fqq/PMHRuCeTwZ+7pJ0Tul7jUdn79XVgs4j/xu6
+ sIG88yOCPDTcWcKULhCk4x7xy0s6sk6FKYOqeuByzBlor7jFvwzytAY8QtiG7hz+HjzHANrKMw0ifvtm
+ OHGYE6OQFq37d/LwjeA6T2FpWXCNUGXatN13pHOFmnVMtUcMS9JJHhLcY7S7TljSWuNZIwv9LwX5cxU2
+ lUW2PKC8YzKTcSWk+WKwbiB1wpV8QbDIPUYYU3UcrklPXc/pKOE9QX48ht6gxVrpxIzdc6zzhmoDD2YV
+ s8Wcqi+gXZu119tBaUrHDBWUeI0L+1v3Cf8OCtAToSmJcNWLPsmFBW3TeMx8Ah3yf7ULG+pKod5pbEFM
+ FsW1tuB/l3SYlwTXuTfczTGJnEmjE65x98ZkzVCBk8uGneY3JnlWvfw13CwM1c48nwu8mTkpq7RoB6nr
+ hrpuUF+SAetFQXpBrG3NupMqHxPHdaFT3KqKfDCIJW3TeKxzKpRpcbqJdBIDiTwsiM8YhpLGrCINP5ub
+ UeoxHq7hc4I0Hw98dx3AvcXc2qyVDqou5WWNZnZo8/Jv+e/UoMxubw2eY3Yfc9GVt4JJggbtOmYo5dFG
+ 0lqjtsEWIW7XITjQoxqMlM3adr/qK8HDf3Hg/63Tq0K97JK2aTzCy1UOgYlNxFi+G+Qx5b7qFJXmoUHn
+ LOmwS8bTpvlQeHzg5olGFs8KDPRaQeRpSq2LKaxr9n9eqHz8W/4iURb7bVnXqNoNd3VhQicLbXmm1nNm
+ MVsF0ilzG3jgqlUe1tU9KuPhttre2K+qwqrwtfLydf+StmU8Kqw2T0XApmZPIzEDEVlsuUzgiiiXoIP1
+ n1FvbMQWmq+olg7Zqsd4dJSXhaqzOTyDC2QdJe9W1l0vDWP3DeFi6ljWgmvXWGZBRigfgYC5NZXZ5wGh
+ wvfSGwRuug8urg3qXwd1aK02rCMGUy7x7VxYkPLZMJV+N9aQq+XBMIus1eNC3b+kbRmP/Se+uLyFtqdc
+ nnZdNIUGndrj4U/XPob9jqF6jEfj6kD8dHsV1iy3D8L90MHsnJcfXzhF0RqQaFYFSH4XlMeelyCO+hA2
+ fmyoYELxsLAUwWplsPlBcC+jb2eJMfndXk77zDHkKdQ81PFChZ8NhHMzLx05VKTYBvems+vGqhf6fzMe
+ RmI/qp4vAjYnbgQ35i2BD++/sGfVHv24WzBLtTIL6dzSONojND5Uj/Eoc28I/qyh9q+UzyxZukhwHTZa
+ 5ySsXPlgTVDIyYm6b24dQwYX67maebhm7wgCOK8I6rpOZEAdCQq0g51/q/9K46iRNlaXInoGH56Gslwv
+ 8JYqrY3U/a56uGjJWj0h1P1L2k3j0ZlFverIj/DocHN0KA2jASyAW7h53DSBE3sGFXpvG1VI9P3Bsyz0
+ GSIDa9F5yiWzV3HCsFOJ+P0pyNNMUq6k9ZBrAjbndGFB3KHvBfc4iTDmkg5lpq5jTQabuVHdjFOBFG4X
+ V+rswfk0M53fzRLqhHfQhr2duGhVLmnt39hLk97MIj33zOaqdvJ7PfNiYUza0Uytf4xhtttYFTDQqdeq
+ 9d+XtJvGo7PXKPbt0I7Km0pDW+jK094Bgyk5yFjvyVAt6ocYdTWqNFwp+zlLR5eW1O6fCMWWy1VRKR2p
+ 5zBrG0ruCenqcFxK6W0YCyHPiXtXpzMMQkvvfeEgoin9w10YiKF5vraVZgxn8mpz+INhKljQ1uEYvYdu
+ R9WGqteE+oxEtQboMbzdMB7PNBqb8v8TjKZG1Z6RtEe3CMpoQes0d6nKvgazggW/0W1Tta6gQa46SBmP
+ +m/D5VNq8xE8WFpLOBrFyKQXFTRDz4kbKy2u4MKC2g6tjsYW+gyYkTNM9e80weWDgVM9+He527YYpvrA
+ Vo2n3SR17qpXok1VADH+Je3UeKwVnPquEYsBOZqym6rzd1DekjCsmWQOvjof3b3C/iJPrrt3U3F3Kqom
+ QGB2pHYzt2czsTUepyKWjMF6ovJ3bm5J7f5MO+hMaa0xD8VNtoZyv0Fl7n30Gy7kpSew2b+x2uM5Gmop
+ okJmgDoIyPdU2UvaifEok47IHZIHH7in06xVO4K2xtOjnoDBWjGWtwd5tsde6mwdeo6xGNntXQ3zGZOR
+ 2qartGa2nm+v2nrrOZHO/XxlkH5tiFnfE8Z2L5dSGPwgkxd5W1AYx+R7Xt4izwjmnm0dDC2dIjwlCEdD
+ 5MZp5N1y1UpGqPo0wWcQwyP3S9qG8XBNat/D5yElbktF0ISplwY8YWFtK/3S2T9uVwUKBCnmDK1ktqlv
+ bxwgbYMtY2rdQi7VmrYUaazwubD/0vpt62IwNfsI3fIzxyrAS+oUFSLs+SShtNZ4PF+YtfYqGI59jCWX
+ YyiL16VPEvzGZWsPyBqB16jXeLgoGtwzpzqN0dUAZb0hP65qG1Xzu91+vwmd2x+aai+GVsEC78eFmZL0
+ lda6bxgJm5LoYwVbrGG071RdG6ztW9UzuOK9EjWrqOdO15Oz2rt374HsEzdREAPq/n+kIWsqhA+NHM/n
+ HhglRHSMXkYVha50PR/DldYYj8Kajus5sDZrP2KbYvhhmg7gmWMfw/mvTcr2Iz/rlbkONqVe47FAFejw
+ TLOAIIVoYZXHiQL7UUsfjTFuLk+l0V6CJ9oK6lhebXtpY209JXVXaUVS18jso+7cq+zquu0/9mZsAfAc
+ ai9Im/QOUu0m96Zt1K2B8SyeudM4w8+whQI1NIxadRDUmqP3M+zSGuMZi5C0n07PYWOu1dxn2P5bp5K9
+ s3fnKk2NmnPqNR6zZ7tHoU51hmF5lJNrM1fPXEvrk7H2EjqWB6xzzE5DA2zF9fORm3zUl5PPa6TOfOlr
+ H2ysPFytuu5dhfZ7v2cyW9dnICJsQuebtFG31hpPiYVLPPUHQJyInfuDG1PaqfH0IuLVSsfr+QMgDpfu
+ 5MvOXuPhRvmGRTROZx2Wp/6wSe8fWvHdkPbyDu0fSGn/sMnwHN6YuOo127WRvbVSh+rSs+f+AEhPYKrE
+ eGo96m9VzA0Cu6Jl49mz578LaXeu1eI/rQAAAABJRU5ErkJggg==
+
+
+
\ No newline at end of file
diff --git a/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit.csproj b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit.csproj
new file mode 100644
index 0000000..5c7ad5a
--- /dev/null
+++ b/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit/SDRSharp.FrequencyEdit.csproj
@@ -0,0 +1,72 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {A50690CF-7A7A-4B31-9A03-B3A2AF0AF0E0}
+ Library
+ SDRSharp.FrequencyEdit
+ SDRSharp.FrequencyEdit
+ v4.6
+ 512
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ true
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ UserControl
+
+
+
+ UserControl
+
+
+
+
+
+ Resources.resx
+ True
+ True
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
\ No newline at end of file
diff --git a/SDRSharp.PanView/Properties/AssemblyInfo.cs b/SDRSharp.PanView/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..acedc97
--- /dev/null
+++ b/SDRSharp.PanView/Properties/AssemblyInfo.cs
@@ -0,0 +1,15 @@
+using System.Diagnostics;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.Versioning;
+using System.Security.Permissions;
+
+[assembly: CompilationRelaxations(8)]
+[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
+[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
+[assembly: AssemblyTitle("Panoramic Spectrum Viewer")]
+[assembly: AssemblyDescription("Panoramic Spectrum Viewer")]
+[assembly: AssemblyProduct("SDR#")]
+[assembly: AssemblyCopyright("Copyright © Youssef TOUIL 2012")]
+[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
+[assembly: AssemblyVersion("0.0.0.0")]
diff --git a/SDRSharp.PanView/SDRSharp.PanView.csproj b/SDRSharp.PanView/SDRSharp.PanView.csproj
new file mode 100644
index 0000000..21a325f
--- /dev/null
+++ b/SDRSharp.PanView/SDRSharp.PanView.csproj
@@ -0,0 +1,73 @@
+
+
+
+ {040E3622-194B-4FA6-9CBB-1A22BD52A13D}
+ Debug
+ AnyCPU
+ Library
+ SDRSharp.PanView
+ .NETFramework
+ v4.6
+ 4
+ True
+
+
+ AnyCPU
+
+
+ bin\Debug\
+ true
+ full
+ false
+
+
+ bin\Release\
+ true
+ pdbonly
+ true
+
+
+
+ ..\SDRSharp.Radio\bin\Debug\SDRSharp.Radio.dll
+
+
+ C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089\System.Windows.Forms.dll
+
+
+ C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll
+
+
+ C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Drawing\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll
+
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+
+
+
+
+
+ UserControl
+
+
+
+ UserControl
+
+
+
+
+ GradientDialog.cs
+
+
+
+
\ No newline at end of file
diff --git a/SDRSharp.PanView/SDRSharp.PanView.sln b/SDRSharp.PanView/SDRSharp.PanView.sln
new file mode 100644
index 0000000..1e62575
--- /dev/null
+++ b/SDRSharp.PanView/SDRSharp.PanView.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27428.2015
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SDRSharp.PanView", "SDRSharp.PanView.csproj", "{040E3622-194B-4FA6-9CBB-1A22BD52A13D}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {040E3622-194B-4FA6-9CBB-1A22BD52A13D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {040E3622-194B-4FA6-9CBB-1A22BD52A13D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {040E3622-194B-4FA6-9CBB-1A22BD52A13D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {040E3622-194B-4FA6-9CBB-1A22BD52A13D}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {A133AD22-210D-4E51-898D-75EF97AE9732}
+ EndGlobalSection
+EndGlobal
diff --git a/SDRSharp.PanView/SDRSharp.PanView/BandType.cs b/SDRSharp.PanView/SDRSharp.PanView/BandType.cs
new file mode 100644
index 0000000..66cc6bd
--- /dev/null
+++ b/SDRSharp.PanView/SDRSharp.PanView/BandType.cs
@@ -0,0 +1,9 @@
+namespace SDRSharp.PanView
+{
+ public enum BandType
+ {
+ Lower,
+ Upper,
+ Center
+ }
+}
diff --git a/SDRSharp.PanView/SDRSharp.PanView/BandwidthEventArgs.cs b/SDRSharp.PanView/SDRSharp.PanView/BandwidthEventArgs.cs
new file mode 100644
index 0000000..23adebd
--- /dev/null
+++ b/SDRSharp.PanView/SDRSharp.PanView/BandwidthEventArgs.cs
@@ -0,0 +1,38 @@
+using System;
+
+namespace SDRSharp.PanView
+{
+ public class BandwidthEventArgs : EventArgs
+ {
+ public int Bandwidth
+ {
+ get;
+ set;
+ }
+
+ public int Offset
+ {
+ get;
+ set;
+ }
+
+ public bool Cancel
+ {
+ get;
+ set;
+ }
+
+ public BandType Side
+ {
+ get;
+ private set;
+ }
+
+ public BandwidthEventArgs(int bandwidth, int offset, BandType side)
+ {
+ this.Bandwidth = bandwidth;
+ this.Offset = offset;
+ this.Side = side;
+ }
+ }
+}
diff --git a/SDRSharp.PanView/SDRSharp.PanView/CustomPaintEventArgs.cs b/SDRSharp.PanView/SDRSharp.PanView/CustomPaintEventArgs.cs
new file mode 100644
index 0000000..bdd84f9
--- /dev/null
+++ b/SDRSharp.PanView/SDRSharp.PanView/CustomPaintEventArgs.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Drawing;
+
+namespace SDRSharp.PanView
+{
+ public class CustomPaintEventArgs : EventArgs
+ {
+ public Graphics Graphics
+ {
+ get;
+ private set;
+ }
+
+ public Point CursorPosition
+ {
+ get;
+ private set;
+ }
+
+ public string CustomTitle
+ {
+ get;
+ set;
+ }
+
+ public bool Cancel
+ {
+ get;
+ set;
+ }
+
+ public CustomPaintEventArgs(Graphics graphics, Point cursorPosition)
+ {
+ this.Graphics = graphics;
+ this.CursorPosition = cursorPosition;
+ }
+ }
+}
diff --git a/SDRSharp.PanView/SDRSharp.PanView/CustomPaintEventHandler.cs b/SDRSharp.PanView/SDRSharp.PanView/CustomPaintEventHandler.cs
new file mode 100644
index 0000000..1bfb161
--- /dev/null
+++ b/SDRSharp.PanView/SDRSharp.PanView/CustomPaintEventHandler.cs
@@ -0,0 +1,4 @@
+namespace SDRSharp.PanView
+{
+ public delegate void CustomPaintEventHandler(object sender, CustomPaintEventArgs e);
+}
diff --git a/SDRSharp.PanView/SDRSharp.PanView/FrequencyChangeSource.cs b/SDRSharp.PanView/SDRSharp.PanView/FrequencyChangeSource.cs
new file mode 100644
index 0000000..c3d4662
--- /dev/null
+++ b/SDRSharp.PanView/SDRSharp.PanView/FrequencyChangeSource.cs
@@ -0,0 +1,9 @@
+namespace SDRSharp.PanView
+{
+ public enum FrequencyChangeSource
+ {
+ Scroll,
+ Drag,
+ Click
+ }
+}
diff --git a/SDRSharp.PanView/SDRSharp.PanView/FrequencyEventArgs.cs b/SDRSharp.PanView/SDRSharp.PanView/FrequencyEventArgs.cs
new file mode 100644
index 0000000..10560ef
--- /dev/null
+++ b/SDRSharp.PanView/SDRSharp.PanView/FrequencyEventArgs.cs
@@ -0,0 +1,31 @@
+using System;
+
+namespace SDRSharp.PanView
+{
+ public class FrequencyEventArgs : EventArgs
+ {
+ public long Frequency
+ {
+ get;
+ set;
+ }
+
+ public FrequencyChangeSource Source
+ {
+ get;
+ set;
+ }
+
+ public bool Cancel
+ {
+ get;
+ set;
+ }
+
+ public FrequencyEventArgs(long frequency, FrequencyChangeSource source)
+ {
+ this.Frequency = frequency;
+ this.Source = source;
+ }
+ }
+}
diff --git a/SDRSharp.PanView/SDRSharp.PanView/GradientDialog.cs b/SDRSharp.PanView/SDRSharp.PanView/GradientDialog.cs
new file mode 100644
index 0000000..41d3a98
--- /dev/null
+++ b/SDRSharp.PanView/SDRSharp.PanView/GradientDialog.cs
@@ -0,0 +1,269 @@
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Windows.Forms;
+
+namespace SDRSharp.PanView
+{
+ public class GradientDialog : Form
+ {
+ private IContainer components;
+
+ private ListBox colorListBox;
+
+ private Button upButton;
+
+ private Button downButton;
+
+ private PictureBox gradientPictureBox;
+
+ private Button addButton;
+
+ private Button deleteButton;
+
+ private Button cancelButton;
+
+ private Button okButton;
+
+ private ColorDialog colorDialog;
+
+ private GradientDialog()
+ {
+ this.InitializeComponent();
+ }
+
+ public static ColorBlend GetGradient(ColorBlend originalBlend)
+ {
+ using (GradientDialog gradientDialog = new GradientDialog())
+ {
+ gradientDialog.SetColorBlend(originalBlend);
+ if (gradientDialog.ShowDialog() == DialogResult.OK)
+ {
+ return gradientDialog.GetColorBlend();
+ }
+ }
+ return null;
+ }
+
+ private ColorBlend GetColorBlend()
+ {
+ ColorBlend colorBlend = new ColorBlend(this.colorListBox.Items.Count);
+ float num = 1f / (float)(colorBlend.Positions.Length - 1);
+ for (int i = 0; i < colorBlend.Positions.Length; i++)
+ {
+ colorBlend.Positions[i] = (float)i * num;
+ colorBlend.Colors[i] = (Color)this.colorListBox.Items[i];
+ }
+ return colorBlend;
+ }
+
+ private void SetColorBlend(ColorBlend colorBlend)
+ {
+ for (int i = 0; i < colorBlend.Positions.Length; i++)
+ {
+ this.colorListBox.Items.Add(colorBlend.Colors[i]);
+ }
+ }
+
+ private void colorListBox_DrawItem(object sender, DrawItemEventArgs e)
+ {
+ if (e.Index >= 0)
+ {
+ Color color = (Color)this.colorListBox.Items[e.Index];
+ Rectangle bounds;
+ if ((e.State & DrawItemState.Selected) == DrawItemState.None)
+ {
+ using (SolidBrush solidBrush = new SolidBrush(color))
+ {
+ Graphics graphics = e.Graphics;
+ SolidBrush brush = solidBrush;
+ bounds = e.Bounds;
+ int x = bounds.Left + 1;
+ bounds = e.Bounds;
+ int y = bounds.Top + 1;
+ bounds = e.Bounds;
+ int width = bounds.Width - 2;
+ bounds = e.Bounds;
+ graphics.FillRectangle(brush, x, y, width, bounds.Height - 1);
+ }
+ }
+ else
+ {
+ using (HatchBrush hatchBrush = new HatchBrush(HatchStyle.Percent70, color, Color.Gray))
+ {
+ Graphics graphics2 = e.Graphics;
+ HatchBrush brush2 = hatchBrush;
+ bounds = e.Bounds;
+ int x2 = bounds.Left + 1;
+ bounds = e.Bounds;
+ int y2 = bounds.Top + 1;
+ bounds = e.Bounds;
+ int width2 = bounds.Width - 2;
+ bounds = e.Bounds;
+ graphics2.FillRectangle(brush2, x2, y2, width2, bounds.Height - 1);
+ }
+ }
+ }
+ }
+
+ private void upButton_Click(object sender, EventArgs e)
+ {
+ int selectedIndex = this.colorListBox.SelectedIndex;
+ if (selectedIndex > 0)
+ {
+ object item = this.colorListBox.Items[selectedIndex];
+ this.colorListBox.Items.RemoveAt(selectedIndex);
+ this.colorListBox.Items.Insert(selectedIndex - 1, item);
+ this.colorListBox.SelectedIndex = selectedIndex - 1;
+ this.gradientPictureBox.Invalidate();
+ }
+ }
+
+ private void downButton_Click(object sender, EventArgs e)
+ {
+ int selectedIndex = this.colorListBox.SelectedIndex;
+ if (selectedIndex >= 0 && selectedIndex < this.colorListBox.Items.Count - 1)
+ {
+ object item = this.colorListBox.Items[selectedIndex];
+ this.colorListBox.Items.RemoveAt(selectedIndex);
+ this.colorListBox.Items.Insert(selectedIndex + 1, item);
+ this.colorListBox.SelectedIndex = selectedIndex + 1;
+ this.gradientPictureBox.Invalidate();
+ }
+ }
+
+ private void addButton_Click(object sender, EventArgs e)
+ {
+ if (this.colorDialog.ShowDialog(this) == DialogResult.OK)
+ {
+ this.colorListBox.Items.Add(this.colorDialog.Color);
+ this.gradientPictureBox.Invalidate();
+ }
+ }
+
+ private void deleteButton_Click(object sender, EventArgs e)
+ {
+ int selectedIndex = this.colorListBox.SelectedIndex;
+ if (selectedIndex >= 0 && this.colorListBox.Items.Count > 2)
+ {
+ this.colorListBox.Items.RemoveAt(selectedIndex);
+ this.gradientPictureBox.Invalidate();
+ }
+ }
+
+ private void gradientPictureBox_Paint(object sender, PaintEventArgs e)
+ {
+ ColorBlend colorBlend = this.GetColorBlend();
+ using (LinearGradientBrush linearGradientBrush = new LinearGradientBrush(this.gradientPictureBox.ClientRectangle, Color.White, Color.Black, LinearGradientMode.Vertical))
+ {
+ linearGradientBrush.InterpolationColors = colorBlend;
+ e.Graphics.FillRectangle(linearGradientBrush, e.ClipRectangle);
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && this.components != null)
+ {
+ this.components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ private void InitializeComponent()
+ {
+ this.colorListBox = new ListBox();
+ this.upButton = new Button();
+ this.downButton = new Button();
+ this.gradientPictureBox = new PictureBox();
+ this.addButton = new Button();
+ this.deleteButton = new Button();
+ this.cancelButton = new Button();
+ this.okButton = new Button();
+ this.colorDialog = new ColorDialog();
+ ((ISupportInitialize)this.gradientPictureBox).BeginInit();
+ base.SuspendLayout();
+ this.colorListBox.DrawMode = DrawMode.OwnerDrawVariable;
+ this.colorListBox.FormattingEnabled = true;
+ this.colorListBox.Location = new Point(12, 12);
+ this.colorListBox.Name = "colorListBox";
+ this.colorListBox.Size = new Size(107, 238);
+ this.colorListBox.TabIndex = 0;
+ this.colorListBox.DrawItem += this.colorListBox_DrawItem;
+ this.upButton.Location = new Point(164, 12);
+ this.upButton.Name = "upButton";
+ this.upButton.Size = new Size(75, 23);
+ this.upButton.TabIndex = 1;
+ this.upButton.Text = "Up";
+ this.upButton.UseVisualStyleBackColor = true;
+ this.upButton.Click += this.upButton_Click;
+ this.downButton.Location = new Point(164, 41);
+ this.downButton.Name = "downButton";
+ this.downButton.Size = new Size(75, 23);
+ this.downButton.TabIndex = 2;
+ this.downButton.Text = "Down";
+ this.downButton.UseVisualStyleBackColor = true;
+ this.downButton.Click += this.downButton_Click;
+ this.gradientPictureBox.BorderStyle = BorderStyle.FixedSingle;
+ this.gradientPictureBox.Location = new Point(125, 12);
+ this.gradientPictureBox.Name = "gradientPictureBox";
+ this.gradientPictureBox.Size = new Size(33, 238);
+ this.gradientPictureBox.TabIndex = 3;
+ this.gradientPictureBox.TabStop = false;
+ this.gradientPictureBox.Paint += this.gradientPictureBox_Paint;
+ this.addButton.Location = new Point(164, 70);
+ this.addButton.Name = "addButton";
+ this.addButton.Size = new Size(75, 23);
+ this.addButton.TabIndex = 3;
+ this.addButton.Text = "Add";
+ this.addButton.UseVisualStyleBackColor = true;
+ this.addButton.Click += this.addButton_Click;
+ this.deleteButton.Location = new Point(164, 99);
+ this.deleteButton.Name = "deleteButton";
+ this.deleteButton.Size = new Size(75, 23);
+ this.deleteButton.TabIndex = 4;
+ this.deleteButton.Text = "Delete";
+ this.deleteButton.UseVisualStyleBackColor = true;
+ this.deleteButton.Click += this.deleteButton_Click;
+ this.cancelButton.DialogResult = DialogResult.Cancel;
+ this.cancelButton.Location = new Point(164, 227);
+ this.cancelButton.Name = "cancelButton";
+ this.cancelButton.Size = new Size(75, 23);
+ this.cancelButton.TabIndex = 6;
+ this.cancelButton.Text = "Cancel";
+ this.cancelButton.UseVisualStyleBackColor = true;
+ this.okButton.DialogResult = DialogResult.OK;
+ this.okButton.Location = new Point(164, 198);
+ this.okButton.Name = "okButton";
+ this.okButton.Size = new Size(75, 23);
+ this.okButton.TabIndex = 5;
+ this.okButton.Text = "OK";
+ this.okButton.UseVisualStyleBackColor = true;
+ this.colorDialog.AnyColor = true;
+ this.colorDialog.FullOpen = true;
+ base.AcceptButton = this.okButton;
+ base.AutoScaleDimensions = new SizeF(6f, 13f);
+ base.AutoScaleMode = AutoScaleMode.Font;
+ base.CancelButton = this.cancelButton;
+ base.ClientSize = new Size(251, 262);
+ base.Controls.Add(this.okButton);
+ base.Controls.Add(this.cancelButton);
+ base.Controls.Add(this.deleteButton);
+ base.Controls.Add(this.addButton);
+ base.Controls.Add(this.gradientPictureBox);
+ base.Controls.Add(this.downButton);
+ base.Controls.Add(this.upButton);
+ base.Controls.Add(this.colorListBox);
+ base.FormBorderStyle = FormBorderStyle.FixedDialog;
+ base.MaximizeBox = false;
+ base.MinimizeBox = false;
+ base.Name = "GradientDialog";
+ base.ShowInTaskbar = false;
+ base.StartPosition = FormStartPosition.CenterParent;
+ this.Text = "Gradient Editor";
+ ((ISupportInitialize)this.gradientPictureBox).EndInit();
+ base.ResumeLayout(false);
+ }
+ }
+}
diff --git a/SDRSharp.PanView/SDRSharp.PanView/GradientDialog.resx b/SDRSharp.PanView/SDRSharp.PanView/GradientDialog.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/SDRSharp.PanView/SDRSharp.PanView/GradientDialog.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/SDRSharp.PanView/SDRSharp.PanView/LineInsertEventArgs.cs b/SDRSharp.PanView/SDRSharp.PanView/LineInsertEventArgs.cs
new file mode 100644
index 0000000..5194747
--- /dev/null
+++ b/SDRSharp.PanView/SDRSharp.PanView/LineInsertEventArgs.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace SDRSharp.PanView
+{
+ public class LineInsertEventArgs : EventArgs
+ {
+ public unsafe int* RgbBuffer
+ {
+ get;
+ private set;
+ }
+
+ public int Length
+ {
+ get;
+ private set;
+ }
+
+ public unsafe LineInsertEventArgs(int* rgbBuffer, int length)
+ {
+ rgbBuffer = this.RgbBuffer;
+ this.Length = length;
+ }
+ }
+}
diff --git a/SDRSharp.PanView/SDRSharp.PanView/LineInsertEventHandler.cs b/SDRSharp.PanView/SDRSharp.PanView/LineInsertEventHandler.cs
new file mode 100644
index 0000000..9dff765
--- /dev/null
+++ b/SDRSharp.PanView/SDRSharp.PanView/LineInsertEventHandler.cs
@@ -0,0 +1,4 @@
+namespace SDRSharp.PanView
+{
+ public delegate void LineInsertEventHandler(object sender, LineInsertEventArgs e);
+}
diff --git a/SDRSharp.PanView/SDRSharp.PanView/ManualBandwidthChangeEventHandler.cs b/SDRSharp.PanView/SDRSharp.PanView/ManualBandwidthChangeEventHandler.cs
new file mode 100644
index 0000000..e178224
--- /dev/null
+++ b/SDRSharp.PanView/SDRSharp.PanView/ManualBandwidthChangeEventHandler.cs
@@ -0,0 +1,4 @@
+namespace SDRSharp.PanView
+{
+ public delegate void ManualBandwidthChangeEventHandler(object sender, BandwidthEventArgs e);
+}
diff --git a/SDRSharp.PanView/SDRSharp.PanView/ManualFrequencyChangeEventHandler.cs b/SDRSharp.PanView/SDRSharp.PanView/ManualFrequencyChangeEventHandler.cs
new file mode 100644
index 0000000..30ac633
--- /dev/null
+++ b/SDRSharp.PanView/SDRSharp.PanView/ManualFrequencyChangeEventHandler.cs
@@ -0,0 +1,4 @@
+namespace SDRSharp.PanView
+{
+ public delegate void ManualFrequencyChangeEventHandler(object sender, FrequencyEventArgs e);
+}
diff --git a/SDRSharp.PanView/SDRSharp.PanView/PeakDetector.cs b/SDRSharp.PanView/SDRSharp.PanView/PeakDetector.cs
new file mode 100644
index 0000000..5040db0
--- /dev/null
+++ b/SDRSharp.PanView/SDRSharp.PanView/PeakDetector.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+
+namespace SDRSharp.PanView
+{
+ public sealed class PeakDetector
+ {
+ private const byte Threshold = 20;
+
+ public static void GetPeaks(byte[] buffer, List peaks, int windowSize)
+ {
+ windowSize |= 1;
+ int halfSize = windowSize / 2;
+ float num = 1f / (float)windowSize;
+ peaks.Clear();
+ for (int i = 0; i < buffer.Length; i++)
+ {
+ int num2 = 0;
+ int max_index = i;
+ for (int j = 0; j < windowSize; j++)
+ {
+ int num3 = i + j - halfSize;
+ if (num3 < 0)
+ {
+ num3 = 0;
+ }
+ if (num3 >= buffer.Length)
+ {
+ num3 = buffer.Length - 1;
+ }
+ if (buffer[num3] >= buffer[max_index])
+ {
+ max_index = num3;
+ }
+ num2 += buffer[num3];
+ }
+ float num4 = (float)num2 * num;
+ if ((float)(int)buffer[max_index] - num4 > 20f && !peaks.Exists(delegate(int x)
+ {
+ if (Math.Abs(max_index - x) <= halfSize)
+ {
+ return buffer[x] > buffer[max_index];
+ }
+ return false;
+ }))
+ {
+ peaks.RemoveAll((int x) => Math.Abs(max_index - x) <= halfSize);
+ peaks.Add(max_index);
+ }
+ }
+ }
+ }
+}
diff --git a/SDRSharp.PanView/SDRSharp.PanView/SpectrumAnalyzer.cs b/SDRSharp.PanView/SDRSharp.PanView/SpectrumAnalyzer.cs
new file mode 100644
index 0000000..91579f6
--- /dev/null
+++ b/SDRSharp.PanView/SDRSharp.PanView/SpectrumAnalyzer.cs
@@ -0,0 +1,1730 @@
+using SDRSharp.Radio;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
+using System.Windows.Forms;
+
+namespace SDRSharp.PanView
+{
+ public class SpectrumAnalyzer : UserControl
+ {
+ private const int RefreshInterval = 20;
+
+ private const int GradientAlpha = 180;
+
+ private const int SnrMeterWidth = 10;
+
+ private static readonly Color _spectrumEnvelopeColor = Utils.GetColorSetting("spectrumAnalyzer.envelopeColor", Color.DarkGray);
+
+ private static readonly Color _spectrumFillColor = Utils.GetColorSetting("spectrumAnalyzer.fillColor", Color.DodgerBlue, 100);
+
+ private static readonly bool _useAntiAliasedDisplay = Utils.GetBooleanSetting("useAntiAliasedDisplay", false);
+
+ private float _attack;
+
+ private float _decay;
+
+ private bool _performNeeded;
+
+ private byte[] _scaledSpectrumEnvelope;
+
+ private byte[] _scaledSpectrumMinimum;
+
+ private float[] _smoothedSpectrumEnvelope;
+
+ private float[] _smoothedSpectrumMinimum;
+
+ private float[] _temp;
+
+ private float _peak;
+
+ private float _floor;
+
+ private float _snr;
+
+ private List _peaks = new List();
+
+ private Bitmap _buffer;
+
+ private Graphics _graphics;
+
+ private long _spectrumWidth;
+
+ private long _centerFrequency;
+
+ private long _displayCenterFrequency;
+
+ private Point[] _envelopePoints;
+
+ private Point[] _minMaxPoints;
+
+ private BandType _bandType;
+
+ private BandType _side;
+
+ private bool _useStepSizeForDisplay;
+
+ private int _filterBandwidth;
+
+ private int _filterOffset;
+
+ private int _stepSize = 1000;
+
+ private float _xIncrement;
+
+ private long _frequency;
+
+ private float _lower;
+
+ private float _upper;
+
+ private int _zoom;
+
+ private float _scale = 1f;
+
+ private int _oldX;
+
+ private float _snappedX;
+
+ private long _trackingFrequency;
+
+ private int _oldFilterBandwidth;
+
+ private int _displayedBandwidth;
+
+ private long _oldFrequency;
+
+ private long _oldCenterFrequency;
+
+ private bool _changingBandwidth;
+
+ private bool _changingFrequency;
+
+ private bool _changingCenterFrequency;
+
+ private bool _useSmoothing;
+
+ private bool _enableFilter = true;
+
+ private bool _enableHotTracking = true;
+
+ private bool _enableFrequencyMarker = true;
+
+ private bool _enableSideFilterResize;
+
+ private bool _enableFilterMove = true;
+
+ private bool _enableSnrBar;
+
+ private bool _hotTrackNeeded;
+
+ private bool _useSnap;
+
+ private bool _markPeaks;
+
+ private float _trackingPower;
+
+ private string _statusText;
+
+ private int _displayRange = 130;
+
+ private int _displayOffset;
+
+ private int _contrast;
+
+ private Point _cursorPosition;
+
+ private string _customTitle;
+
+ private SpectrumStyle _spectrumStyle = SpectrumStyle.StaticGradient;
+
+ private Pen[] _gradientPens = new Pen[256];
+
+ private Pen[] _snrGradientPens = new Pen[100];
+
+ private Timer _performTimer;
+
+ private LinearGradientBrush _gradientBrush;
+
+ private ColorBlend _staticGradient = Utils.GetGradientBlend(180, "spectrumAnalyzer.gradient");
+
+ private ColorBlend _verticalLinesGradient = Utils.GetGradientBlend(255, "waterfall.gradient");
+
+ private ColorBlend _snrGradient = Utils.GetGradientBlend(255, "spectrumAnalyzer.snrGradient");
+
+ public int SpectrumWidth
+ {
+ get
+ {
+ return (int)this._spectrumWidth;
+ }
+ set
+ {
+ if (this._spectrumWidth != value)
+ {
+ this._spectrumWidth = value;
+ this.ApplyZoom();
+ }
+ }
+ }
+
+ public int FilterBandwidth
+ {
+ get
+ {
+ return this._filterBandwidth;
+ }
+ set
+ {
+ if (this._filterBandwidth != value)
+ {
+ this._filterBandwidth = value;
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ public int FilterOffset
+ {
+ get
+ {
+ return this._filterOffset;
+ }
+ set
+ {
+ if (this._filterOffset != value)
+ {
+ this._filterOffset = value;
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ public BandType BandType
+ {
+ get
+ {
+ return this._bandType;
+ }
+ set
+ {
+ if (this._bandType != value)
+ {
+ this._bandType = value;
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ public bool UseStepSizeForDisplay
+ {
+ get
+ {
+ return this._useStepSizeForDisplay;
+ }
+ set
+ {
+ if (this._useStepSizeForDisplay != value)
+ {
+ this._useStepSizeForDisplay = value;
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ public long Frequency
+ {
+ get
+ {
+ return this._frequency;
+ }
+ set
+ {
+ if (this._frequency != value)
+ {
+ this._frequency = value;
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ public long CenterFrequency
+ {
+ get
+ {
+ return this._centerFrequency;
+ }
+ set
+ {
+ if (this._centerFrequency != value)
+ {
+ this._displayCenterFrequency += value - this._centerFrequency;
+ this._centerFrequency = value;
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ public int DisplayedBandwidth
+ {
+ get
+ {
+ return this._displayedBandwidth;
+ }
+ }
+
+ public long DisplayCenterFrequency
+ {
+ get
+ {
+ return this._displayCenterFrequency;
+ }
+ }
+
+ public int DisplayRange
+ {
+ get
+ {
+ return this._displayRange;
+ }
+ set
+ {
+ if (this._displayRange != value)
+ {
+ this._displayRange = value;
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ public int DisplayOffset
+ {
+ get
+ {
+ return this._displayOffset;
+ }
+ set
+ {
+ if (this._displayOffset != value)
+ {
+ this._displayOffset = value;
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ public int Zoom
+ {
+ get
+ {
+ return this._zoom;
+ }
+ set
+ {
+ if (this._zoom != value)
+ {
+ this._zoom = value;
+ this.ApplyZoom();
+ }
+ }
+ }
+
+ public bool UseSmoothing
+ {
+ get
+ {
+ return this._useSmoothing;
+ }
+ set
+ {
+ this._useSmoothing = value;
+ }
+ }
+
+ public bool EnableFilter
+ {
+ get
+ {
+ return this._enableFilter;
+ }
+ set
+ {
+ this._enableFilter = value;
+ this._performNeeded = true;
+ }
+ }
+
+ public bool EnableSNR
+ {
+ get
+ {
+ return this._enableSnrBar;
+ }
+ set
+ {
+ this._enableSnrBar = value;
+ this._performNeeded = true;
+ }
+ }
+
+ public float VisualSNR
+ {
+ get
+ {
+ return this._snr;
+ }
+ }
+
+ public bool EnableHotTracking
+ {
+ get
+ {
+ return this._enableHotTracking;
+ }
+ set
+ {
+ this._enableHotTracking = value;
+ this._performNeeded = true;
+ }
+ }
+
+ public bool EnableFrequencyMarker
+ {
+ get
+ {
+ return this._enableFrequencyMarker;
+ }
+ set
+ {
+ this._enableFrequencyMarker = value;
+ this._performNeeded = true;
+ }
+ }
+
+ public bool EnableSideFilterResize
+ {
+ get
+ {
+ return this._enableSideFilterResize;
+ }
+ set
+ {
+ this._enableSideFilterResize = value;
+ this._performNeeded = true;
+ }
+ }
+
+ public bool EnableFilterMove
+ {
+ get
+ {
+ return this._enableFilterMove;
+ }
+ set
+ {
+ this._enableFilterMove = value;
+ this._performNeeded = true;
+ }
+ }
+
+ public string StatusText
+ {
+ get
+ {
+ return this._statusText;
+ }
+ set
+ {
+ if (this._statusText != value)
+ {
+ this._statusText = value;
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public ColorBlend GradientColorBlend
+ {
+ get
+ {
+ return this._staticGradient;
+ }
+ set
+ {
+ this._staticGradient = new ColorBlend(value.Colors.Length);
+ for (int i = 0; i < value.Colors.Length; i++)
+ {
+ this._staticGradient.Colors[i] = Color.FromArgb(180, value.Colors[i]);
+ this._staticGradient.Positions[i] = value.Positions[i];
+ }
+ this._gradientBrush.Dispose();
+ this._gradientBrush = new LinearGradientBrush(new Rectangle(30, 30, this._buffer.Width - 30, this._buffer.Height - 30), Color.White, Color.Black, LinearGradientMode.Vertical);
+ this._gradientBrush.InterpolationColors = this._staticGradient;
+ this._performNeeded = true;
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public ColorBlend SNRGradient
+ {
+ get
+ {
+ return this._snrGradient;
+ }
+ set
+ {
+ if (this._snrGradient != value)
+ {
+ this._snrGradient = value;
+ this.BuildSNRGradientVector();
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public ColorBlend VerticalLinesGradient
+ {
+ get
+ {
+ return this._verticalLinesGradient;
+ }
+ set
+ {
+ if (this._verticalLinesGradient != value)
+ {
+ this._verticalLinesGradient = value;
+ this.BuildDynamicGradientVector();
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ public SpectrumStyle SpectrumStyle
+ {
+ get
+ {
+ return this._spectrumStyle;
+ }
+ set
+ {
+ if (this._spectrumStyle != value)
+ {
+ this._spectrumStyle = value;
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ public int Contrast
+ {
+ get
+ {
+ return this._contrast;
+ }
+ set
+ {
+ this._contrast = value;
+ }
+ }
+
+ public float Attack
+ {
+ get
+ {
+ return this._attack;
+ }
+ set
+ {
+ this._attack = value;
+ }
+ }
+
+ public float Decay
+ {
+ get
+ {
+ return this._decay;
+ }
+ set
+ {
+ this._decay = value;
+ }
+ }
+
+ public int StepSize
+ {
+ get
+ {
+ return this._stepSize;
+ }
+ set
+ {
+ if (this._stepSize != value)
+ {
+ this._stepSize = value;
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ public bool UseSnap
+ {
+ get
+ {
+ return this._useSnap;
+ }
+ set
+ {
+ this._useSnap = value;
+ }
+ }
+
+ public bool MarkPeaks
+ {
+ get
+ {
+ return this._markPeaks;
+ }
+ set
+ {
+ this._markPeaks = value;
+ }
+ }
+
+ public event ManualFrequencyChangeEventHandler FrequencyChanged;
+
+ public event ManualFrequencyChangeEventHandler CenterFrequencyChanged;
+
+ public event ManualBandwidthChangeEventHandler BandwidthChanged;
+
+ public event CustomPaintEventHandler CustomPaint;
+
+ public event CustomPaintEventHandler BackgroundCustomPaint;
+
+ public SpectrumAnalyzer()
+ {
+ this._performTimer = new Timer();
+ this._performTimer.Enabled = true;
+ this._performTimer.Interval = 20;
+ this._performTimer.Tick += this.performTimer_Tick;
+ Rectangle 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);
+ this._gradientBrush = new LinearGradientBrush(new Rectangle(30, 30, this._buffer.Width - 30, this._buffer.Height - 30), Color.White, Color.Black, LinearGradientMode.Vertical);
+ this._gradientBrush.InterpolationColors = this._staticGradient;
+ int num = this._buffer.Width - 60;
+ this._smoothedSpectrumEnvelope = new float[num];
+ this._smoothedSpectrumMinimum = new float[num];
+ this._scaledSpectrumEnvelope = new byte[num];
+ this._scaledSpectrumMinimum = new byte[num];
+ this._envelopePoints = new Point[num + 2];
+ this._minMaxPoints = new Point[num * 2 + 2];
+ this._temp = new float[num];
+ for (int i = 0; i < this._smoothedSpectrumEnvelope.Length; i++)
+ {
+ this._smoothedSpectrumEnvelope[i] = -250f;
+ this._smoothedSpectrumMinimum[i] = -250f;
+ }
+ base.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
+ base.SetStyle(ControlStyles.DoubleBuffer, true);
+ base.SetStyle(ControlStyles.UserPaint, true);
+ base.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
+ base.UpdateStyles();
+ this.BuildDynamicGradientVector();
+ this.BuildSNRGradientVector();
+ }
+
+ ~SpectrumAnalyzer()
+ {
+ this._buffer.Dispose();
+ this._graphics.Dispose();
+ this._gradientBrush.Dispose();
+ }
+
+ private void BuildDynamicGradientVector()
+ {
+ using (Bitmap bitmap = new Bitmap(1, this._gradientPens.Length))
+ {
+ using (Graphics graphics = Graphics.FromImage(bitmap))
+ {
+ using (LinearGradientBrush linearGradientBrush = new LinearGradientBrush(new Rectangle(0, 0, 1, this._gradientPens.Length), Color.White, Color.Black, LinearGradientMode.Vertical))
+ {
+ linearGradientBrush.InterpolationColors = this._verticalLinesGradient;
+ Pen pen = new Pen(linearGradientBrush);
+ graphics.DrawLine(pen, 0, 0, 0, this._gradientPens.Length - 1);
+ for (int i = 0; i < this._gradientPens.Length; i++)
+ {
+ this._gradientPens[this._gradientPens.Length - 1 - i] = new Pen(Color.FromArgb(bitmap.GetPixel(0, i).ToArgb()));
+ }
+ }
+ }
+ }
+ }
+
+ private void BuildSNRGradientVector()
+ {
+ using (Bitmap bitmap = new Bitmap(1, this._snrGradientPens.Length))
+ {
+ using (Graphics graphics = Graphics.FromImage(bitmap))
+ {
+ using (LinearGradientBrush linearGradientBrush = new LinearGradientBrush(new Rectangle(0, 0, 1, this._snrGradientPens.Length), Color.Black, Color.White, LinearGradientMode.Vertical))
+ {
+ linearGradientBrush.InterpolationColors = this._snrGradient;
+ Pen pen = new Pen(linearGradientBrush);
+ graphics.DrawLine(pen, 0, 0, 0, this._snrGradientPens.Length - 1);
+ for (int i = 0; i < this._snrGradientPens.Length; i++)
+ {
+ this._snrGradientPens[this._snrGradientPens.Length - 1 - i] = new Pen(Color.FromArgb(bitmap.GetPixel(0, i).ToArgb()), 10f);
+ }
+ }
+ }
+ }
+ }
+
+ private void ApplyZoom()
+ {
+ this._scale = (float)Math.Pow(10.0, (double)((float)this._zoom * 4f / 100f));
+ if (this._spectrumWidth > 0)
+ {
+ this._displayCenterFrequency = this.GetDisplayCenterFrequency();
+ this._xIncrement = this._scale * (float)(this._buffer.Width - 60) / (float)this._spectrumWidth;
+ this._displayedBandwidth = (int)((float)this._spectrumWidth / this._scale);
+ this._performNeeded = true;
+ }
+ }
+
+ public void CenterZoom()
+ {
+ this._displayCenterFrequency = this.GetDisplayCenterFrequency();
+ this._performNeeded = true;
+ }
+
+ private long GetDisplayCenterFrequency()
+ {
+ long num = this._frequency + this._filterOffset;
+ switch (this._bandType)
+ {
+ case BandType.Lower:
+ num = (long)((float)num - (float)this._filterBandwidth * 0.5f);
+ break;
+ case BandType.Upper:
+ num = (long)((float)num + (float)this._filterBandwidth * 0.5f);
+ break;
+ }
+ long num2 = (long)((double)((float)this._centerFrequency - (float)this._spectrumWidth * 0.5f) - ((double)num - (double)this._spectrumWidth * 0.5 / (double)this._scale));
+ if (num2 > 0)
+ {
+ num += num2;
+ }
+ long num3 = (long)((double)((float)num + (float)this._spectrumWidth * 0.5f / this._scale) - ((double)this._centerFrequency + (double)this._spectrumWidth * 0.5));
+ if (num3 > 0)
+ {
+ num -= num3;
+ }
+ return num;
+ }
+
+ private void performTimer_Tick(object sender, EventArgs e)
+ {
+ this.Perform(false);
+ }
+
+ private void Perform(bool force)
+ {
+ if (this._performNeeded | force)
+ {
+ this.DrawLayers();
+ base.Invalidate();
+ this._performNeeded = false;
+ }
+ }
+
+ public void Perform()
+ {
+ this._performNeeded = true;
+ }
+
+ private void DrawCursor(bool drawFilter = true, bool drawFrequencyMarker = true, bool drawPeaks = true, bool drawHotTrackingLine = true, bool drawHotTrackingText = true)
+ {
+ this._lower = 0f;
+ int num = (int)Math.Max((float)this._filterBandwidth * this._xIncrement, 2f) | 1;
+ float num2 = (float)this._buffer.Width * 0.5f + (float)(this._frequency - this._displayCenterFrequency) * this._xIncrement;
+ switch (this._bandType)
+ {
+ case BandType.Upper:
+ this._lower = num2;
+ break;
+ case BandType.Lower:
+ this._lower = num2 - (float)num;
+ break;
+ case BandType.Center:
+ this._lower = num2 - (float)num * 0.5f;
+ break;
+ }
+ this._lower += (float)this._filterOffset * this._xIncrement;
+ this._upper = this._lower + (float)num;
+ using (SolidBrush brush = new SolidBrush(Color.FromArgb(80, Color.DarkGray)))
+ {
+ using (SolidBrush brush2 = new SolidBrush(Color.FromArgb(200, 50, 50, 50)))
+ {
+ using (Pen pen5 = new Pen(Color.FromArgb(200, Color.Gray)))
+ {
+ using (Pen pen3 = new Pen(Color.DodgerBlue))
+ {
+ using (Pen pen2 = new Pen(Color.LimeGreen))
+ {
+ using (Pen pen = new Pen(Color.Red))
+ {
+ using (FontFamily family = new FontFamily("Verdana"))
+ {
+ using (GraphicsPath graphicsPath = new GraphicsPath())
+ {
+ using (Pen pen4 = new Pen(Color.Black))
+ {
+ if (drawFilter && this._enableFilter && num < this._buffer.Width - 60)
+ {
+ float num3 = this._lower;
+ float num4 = (float)num;
+ if (this._lower < 30f)
+ {
+ num3 = 31f;
+ num4 -= num3 - this._lower;
+ }
+ if (this._upper > (float)(this._buffer.Width - 30))
+ {
+ num4 -= this._upper - (float)(this._buffer.Width - 30);
+ }
+ this._graphics.FillRectangle(brush, num3, 30f, num4, (float)(this._buffer.Height - 60));
+ }
+ if (drawFrequencyMarker && this._enableFrequencyMarker && num2 > 30f && num2 < (float)(this._buffer.Width - 30))
+ {
+ pen.Width = 1f;
+ this._graphics.DrawLine(pen, num2, 30f, num2, (float)(this._buffer.Height - 30));
+ }
+ if (drawPeaks && this._markPeaks && this._spectrumWidth > 0)
+ {
+ int val = num;
+ val = Math.Max(val, 10);
+ val = Math.Min(val, this._scaledSpectrumEnvelope.Length);
+ PeakDetector.GetPeaks(this._scaledSpectrumEnvelope, this._peaks, val);
+ float num5 = (float)(this._buffer.Height - 60) / 255f;
+ foreach (int peak in this._peaks)
+ {
+ int num6 = (int)((float)(this._buffer.Height - 30) - (float)(int)this._scaledSpectrumEnvelope[peak] * num5);
+ int num7 = peak + 30;
+ this._graphics.DrawEllipse(Pens.Yellow, num7 - 5, num6 - 5, 10, 10);
+ }
+ }
+ if (this._enableHotTracking && this._hotTrackNeeded && this._cursorPosition.X >= 30 && this._cursorPosition.X <= this._buffer.Width - 30 && this._cursorPosition.Y >= 30 && this._cursorPosition.Y <= this._buffer.Height - 30)
+ {
+ if (drawHotTrackingLine && this.Cursor != Cursors.SizeWE && (this._snappedX < this._lower || this._snappedX > this._upper) && this._scaledSpectrumEnvelope != null && !this._changingFrequency && !this._changingCenterFrequency && !this._changingBandwidth)
+ {
+ pen2.DashStyle = DashStyle.Dash;
+ this._graphics.DrawLine(pen3, this._snappedX, 30f, this._snappedX, (float)(this._buffer.Height - 30));
+ int num8 = num / 2;
+ switch (this._bandType)
+ {
+ case BandType.Center:
+ this._graphics.DrawLine(pen2, this._snappedX - (float)num8, 30f, this._snappedX - (float)num8, (float)(this._buffer.Height - 30));
+ this._graphics.DrawLine(pen2, this._snappedX + (float)num8, 30f, this._snappedX + (float)num8, (float)(this._buffer.Height - 30));
+ break;
+ case BandType.Lower:
+ this._graphics.DrawLine(pen2, this._snappedX - (float)num, 30f, this._snappedX - (float)num, (float)(this._buffer.Height - 30));
+ break;
+ case BandType.Upper:
+ this._graphics.DrawLine(pen2, this._snappedX + (float)num, 30f, this._snappedX + (float)num, (float)(this._buffer.Height - 30));
+ break;
+ }
+ }
+ if (drawHotTrackingText)
+ {
+ string text = string.Empty;
+ if (this._changingBandwidth || this.Cursor == Cursors.SizeWE)
+ {
+ text = "Bandwidth: " + SpectrumAnalyzer.GetFrequencyDisplay(this._filterBandwidth);
+ }
+ else if (!this._changingCenterFrequency)
+ {
+ if (!string.IsNullOrEmpty(this._customTitle))
+ {
+ text = this._customTitle;
+ }
+ if (this._changingFrequency || ((float)this._cursorPosition.X >= this._lower && (float)this._cursorPosition.X <= this._upper))
+ {
+ if (!string.IsNullOrEmpty(text))
+ {
+ text = text + Environment.NewLine + Environment.NewLine;
+ }
+ text += string.Format("VFO:\t{0}{1}Peak:\t{2:0.0}dBFS{3}Floor:\t{4:0.0}dBFS{5}SNR:\t{6:0.0}dB", SpectrumAnalyzer.GetFrequencyDisplay(this._frequency), Environment.NewLine, this._peak, Environment.NewLine, this._floor, Environment.NewLine, this._snr);
+ }
+ if (string.IsNullOrEmpty(text))
+ {
+ text = string.Format("{0}\r\n{1:0.##}dBFS", SpectrumAnalyzer.GetFrequencyDisplay(this._trackingFrequency), this._trackingPower);
+ }
+ }
+ graphicsPath.AddString(text, family, 0, 16f, Point.Empty, StringFormat.GenericTypographic);
+ RectangleF bounds = graphicsPath.GetBounds();
+ Cursor current2 = Cursor.Current;
+ float val2 = (float)this._cursorPosition.X + 30f;
+ float val3 = (float)this._cursorPosition.Y + ((current2 == (Cursor)null) ? 32f : ((float)current2.Size.Height)) - 8f;
+ val2 = Math.Min(val2, (float)this._buffer.Width - bounds.Width - 30f - 20f);
+ val3 = Math.Min(val3, (float)this._buffer.Height - bounds.Height - 30f - 20f);
+ graphicsPath.Reset();
+ graphicsPath.AddString(text, family, 0, 16f, new Point((int)val2, (int)val3), StringFormat.GenericTypographic);
+ SmoothingMode smoothingMode = this._graphics.SmoothingMode;
+ InterpolationMode interpolationMode = this._graphics.InterpolationMode;
+ this._graphics.SmoothingMode = SmoothingMode.AntiAlias;
+ this._graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;
+ pen4.Width = 2f;
+ RectangleF bounds2 = graphicsPath.GetBounds();
+ bounds2.X -= 10f;
+ bounds2.Y -= 10f;
+ bounds2.Width += 20f;
+ bounds2.Height += 20f;
+ this._graphics.FillRoundedRectangle(brush2, bounds2, 6);
+ this._graphics.DrawRoundedRectangle(pen5, bounds2, 6);
+ this._graphics.DrawPath(pen4, graphicsPath);
+ this._graphics.FillPath(Brushes.White, graphicsPath);
+ this._graphics.SmoothingMode = smoothingMode;
+ this._graphics.InterpolationMode = interpolationMode;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void ProcessVfo()
+ {
+ int num = Math.Max((int)this._lower - 30, 0);
+ int num2 = Math.Min((int)this._upper - 30, this._smoothedSpectrumEnvelope.Length - 1);
+ float num3 = -600f;
+ for (int i = num; i <= num2; i++)
+ {
+ if (this._smoothedSpectrumEnvelope[i] > num3)
+ {
+ num3 = this._smoothedSpectrumEnvelope[i];
+ }
+ }
+ float num4 = (num3 > this._peak) ? 0.3f : 0.01f;
+ this._peak += num4 * (num3 - this._peak);
+ float val = (this._upper - this._lower) * 0.25f;
+ val = Math.Min(5f, val);
+ float num5 = 0f;
+ float val2 = 0f;
+ float val3 = 0f;
+ num = Math.Max((int)(this._lower - val) - 30, 0);
+ num2 = Math.Min((int)this._lower - 30, this._smoothedSpectrumEnvelope.Length - 1);
+ if (num2 > num)
+ {
+ for (int j = num; j <= num2; j++)
+ {
+ num5 += this._smoothedSpectrumEnvelope[j];
+ }
+ val2 = num5 / (float)(num2 - num + 1);
+ }
+ num = Math.Max((int)this._upper - 30, 0);
+ num2 = Math.Min((int)(this._upper + val) - 30, this._smoothedSpectrumEnvelope.Length - 1);
+ if (num2 > num)
+ {
+ num5 = 0f;
+ for (int k = num; k <= num2; k++)
+ {
+ num5 += this._smoothedSpectrumEnvelope[k];
+ }
+ val3 = num5 / (float)(num2 - num + 1);
+ }
+ float num6 = Math.Min(val2, val3);
+ if (num6 == 0f)
+ {
+ num = Math.Max((int)this._lower - 30, 0);
+ num2 = Math.Min((int)this._upper - 30, this._smoothedSpectrumEnvelope.Length - 1);
+ float num7 = 600f;
+ for (int l = num; l <= num2; l++)
+ {
+ if (this._smoothedSpectrumEnvelope[l] < num7)
+ {
+ num7 = this._smoothedSpectrumEnvelope[l];
+ }
+ }
+ num6 = num7;
+ }
+ this._floor += 0.03f * (num6 - this._floor);
+ this._snr = this._peak - this._floor;
+ }
+
+ public unsafe void Render(float* powerSpectrum, int length)
+ {
+ float offset = (float)(this._displayCenterFrequency - this._centerFrequency) / (float)this._spectrumWidth;
+ this.ExtractSpectrum(powerSpectrum, length, offset, this._scale, this._useSmoothing);
+ this._performNeeded = true;
+ }
+
+ private unsafe void ExtractSpectrum(float* powerSpectrum, int length, float offset, float scale, bool useSmoothing)
+ {
+ int num = this._displayOffset / 10 * 10;
+ int num2 = this._displayRange / 10 * 10;
+ float[] temp = this._temp;
+ fixed (float* ptr = temp)
+ {
+ float[] smoothedSpectrumEnvelope = this._smoothedSpectrumEnvelope;
+ fixed (float* ptr2 = smoothedSpectrumEnvelope)
+ {
+ byte[] scaledSpectrumEnvelope = this._scaledSpectrumEnvelope;
+ fixed (byte* dest = scaledSpectrumEnvelope)
+ {
+ byte[] scaledSpectrumMinimum = this._scaledSpectrumMinimum;
+ fixed (byte* ptr3 = scaledSpectrumMinimum)
+ {
+ Fourier.SmoothMaxCopy(powerSpectrum, ptr, length, this._smoothedSpectrumEnvelope.Length, scale, offset);
+ if (useSmoothing)
+ {
+ for (int i = 0; i < this._temp.Length; i++)
+ {
+ float num3 = (ptr2[i] < ptr[i]) ? this.Attack : this.Decay;
+ ptr2[i] = ptr2[i] * (1f - num3) + ptr[i] * num3;
+ }
+ }
+ else
+ {
+ Utils.Memcpy(ptr2, ptr, this._smoothedSpectrumEnvelope.Length * 4);
+ }
+ Fourier.ScaleFFT(ptr2, dest, this._smoothedSpectrumEnvelope.Length, (float)(num - num2), (float)num);
+ }
+ }
+ }
+ }
+ this.ProcessVfo();
+ if (this._spectrumStyle == SpectrumStyle.MinMax)
+ {
+ temp = this._temp;
+ fixed (float* ptr4 = temp)
+ {
+ float[] smoothedSpectrumEnvelope = this._smoothedSpectrumMinimum;
+ fixed (float* ptr5 = smoothedSpectrumEnvelope)
+ {
+ byte[] scaledSpectrumEnvelope = this._scaledSpectrumMinimum;
+ fixed (byte* dest2 = scaledSpectrumEnvelope)
+ {
+ Fourier.SmoothMinCopy(powerSpectrum, ptr4, length, this._smoothedSpectrumMinimum.Length, scale, offset);
+ if (useSmoothing)
+ {
+ for (int j = 0; j < this._temp.Length; j++)
+ {
+ float num4 = (ptr5[j] < ptr4[j]) ? this.Attack : this.Decay;
+ ptr5[j] = ptr5[j] * (1f - num4) + ptr4[j] * num4;
+ }
+ }
+ else
+ {
+ Utils.Memcpy(ptr5, ptr4, this._smoothedSpectrumMinimum.Length * 4);
+ }
+ Fourier.ScaleFFT(ptr5, dest2, this._smoothedSpectrumMinimum.Length, (float)(num - num2), (float)num);
+ }
+ }
+ }
+ }
+ }
+
+ private void DrawStatusText()
+ {
+ using (Font font = new Font("Lucida Console", 9f))
+ {
+ if (!string.IsNullOrEmpty(this._statusText))
+ {
+ this._graphics.DrawString(this._statusText, font, Brushes.White, 30f, 10f);
+ }
+ }
+ }
+
+ private void DrawGrid()
+ {
+ if (this._displayRange > 0)
+ {
+ using (SolidBrush brush = new SolidBrush(Color.Silver))
+ {
+ using (Pen pen = new Pen(Color.FromArgb(80, 80, 80)))
+ {
+ using (Font font = new Font("Arial", 8f))
+ {
+ using (new Pen(Color.DarkGray))
+ {
+ int num = (int)this._graphics.MeasureString("100", font).Height;
+ int num2 = this._buffer.Height - 60;
+ int num3 = 1;
+ int num4 = this._displayRange / num3;
+ if (num2 < num * num4)
+ {
+ num3 = 5;
+ num4 = this._displayRange / num3;
+ }
+ if (num2 < num * num4)
+ {
+ num3 = 10;
+ num4 = this._displayRange / num3;
+ }
+ float num5 = (float)(this._buffer.Height - 60) / (float)num4;
+ for (int i = 1; i <= num4; i++)
+ {
+ this._graphics.DrawLine(pen, 30, (int)((float)(this._buffer.Height - 30) - (float)i * num5), this._buffer.Width - 30, (int)((float)(this._buffer.Height - 30) - (float)i * num5));
+ }
+ int num6 = this._displayOffset / 10 * 10;
+ for (int j = 0; j <= num4; j++)
+ {
+ string text = (num6 - (num4 - j) * num3).ToString();
+ SizeF sizeF = this._graphics.MeasureString(text, font);
+ float width = sizeF.Width;
+ float height = sizeF.Height;
+ this._graphics.DrawString(text, font, brush, 30f - width, (float)(this._buffer.Height - 30) - (float)j * num5 - height * 0.5f);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void DrawFrequencyMarkers()
+ {
+ if (this._spectrumWidth > 0)
+ {
+ using (SolidBrush brush = new SolidBrush(Color.Silver))
+ {
+ using (Pen pen = new Pen(Color.FromArgb(80, 80, 80)))
+ {
+ using (Font font = new Font("Arial", 8f))
+ {
+ using (Pen pen2 = new Pen(Color.DarkGray))
+ {
+ string frequencyDisplay = Utils.GetFrequencyDisplay((long)((float)this._centerFrequency + (float)this._spectrumWidth * 0.5f), false);
+ float num = this._graphics.MeasureString(frequencyDisplay, font).Width + 30f;
+ long num2 = (long)((float)(this._buffer.Width - 60) / num);
+ long num3;
+ long num4;
+ if (this._useStepSizeForDisplay)
+ {
+ num3 = 0L;
+ do
+ {
+ num3 += this._stepSize;
+ num4 = (int)((float)this._spectrumWidth / this._scale) / num3;
+ }
+ while (num4 > num2);
+ }
+ else
+ {
+ int num5 = 2;
+ num3 = 10L;
+ do
+ {
+ num5 = ((num5 == 2) ? 5 : 2);
+ num3 *= num5;
+ num4 = (int)((float)this._spectrumWidth / this._scale) / num3;
+ }
+ while (num4 > num2);
+ if (num4 > 0)
+ {
+ if (num4 * 5 < num2)
+ {
+ num4 *= 5;
+ num3 /= 5;
+ }
+ if (num4 * 2 < num2)
+ {
+ num3 /= 2;
+ }
+ }
+ }
+ num4 = num2 * 2;
+ long num6 = this._displayCenterFrequency / num3 * num3;
+ for (long num7 = -num4 / 2; num7 < num4 / 2; num7++)
+ {
+ long frequency = num6 + num3 * num7;
+ float num8 = this.FrequencyToPoint(frequency);
+ if (num8 >= 29f && num8 <= (float)(this._buffer.Width - 30 + 1))
+ {
+ this._graphics.DrawLine(pen, num8, 30f, num8, (float)(this._buffer.Height - 30));
+ this._graphics.DrawLine(pen2, num8, (float)(this._buffer.Height - 30), num8, (float)(this._buffer.Height - 30 + 5));
+ string frequencyDisplay2 = Utils.GetFrequencyDisplay(frequency, false);
+ float width = this._graphics.MeasureString(frequencyDisplay2, font).Width;
+ num8 -= width * 0.5f;
+ this._graphics.DrawString(frequencyDisplay2, font, brush, num8, (float)(this._buffer.Height - 30) + 8f);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void DrawAxis()
+ {
+ using (Pen pen = new Pen(Color.DarkGray))
+ {
+ this._graphics.DrawLine(pen, 30, 30, 30, this._buffer.Height - 30);
+ this._graphics.DrawLine(pen, 30, this._buffer.Height - 30, this._buffer.Width - 30, this._buffer.Height - 30);
+ }
+ }
+
+ public static string GetFrequencyDisplay(long frequency)
+ {
+ return Utils.GetFrequencyDisplay(frequency, true);
+ }
+
+ public static void ConfigureGraphics(Graphics graphics, bool useAntiAliasedDisplay)
+ {
+ if (useAntiAliasedDisplay)
+ {
+ graphics.CompositingMode = CompositingMode.SourceOver;
+ graphics.CompositingQuality = CompositingQuality.AssumeLinear;
+ graphics.SmoothingMode = SmoothingMode.AntiAlias;
+ graphics.PixelOffsetMode = PixelOffsetMode.Default;
+ graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;
+ }
+ else
+ {
+ graphics.CompositingMode = CompositingMode.SourceOver;
+ graphics.CompositingQuality = CompositingQuality.HighSpeed;
+ graphics.SmoothingMode = SmoothingMode.None;
+ graphics.PixelOffsetMode = PixelOffsetMode.HighSpeed;
+ graphics.InterpolationMode = InterpolationMode.Low;
+ }
+ }
+
+ public void ResetSpectrum()
+ {
+ for (int i = 0; i < this._scaledSpectrumEnvelope.Length; i++)
+ {
+ this._scaledSpectrumEnvelope[i] = 0;
+ this._scaledSpectrumMinimum[i] = 0;
+ }
+ this._peak = 0f;
+ this._floor = 0f;
+ this._snr = 0f;
+ this._performNeeded = true;
+ }
+
+ private void DrawLayers()
+ {
+ if (this._buffer.Width > 30 && this._buffer.Height > 30)
+ {
+ this._graphics.Clear(Color.Black);
+ this.DrawStatusText();
+ this.DrawSNR();
+ this.OnBackgroundCustomPaint(new CustomPaintEventArgs(this._graphics, this._cursorPosition));
+ this.DrawGrid();
+ this.DrawFrequencyMarkers();
+ this.DrawSpectrum();
+ this.DrawAxis();
+ }
+ }
+
+ private void DrawSpectrum()
+ {
+ if (this._scaledSpectrumEnvelope != null && this._scaledSpectrumEnvelope.Length != 0)
+ {
+ float num = (float)(this._buffer.Width - 60) / (float)this._scaledSpectrumEnvelope.Length;
+ float num2 = (float)(this._buffer.Height - 60) / 255f;
+ if (this._spectrumStyle == SpectrumStyle.Dots)
+ {
+ this.DrawCursor(true, true, true, false, false);
+ for (int i = 0; i < this._scaledSpectrumEnvelope.Length; i++)
+ {
+ Math.Min(Math.Max(0, this._scaledSpectrumEnvelope[i] + this._contrast * 2), 255);
+ int x = (int)(30f + (float)i * num);
+ int y = (int)((float)(this._buffer.Height - 30) - (float)(int)this._scaledSpectrumEnvelope[i] * num2);
+ this._buffer.SetPixel(x, y, SpectrumAnalyzer._spectrumEnvelopeColor);
+ }
+ this.OnCustomPaint(new CustomPaintEventArgs(this._graphics, this._cursorPosition));
+ this.DrawCursor(false, false, false, true, true);
+ }
+ else if (this._spectrumStyle == SpectrumStyle.DynamicGradient)
+ {
+ for (int j = 0; j < this._scaledSpectrumEnvelope.Length; j++)
+ {
+ int num3 = Math.Min(Math.Max(0, this._scaledSpectrumEnvelope[j] + this._contrast * 2), 255);
+ int num4 = (int)(30f + (float)j * num);
+ int num5 = (int)((float)(this._buffer.Height - 30) - (float)(int)this._scaledSpectrumEnvelope[j] * num2);
+ this._envelopePoints[j + 1].X = num4;
+ this._envelopePoints[j + 1].Y = num5;
+ this._graphics.DrawLine(this._gradientPens[num3], num4, num5, num4, this._buffer.Height - 30);
+ }
+ this.DrawCursor(true, true, true, false, false);
+ this._envelopePoints[0] = this._envelopePoints[1];
+ this._envelopePoints[this._envelopePoints.Length - 1] = this._envelopePoints[this._envelopePoints.Length - 2];
+ SpectrumAnalyzer.ConfigureGraphics(this._graphics, SpectrumAnalyzer._useAntiAliasedDisplay);
+ this._graphics.DrawLines(Pens.Gray, this._envelopePoints);
+ SpectrumAnalyzer.ConfigureGraphics(this._graphics, false);
+ this.OnCustomPaint(new CustomPaintEventArgs(this._graphics, this._cursorPosition));
+ this.DrawCursor(false, false, false, true, true);
+ }
+ else if (this._spectrumStyle == SpectrumStyle.MinMax)
+ {
+ for (int k = 0; k < this._scaledSpectrumEnvelope.Length; k++)
+ {
+ byte b = this._scaledSpectrumMinimum[k];
+ byte b2 = this._scaledSpectrumEnvelope[k];
+ int x2 = (int)(30f + (float)k * num);
+ int y2 = (int)((float)(this._buffer.Height - 30) - (float)(int)b * num2);
+ int y3 = (int)((float)(this._buffer.Height - 30) - (float)(int)b2 * num2);
+ this._minMaxPoints[k * 2 + 1].X = x2;
+ this._minMaxPoints[k * 2 + 1].Y = y2;
+ this._minMaxPoints[k * 2 + 2].X = x2;
+ this._minMaxPoints[k * 2 + 2].Y = y3;
+ }
+ this.DrawCursor(true, true, true, false, false);
+ this._minMaxPoints[0] = this._minMaxPoints[1];
+ this._minMaxPoints[this._minMaxPoints.Length - 1] = this._minMaxPoints[this._minMaxPoints.Length - 2];
+ SpectrumAnalyzer.ConfigureGraphics(this._graphics, SpectrumAnalyzer._useAntiAliasedDisplay);
+ this._graphics.DrawLines(new Pen(SpectrumAnalyzer._spectrumEnvelopeColor), this._minMaxPoints);
+ SpectrumAnalyzer.ConfigureGraphics(this._graphics, false);
+ this.OnCustomPaint(new CustomPaintEventArgs(this._graphics, this._cursorPosition));
+ this.DrawCursor(false, false, false, true, true);
+ }
+ else
+ {
+ for (int l = 0; l < this._scaledSpectrumEnvelope.Length; l++)
+ {
+ byte b3 = this._scaledSpectrumEnvelope[l];
+ int x3 = (int)(30f + (float)l * num);
+ int y4 = (int)((float)(this._buffer.Height - 30) - (float)(int)b3 * num2);
+ this._envelopePoints[l + 1].X = x3;
+ this._envelopePoints[l + 1].Y = y4;
+ }
+ if (this._spectrumStyle == SpectrumStyle.StaticGradient)
+ {
+ this._envelopePoints[0].X = 30;
+ this._envelopePoints[0].Y = this._buffer.Height - 30 + 1;
+ this._envelopePoints[this._envelopePoints.Length - 1].X = this._buffer.Width - 30;
+ this._envelopePoints[this._envelopePoints.Length - 1].Y = this._buffer.Height - 30 + 1;
+ this._graphics.FillPolygon(this._gradientBrush, this._envelopePoints);
+ }
+ else if (this._spectrumStyle == SpectrumStyle.SolidFill)
+ {
+ this._envelopePoints[0].X = 30;
+ this._envelopePoints[0].Y = this._buffer.Height - 30 + 1;
+ this._envelopePoints[this._envelopePoints.Length - 1].X = this._buffer.Width - 30;
+ this._envelopePoints[this._envelopePoints.Length - 1].Y = this._buffer.Height - 30 + 1;
+ this._graphics.FillPolygon(new SolidBrush(SpectrumAnalyzer._spectrumFillColor), this._envelopePoints);
+ }
+ this.DrawCursor(true, true, true, false, false);
+ this._envelopePoints[0] = this._envelopePoints[1];
+ this._envelopePoints[this._envelopePoints.Length - 1] = this._envelopePoints[this._envelopePoints.Length - 2];
+ SpectrumAnalyzer.ConfigureGraphics(this._graphics, SpectrumAnalyzer._useAntiAliasedDisplay);
+ this._graphics.DrawLines(new Pen(SpectrumAnalyzer._spectrumEnvelopeColor), this._envelopePoints);
+ SpectrumAnalyzer.ConfigureGraphics(this._graphics, false);
+ this.OnCustomPaint(new CustomPaintEventArgs(this._graphics, this._cursorPosition));
+ this.DrawCursor(false, false, false, true, true);
+ }
+ }
+ }
+
+ private void DrawSNR()
+ {
+ if (this._enableSnrBar)
+ {
+ int val = (int)(this._snr / 100f * (float)this._snrGradientPens.Length);
+ val = Math.Max(0, val);
+ val = Math.Min(this._snrGradientPens.Length - 1, val);
+ float val2 = (float)(this._buffer.Height - 60) * this._snr / 100f;
+ val2 = Math.Max(0f, val2);
+ val2 = Math.Min((float)(this._buffer.Height - 30), val2);
+ this._graphics.DrawLine(new Pen(Color.FromArgb(50, 50, 50), 14f), (float)this._buffer.Width - 15f, 30f, (float)this._buffer.Width - 15f, (float)(this._buffer.Height - 30 + 1));
+ this._graphics.DrawLine(this._snrGradientPens[val], (float)this._buffer.Width - 15f, (float)(this._buffer.Height - 30) - val2, (float)this._buffer.Width - 15f, (float)(this._buffer.Height - 30 + 1));
+ string text = this._snr.ToString("##");
+ SizeF sizeF = this._graphics.MeasureString(text, this.Font);
+ this._graphics.DrawString(text, this.Font, new SolidBrush(Color.White), (float)this._buffer.Width - (30f + sizeF.Width) * 0.5f, (float)(this._buffer.Height - 30) - val2 - sizeF.Height);
+ }
+ }
+
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ Rectangle clientRectangle = base.ClientRectangle;
+ if (clientRectangle.Width > 60)
+ {
+ clientRectangle = base.ClientRectangle;
+ if (clientRectangle.Height <= 60)
+ {
+ goto IL_0024;
+ }
+ SpectrumAnalyzer.ConfigureGraphics(e.Graphics, false);
+ e.Graphics.DrawImageUnscaled(this._buffer, 0, 0);
+ return;
+ }
+ goto IL_0024;
+ IL_0024:
+ e.Graphics.Clear(Color.Black);
+ }
+
+ protected override void OnPaintBackground(PaintEventArgs e)
+ {
+ }
+
+ protected override void OnResize(EventArgs e)
+ {
+ base.OnResize(e);
+ Rectangle clientRectangle = base.ClientRectangle;
+ if (clientRectangle.Width > 60)
+ {
+ clientRectangle = base.ClientRectangle;
+ if (clientRectangle.Height > 60)
+ {
+ this._buffer.Dispose();
+ this._graphics.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);
+ SpectrumAnalyzer.ConfigureGraphics(this._graphics, false);
+ int num = this._buffer.Width - 60;
+ this._scaledSpectrumEnvelope = new byte[num];
+ this._scaledSpectrumMinimum = new byte[num];
+ this._smoothedSpectrumEnvelope = new float[num];
+ this._smoothedSpectrumMinimum = new float[num];
+ this._temp = new float[num];
+ this._envelopePoints = new Point[num + 2];
+ this._minMaxPoints = new Point[num * 2 + 2];
+ if (this._spectrumWidth > 0)
+ {
+ this._xIncrement = this._scale * (float)num / (float)this._spectrumWidth;
+ }
+ this._gradientBrush.Dispose();
+ this._gradientBrush = new LinearGradientBrush(new Rectangle(30, 30, this._buffer.Width - 30, this._buffer.Height - 30), Color.White, Color.Black, LinearGradientMode.Vertical);
+ this._gradientBrush.InterpolationColors = this._staticGradient;
+ this.Perform(true);
+ }
+ }
+ }
+
+ public float FrequencyToPoint(long frequency)
+ {
+ return (float)this._buffer.Width * 0.5f + (float)(frequency - this._displayCenterFrequency) * this._xIncrement;
+ }
+
+ public long PointToFrequency(float point)
+ {
+ return (long)((point - (float)this._buffer.Width * 0.5f) / this._xIncrement) + this._displayCenterFrequency;
+ }
+
+ protected virtual void OnFrequencyChanged(FrequencyEventArgs e)
+ {
+ ManualFrequencyChangeEventHandler frequencyChanged = this.FrequencyChanged;
+ if (frequencyChanged != null)
+ {
+ frequencyChanged(this, e);
+ }
+ }
+
+ protected virtual void OnCenterFrequencyChanged(FrequencyEventArgs e)
+ {
+ ManualFrequencyChangeEventHandler centerFrequencyChanged = this.CenterFrequencyChanged;
+ if (centerFrequencyChanged != null)
+ {
+ centerFrequencyChanged(this, e);
+ }
+ }
+
+ protected virtual void OnBandwidthChanged(BandwidthEventArgs e)
+ {
+ ManualBandwidthChangeEventHandler bandwidthChanged = this.BandwidthChanged;
+ if (bandwidthChanged != null)
+ {
+ bandwidthChanged(this, e);
+ }
+ }
+
+ private bool UpdateFrequency(long f, FrequencyChangeSource source)
+ {
+ if (this._useSnap)
+ {
+ f = (long)((float)f + (float)(Math.Sign(f) * this._stepSize) * 0.5f) / this._stepSize * this._stepSize;
+ }
+ long num = (long)((float)this._displayCenterFrequency - (float)this._spectrumWidth * 0.5f / this._scale);
+ long num2 = (long)((float)this._displayCenterFrequency + (float)this._spectrumWidth * 0.5f / this._scale);
+ if (source == FrequencyChangeSource.Scroll)
+ {
+ if (f < num || f > num2)
+ {
+ long num3 = f - this._frequency;
+ if (num3 != 0L && !this.UpdateCenterFrequency(this._centerFrequency + num3, source))
+ {
+ return false;
+ }
+ }
+ }
+ else if (f < num)
+ {
+ f = num;
+ }
+ else if (f > num2)
+ {
+ f = num2;
+ }
+ if (f != this._frequency)
+ {
+ FrequencyEventArgs frequencyEventArgs = new FrequencyEventArgs(f, source);
+ this.OnFrequencyChanged(frequencyEventArgs);
+ if (!frequencyEventArgs.Cancel)
+ {
+ this._frequency = frequencyEventArgs.Frequency;
+ this._performNeeded = true;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private bool UpdateCenterFrequency(long f, FrequencyChangeSource source)
+ {
+ if (this._useSnap)
+ {
+ f = (long)((float)f + (float)(Math.Sign(f) * this._stepSize) * 0.5f) / this._stepSize * this._stepSize;
+ }
+ if (f < 0)
+ {
+ f = 0L;
+ }
+ if (f != this._centerFrequency)
+ {
+ FrequencyEventArgs frequencyEventArgs = new FrequencyEventArgs(f, source);
+ this.OnCenterFrequencyChanged(frequencyEventArgs);
+ if (!frequencyEventArgs.Cancel)
+ {
+ long num = frequencyEventArgs.Frequency - this._centerFrequency;
+ this._displayCenterFrequency += num;
+ this._centerFrequency = frequencyEventArgs.Frequency;
+ this._performNeeded = true;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void UpdateBandwidth(int bw)
+ {
+ bw = 10 * (bw / 10);
+ if (bw < 10)
+ {
+ bw = 10;
+ }
+ int num = (int)((float)(18 * this._spectrumWidth) / this._scale / (float)(this._buffer.Width - 60));
+ if (bw < num)
+ {
+ bw = num;
+ }
+ if (bw != this._filterBandwidth)
+ {
+ int num2 = this._enableSideFilterResize ? ((int)((float)(bw - this._filterBandwidth) * 0.5f)) : 0;
+ int offset = this._filterOffset + ((this._side == BandType.Upper) ? num2 : (-num2));
+ BandwidthEventArgs bandwidthEventArgs = new BandwidthEventArgs(bw, offset, this._side);
+ this.OnBandwidthChanged(bandwidthEventArgs);
+ if (!bandwidthEventArgs.Cancel)
+ {
+ this._filterOffset = bandwidthEventArgs.Offset;
+ this._filterBandwidth = bandwidthEventArgs.Bandwidth;
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ protected virtual void OnCustomPaint(CustomPaintEventArgs e)
+ {
+ CustomPaintEventHandler customPaint = this.CustomPaint;
+ if (customPaint != null)
+ {
+ customPaint(this, e);
+ this._customTitle = e.CustomTitle;
+ }
+ }
+
+ protected virtual void OnBackgroundCustomPaint(CustomPaintEventArgs e)
+ {
+ CustomPaintEventHandler backgroundCustomPaint = this.BackgroundCustomPaint;
+ if (backgroundCustomPaint != null)
+ {
+ backgroundCustomPaint(this, e);
+ this._customTitle = e.CustomTitle;
+ }
+ }
+
+ protected override void OnMouseDown(MouseEventArgs e)
+ {
+ base.OnMouseDown(e);
+ if (e.X >= 30 && e.X <= this._buffer.Width - 30 && e.Y >= 30)
+ {
+ if (e.Button == MouseButtons.Left)
+ {
+ float num = Math.Max((float)this._filterBandwidth * this._xIncrement, 2f);
+ if (this._enableFilter && e.Y <= this._buffer.Height - 30)
+ {
+ if (this._enableFilterMove && (float)e.X > this._lower && (float)e.X < this._upper && num < (float)this._buffer.Width)
+ {
+ this._oldX = e.X;
+ this._oldFrequency = this._frequency;
+ this._changingFrequency = true;
+ }
+ else if (this._upper - this._lower > 12f)
+ {
+ if (Math.Abs((float)e.X - this._upper - 6f) <= 6f && (this._bandType == BandType.Center || this._bandType == BandType.Upper))
+ {
+ this._side = BandType.Upper;
+ this._oldX = e.X;
+ this._oldFilterBandwidth = this._filterBandwidth;
+ this._changingBandwidth = true;
+ }
+ else if (Math.Abs((float)e.X - this._lower + 6f) <= 6f && (this._bandType == BandType.Center || this._bandType == BandType.Lower))
+ {
+ this._side = BandType.Lower;
+ this._oldX = e.X;
+ this._oldFilterBandwidth = this._filterBandwidth;
+ this._changingBandwidth = true;
+ }
+ }
+ }
+ if (!this._changingBandwidth && !this._changingFrequency)
+ {
+ this._oldX = e.X;
+ this._oldCenterFrequency = this._centerFrequency;
+ this._changingCenterFrequency = true;
+ }
+ }
+ else if (e.Button == MouseButtons.Right)
+ {
+ this.UpdateFrequency(this._frequency / 500 * 500, FrequencyChangeSource.Click);
+ }
+ }
+ }
+
+ protected override void OnMouseUp(MouseEventArgs e)
+ {
+ base.OnMouseUp(e);
+ if (this._enableFilterMove && this._changingCenterFrequency && e.X == this._oldX)
+ {
+ long f = (long)(((float)this._oldX - (float)this._buffer.Width * 0.5f) * (float)this._spectrumWidth / this._scale / (float)(this._buffer.Width - 60) + (float)this._displayCenterFrequency);
+ this.UpdateFrequency(f, FrequencyChangeSource.Click);
+ }
+ this._changingCenterFrequency = false;
+ this._changingBandwidth = false;
+ this._changingFrequency = false;
+ this._performNeeded = true;
+ }
+
+ protected override void OnMouseMove(MouseEventArgs e)
+ {
+ base.OnMouseMove(e);
+ this._cursorPosition.X = e.X;
+ this._cursorPosition.Y = e.Y;
+ if (this._enableHotTracking)
+ {
+ this._snappedX = (float)e.X;
+ this._trackingFrequency = (long)(((float)e.X - (float)this._buffer.Width * 0.5f) * (float)this._spectrumWidth / this._scale / (float)(this._buffer.Width - 60) + (float)this._displayCenterFrequency);
+ if (this._useSnap)
+ {
+ this._trackingFrequency = (long)((float)this._trackingFrequency + (float)(Math.Sign(this._trackingFrequency) * this._stepSize) * 0.5f) / this._stepSize * this._stepSize;
+ this._snappedX = this.FrequencyToPoint(this._trackingFrequency);
+ }
+ int num = this._displayRange / 10 * 10;
+ int num2 = this._displayOffset / 10 * 10;
+ float num3 = (float)(this._buffer.Height - 60) / (float)num;
+ this._trackingPower = (float)(num2 - num) - (float)(e.Y + 30 - this._buffer.Height) / num3;
+ }
+ if (this._changingFrequency)
+ {
+ long f = (long)((float)((e.X - this._oldX) * this._spectrumWidth) / this._scale / (float)(this._buffer.Width - 60) + (float)this._oldFrequency);
+ this.UpdateFrequency(f, FrequencyChangeSource.Drag);
+ }
+ else if (this._changingCenterFrequency)
+ {
+ long f2 = (long)((float)((this._oldX - e.X) * this._spectrumWidth) / this._scale / (float)(this._buffer.Width - 60) + (float)this._oldCenterFrequency);
+ this.UpdateCenterFrequency(f2, FrequencyChangeSource.Drag);
+ }
+ else if (this._changingBandwidth)
+ {
+ int num4 = 0;
+ switch (this._side)
+ {
+ case BandType.Upper:
+ num4 = e.X - this._oldX;
+ break;
+ case BandType.Lower:
+ num4 = this._oldX - e.X;
+ break;
+ }
+ if (this._bandType == BandType.Center && !this._enableSideFilterResize)
+ {
+ num4 *= 2;
+ }
+ num4 = (int)((float)(num4 * this._spectrumWidth) / this._scale / (float)(this._buffer.Width - 60) + (float)this._oldFilterBandwidth);
+ this.UpdateBandwidth(num4);
+ }
+ else if (this._enableFilter)
+ {
+ if (e.Y >= 30 && e.Y <= this._buffer.Height - 30 && e.X >= 30 && e.X <= this._buffer.Width - 30 && this._upper - this._lower > 12f)
+ {
+ if (Math.Abs((float)e.X - this._lower + 6f) <= 6f && (this._bandType == BandType.Center || this._bandType == BandType.Lower))
+ {
+ goto IL_0325;
+ }
+ if (Math.Abs((float)e.X - this._upper - 6f) <= 6f && (this._bandType == BandType.Center || this._bandType == BandType.Upper))
+ {
+ goto IL_0325;
+ }
+ this.Cursor = Cursors.Default;
+ }
+ else
+ {
+ this.Cursor = Cursors.Default;
+ }
+ }
+ goto IL_034a;
+ IL_034a:
+ this._performNeeded = true;
+ return;
+ IL_0325:
+ this.Cursor = Cursors.SizeWE;
+ goto IL_034a;
+ }
+
+ protected override void OnMouseWheel(MouseEventArgs e)
+ {
+ base.OnMouseWheel(e);
+ if (this._enableFilter)
+ {
+ this.UpdateFrequency(this._frequency + this._stepSize * Math.Sign(e.Delta), FrequencyChangeSource.Scroll);
+ }
+ }
+
+ protected override void OnMouseLeave(EventArgs e)
+ {
+ base.OnMouseLeave(e);
+ this._hotTrackNeeded = false;
+ this._performNeeded = true;
+ this._cursorPosition = Point.Empty;
+ }
+
+ protected override void OnMouseEnter(EventArgs e)
+ {
+ base.Focus();
+ base.OnMouseEnter(e);
+ this._hotTrackNeeded = true;
+ }
+ }
+}
diff --git a/SDRSharp.PanView/SDRSharp.PanView/SpectrumStyle.cs b/SDRSharp.PanView/SDRSharp.PanView/SpectrumStyle.cs
new file mode 100644
index 0000000..e5c6f41
--- /dev/null
+++ b/SDRSharp.PanView/SDRSharp.PanView/SpectrumStyle.cs
@@ -0,0 +1,12 @@
+namespace SDRSharp.PanView
+{
+ public enum SpectrumStyle
+ {
+ Dots,
+ SimpleCurve,
+ SolidFill,
+ StaticGradient,
+ DynamicGradient,
+ MinMax
+ }
+}
diff --git a/SDRSharp.PanView/SDRSharp.PanView/Waterfall.cs b/SDRSharp.PanView/SDRSharp.PanView/Waterfall.cs
new file mode 100644
index 0000000..491ecc5
--- /dev/null
+++ b/SDRSharp.PanView/SDRSharp.PanView/Waterfall.cs
@@ -0,0 +1,1280 @@
+using SDRSharp.Radio;
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
+using System.Windows.Forms;
+
+namespace SDRSharp.PanView
+{
+ public class Waterfall : UserControl
+ {
+ private const int RefreshInterval = 20;
+
+ public const float TrackingFontSize = 16f;
+
+ public const float TimestampFontSize = 14f;
+
+ public const int CarrierPenWidth = 1;
+
+ public const int AxisMargin = 30;
+
+ public const int CursorSnapDistance = 6;
+
+ public const float MaxZoom = 4f;
+
+ public const int RightClickSnapDistance = 500;
+
+ public const float DefaultCursorHeight = 32f;
+
+ private static readonly bool _useUtcTimeStamp = Utils.GetBooleanSetting("waterfall.useUtcTimeStamp");
+
+ private float _attack;
+
+ private float _decay;
+
+ private bool _performNeeded;
+
+ private Bitmap _buffer;
+
+ private Bitmap _buffer2;
+
+ private Graphics _graphics;
+
+ private Graphics _graphics2;
+
+ private BandType _bandType;
+
+ private BandType _side;
+
+ private int _filterBandwidth;
+
+ private int _filterOffset;
+
+ private float _xIncrement;
+
+ private float[] _temp;
+
+ private float[] _smoothedSpectrum;
+
+ private byte[] _scaledSpectrum;
+
+ private long _centerFrequency;
+
+ private long _spectrumWidth;
+
+ private int _stepSize;
+
+ private long _frequency;
+
+ private float _lower;
+
+ private float _upper;
+
+ private float _scale = 1f;
+
+ private long _displayCenterFrequency;
+
+ private bool _changingBandwidth;
+
+ private bool _changingFrequency;
+
+ private bool _changingCenterFrequency;
+
+ private bool _mouseIn;
+
+ private int _oldX;
+
+ private int _displayedBandwidth;
+
+ private long _oldFrequency;
+
+ private long _oldCenterFrequency;
+
+ private int _oldFilterBandwidth;
+
+ private int[] _gradientPixels;
+
+ private int _contrast;
+
+ private int _zoom;
+
+ private bool _useSmoothing;
+
+ private bool _enableFilter = true;
+
+ private bool _enableHotTracking = true;
+
+ private bool _enableFrequencyMarker = true;
+
+ private bool _enableSideFilterResize;
+
+ private bool _enableFilterMove = true;
+
+ private bool _useSnap;
+
+ private float _snappedX;
+
+ private long _trackingFrequency;
+
+ private bool _useTimestamps;
+
+ private int _scanlines;
+
+ private int _timestampInterval;
+
+ private int _displayRange = 130;
+
+ private int _displayOffset;
+
+ private Point _cursorPosition;
+
+ private string _customTitle;
+
+ private Timer _performTimer;
+
+ private LinearGradientBrush _gradientBrush;
+
+ private ColorBlend _gradientColorBlend = Utils.GetGradientBlend(255, "waterfall.gradient");
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public ColorBlend GradientColorBlend
+ {
+ get
+ {
+ return this._gradientColorBlend;
+ }
+ set
+ {
+ if (this._gradientColorBlend != value)
+ {
+ this._gradientColorBlend = value;
+ this._gradientBrush.Dispose();
+ this._gradientBrush = new LinearGradientBrush(new RectangleF(15f, 15f, (float)this._buffer.Width - 15f, (float)this._buffer.Height - 15f), Color.White, Color.Black, LinearGradientMode.Vertical);
+ this._gradientBrush.InterpolationColors = this._gradientColorBlend;
+ this.DrawGradient();
+ this.BuildGradientVector();
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ public long CenterFrequency
+ {
+ get
+ {
+ return this._centerFrequency;
+ }
+ set
+ {
+ if (this._centerFrequency != value)
+ {
+ this._displayCenterFrequency += value - this._centerFrequency;
+ this._centerFrequency = value;
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ public int DisplayedBandwidth
+ {
+ get
+ {
+ return this._displayedBandwidth;
+ }
+ }
+
+ public long DisplayCenterFrequency
+ {
+ get
+ {
+ return this._displayCenterFrequency;
+ }
+ }
+
+ public int SpectrumWidth
+ {
+ get
+ {
+ return (int)this._spectrumWidth;
+ }
+ set
+ {
+ if (this._spectrumWidth != value)
+ {
+ this._spectrumWidth = value;
+ this.ApplyZoom();
+ }
+ }
+ }
+
+ public long Frequency
+ {
+ get
+ {
+ return this._frequency;
+ }
+ set
+ {
+ if (this._frequency != value)
+ {
+ this._frequency = value;
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ public int FilterBandwidth
+ {
+ get
+ {
+ return this._filterBandwidth;
+ }
+ set
+ {
+ if (this._filterBandwidth != value)
+ {
+ this._filterBandwidth = value;
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ public int DisplayRange
+ {
+ get
+ {
+ return this._displayRange;
+ }
+ set
+ {
+ this._displayRange = value;
+ }
+ }
+
+ public int DisplayOffset
+ {
+ get
+ {
+ return this._displayOffset;
+ }
+ set
+ {
+ this._displayOffset = value;
+ }
+ }
+
+ public int FilterOffset
+ {
+ get
+ {
+ return this._filterOffset;
+ }
+ set
+ {
+ if (this._filterOffset != value)
+ {
+ this._filterOffset = value;
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ public BandType BandType
+ {
+ get
+ {
+ return this._bandType;
+ }
+ set
+ {
+ if (this._bandType != value)
+ {
+ this._bandType = value;
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ public int Contrast
+ {
+ get
+ {
+ return this._contrast;
+ }
+ set
+ {
+ this._contrast = value;
+ }
+ }
+
+ public int Zoom
+ {
+ get
+ {
+ return this._zoom;
+ }
+ set
+ {
+ if (this._zoom != value)
+ {
+ this._zoom = value;
+ this.ApplyZoom();
+ }
+ }
+ }
+
+ public bool UseSmoothing
+ {
+ get
+ {
+ return this._useSmoothing;
+ }
+ set
+ {
+ this._useSmoothing = value;
+ }
+ }
+
+ public bool EnableFilter
+ {
+ get
+ {
+ return this._enableFilter;
+ }
+ set
+ {
+ this._enableFilter = value;
+ this._performNeeded = true;
+ }
+ }
+
+ public bool EnableHotTracking
+ {
+ get
+ {
+ return this._enableHotTracking;
+ }
+ set
+ {
+ this._enableHotTracking = value;
+ this._performNeeded = true;
+ }
+ }
+
+ public bool EnableFrequencyMarker
+ {
+ get
+ {
+ return this._enableFrequencyMarker;
+ }
+ set
+ {
+ this._enableFrequencyMarker = value;
+ this._performNeeded = true;
+ }
+ }
+
+ public bool EnableSideFilterResize
+ {
+ get
+ {
+ return this._enableSideFilterResize;
+ }
+ set
+ {
+ this._enableSideFilterResize = value;
+ this._performNeeded = true;
+ }
+ }
+
+ public bool EnableFilterMove
+ {
+ get
+ {
+ return this._enableFilterMove;
+ }
+ set
+ {
+ this._enableFilterMove = value;
+ this._performNeeded = true;
+ }
+ }
+
+ public float Decay
+ {
+ get
+ {
+ return this._decay;
+ }
+ set
+ {
+ this._decay = value;
+ }
+ }
+
+ public float Attack
+ {
+ get
+ {
+ return this._attack;
+ }
+ set
+ {
+ this._attack = value;
+ }
+ }
+
+ public int StepSize
+ {
+ get
+ {
+ return this._stepSize;
+ }
+ set
+ {
+ this._performNeeded = true;
+ this._stepSize = value;
+ }
+ }
+
+ public bool UseSnap
+ {
+ get
+ {
+ return this._useSnap;
+ }
+ set
+ {
+ this._useSnap = value;
+ }
+ }
+
+ public bool UseTimestamps
+ {
+ get
+ {
+ return this._useTimestamps;
+ }
+ set
+ {
+ this._useTimestamps = value;
+ this._scanlines = 0;
+ }
+ }
+
+ public int TimestampInterval
+ {
+ get
+ {
+ return this._timestampInterval;
+ }
+ set
+ {
+ this._timestampInterval = value;
+ }
+ }
+
+ public event ManualFrequencyChangeEventHandler FrequencyChanged;
+
+ public event ManualFrequencyChangeEventHandler CenterFrequencyChanged;
+
+ public event ManualBandwidthChangeEventHandler BandwidthChanged;
+
+ public event CustomPaintEventHandler CustomPaint;
+
+ public event LineInsertEventHandler LineInserted;
+
+ public Waterfall()
+ {
+ this._performTimer = new Timer();
+ this._performTimer.Enabled = true;
+ this._performTimer.Interval = 20;
+ this._performTimer.Tick += this.performTimer_Tick;
+ Rectangle clientRectangle = base.ClientRectangle;
+ int width = clientRectangle.Width;
+ clientRectangle = base.ClientRectangle;
+ this._buffer = new Bitmap(width, clientRectangle.Height, PixelFormat.Format32bppPArgb);
+ clientRectangle = base.ClientRectangle;
+ int width2 = clientRectangle.Width;
+ clientRectangle = base.ClientRectangle;
+ this._buffer2 = new Bitmap(width2, clientRectangle.Height, PixelFormat.Format32bppPArgb);
+ this._graphics = Graphics.FromImage(this._buffer);
+ this._graphics2 = Graphics.FromImage(this._buffer2);
+ this._gradientBrush = new LinearGradientBrush(new RectangleF(15f, 15f, (float)this._buffer.Width - 15f, (float)this._buffer.Height - 15f), Color.White, Color.Black, LinearGradientMode.Vertical);
+ this._gradientBrush.InterpolationColors = this._gradientColorBlend;
+ this._smoothedSpectrum = new float[this._buffer.Width - 60];
+ this._scaledSpectrum = new byte[this._smoothedSpectrum.Length];
+ this._temp = new float[this._smoothedSpectrum.Length];
+ this._gradientPixels = new int[256];
+ for (int i = 0; i < this._smoothedSpectrum.Length; i++)
+ {
+ this._smoothedSpectrum[i] = -250f;
+ }
+ base.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
+ base.SetStyle(ControlStyles.DoubleBuffer, true);
+ base.SetStyle(ControlStyles.UserPaint, true);
+ base.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
+ base.UpdateStyles();
+ this.BuildGradientVector();
+ }
+
+ ~Waterfall()
+ {
+ this._buffer.Dispose();
+ this._buffer2.Dispose();
+ this._graphics.Dispose();
+ this._graphics2.Dispose();
+ this._gradientBrush.Dispose();
+ }
+
+ private void performTimer_Tick(object sender, EventArgs e)
+ {
+ this.Perform(false);
+ }
+
+ private void Perform(bool force)
+ {
+ if (this._performNeeded | force)
+ {
+ this.DrawGradient();
+ this.CopyMainBuffer();
+ this.OnCustomPaint(new CustomPaintEventArgs(this._graphics2, this._cursorPosition));
+ if (this._mouseIn)
+ {
+ this.DrawCursor();
+ }
+ base.Invalidate();
+ this._performNeeded = false;
+ }
+ }
+
+ public void Perform()
+ {
+ this._performNeeded = true;
+ }
+
+ private void ApplyZoom()
+ {
+ this._scale = (float)Math.Pow(10.0, (double)((float)this._zoom * 4f / 100f));
+ this._displayCenterFrequency = this.GetDisplayCenterFrequency();
+ if (this._spectrumWidth > 0)
+ {
+ this._xIncrement = this._scale * (float)(this._buffer.Width - 60) / (float)this._spectrumWidth;
+ this._displayedBandwidth = (int)((float)this._spectrumWidth / this._scale);
+ this._performNeeded = true;
+ }
+ }
+
+ public void CenterZoom()
+ {
+ this._displayCenterFrequency = this.GetDisplayCenterFrequency();
+ this._performNeeded = true;
+ }
+
+ private long GetDisplayCenterFrequency()
+ {
+ long num = this._frequency + this._filterOffset;
+ switch (this._bandType)
+ {
+ case BandType.Lower:
+ num = (long)((float)num - (float)this._filterBandwidth * 0.5f);
+ break;
+ case BandType.Upper:
+ num = (long)((float)num + (float)this._filterBandwidth * 0.5f);
+ break;
+ }
+ long num2 = (long)((double)this._centerFrequency - (double)this._spectrumWidth * 0.5 - ((double)num - (double)this._spectrumWidth * 0.5 / (double)this._scale));
+ if (num2 > 0)
+ {
+ num += num2;
+ }
+ long num3 = (long)((double)num + (double)this._spectrumWidth * 0.5 / (double)this._scale - ((double)this._centerFrequency + (double)this._spectrumWidth * 0.5));
+ if (num3 > 0)
+ {
+ num -= num3;
+ }
+ return num;
+ }
+
+ public unsafe void Render(float* powerSpectrum, int length)
+ {
+ float offset = (float)(this._displayCenterFrequency - this._centerFrequency) / (float)this._spectrumWidth;
+ this.ExtractSpectrum(powerSpectrum, length, offset, this._scale, this._useSmoothing);
+ this.Draw();
+ }
+
+ private unsafe void ExtractSpectrum(float* powerSpectrum, int length, float offset, float scale, bool useSmoothing)
+ {
+ int num = this._displayOffset / 10 * 10;
+ int num2 = this._displayRange / 10 * 10;
+ float[] temp = this._temp;
+ fixed (float* ptr = temp)
+ {
+ float[] smoothedSpectrum = this._smoothedSpectrum;
+ fixed (float* ptr2 = smoothedSpectrum)
+ {
+ byte[] scaledSpectrum = this._scaledSpectrum;
+ fixed (byte* dest = scaledSpectrum)
+ {
+ Fourier.SmoothMaxCopy(powerSpectrum, ptr, length, this._smoothedSpectrum.Length, scale, offset);
+ if (useSmoothing)
+ {
+ for (int i = 0; i < this._temp.Length; i++)
+ {
+ float num3 = (ptr2[i] < ptr[i]) ? this.Attack : this.Decay;
+ ptr2[i] = ptr2[i] * (1f - num3) + ptr[i] * num3;
+ }
+ }
+ else
+ {
+ Utils.Memcpy(ptr2, ptr, this._smoothedSpectrum.Length * 4);
+ }
+ Fourier.ScaleFFT(ptr2, dest, this._smoothedSpectrum.Length, (float)(num - num2), (float)num);
+ }
+ }
+ }
+ }
+
+ private void Draw()
+ {
+ if (this._buffer.Width > 30 && this._buffer.Height > 30)
+ {
+ this.InsertNewLine();
+ if (this._useTimestamps && this._scanlines == 0)
+ {
+ this.DrawTimestamp();
+ }
+ this._performNeeded = true;
+ }
+ }
+
+ private unsafe void InsertNewLine()
+ {
+ if (this._buffer.Width > 0 && this._buffer.Height > 0)
+ {
+ Rectangle rect = new Rectangle(0, 0, this._buffer.Width, this._buffer.Height);
+ BitmapData bitmapData = this._buffer.LockBits(rect, ImageLockMode.ReadWrite, this._buffer.PixelFormat);
+ void* ptr;
+ void* dest;
+ if (bitmapData.Stride > 0)
+ {
+ ptr = (void*)bitmapData.Scan0;
+ dest = (void*)((long)bitmapData.Scan0 + bitmapData.Stride);
+ }
+ else
+ {
+ dest = (void*)bitmapData.Scan0;
+ ptr = (void*)((long)bitmapData.Scan0 - bitmapData.Stride);
+ }
+ Utils.Memmove(dest, ptr, (bitmapData.Height - 1) * Math.Abs(bitmapData.Stride));
+ if (this._scaledSpectrum != null && this._scaledSpectrum.Length != 0)
+ {
+ int* ptr2 = (int*)((byte*)ptr + 30L * 4L);
+ int* ptr3 = ptr2;
+ for (int i = 0; i < this._scaledSpectrum.Length; i++)
+ {
+ int val = (this._scaledSpectrum[i] + this._contrast * 2) * this._gradientPixels.Length / 255;
+ val = Math.Max(val, 0);
+ val = Math.Min(val, this._gradientPixels.Length - 1);
+ int* intPtr = ptr3;
+ ptr3 = intPtr + 1;
+ *intPtr = this._gradientPixels[val];
+ }
+ this.OnLineInserted(new LineInsertEventArgs(ptr2, this._scaledSpectrum.Length));
+ }
+ this._buffer.UnlockBits(bitmapData);
+ this._scanlines++;
+ if (this._scanlines >= this.TimestampInterval)
+ {
+ this._scanlines = 0;
+ }
+ }
+ }
+
+ private void DrawTimestamp()
+ {
+ using (FontFamily family = new FontFamily("Verdana"))
+ {
+ using (GraphicsPath graphicsPath = new GraphicsPath())
+ {
+ using (Pen pen = new Pen(Color.Black))
+ {
+ DateTime dateTime;
+ string s;
+ if (Waterfall._useUtcTimeStamp)
+ {
+ dateTime = DateTime.UtcNow;
+ s = dateTime.ToString("u");
+ }
+ else
+ {
+ dateTime = DateTime.Now;
+ s = dateTime.ToString();
+ }
+ graphicsPath.AddString(s, family, 0, 14f, new Point(30, 0), StringFormat.GenericTypographic);
+ SmoothingMode smoothingMode = this._graphics.SmoothingMode;
+ InterpolationMode interpolationMode = this._graphics.InterpolationMode;
+ this._graphics.SmoothingMode = SmoothingMode.AntiAlias;
+ this._graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;
+ pen.Width = 2f;
+ this._graphics.DrawPath(pen, graphicsPath);
+ this._graphics.FillPath(Brushes.White, graphicsPath);
+ this._graphics.SmoothingMode = smoothingMode;
+ this._graphics.InterpolationMode = interpolationMode;
+ }
+ }
+ }
+ }
+
+ private void DrawCursor()
+ {
+ this._lower = 0f;
+ float num = (float)((int)Math.Max((float)this._filterBandwidth * this._xIncrement, 2f) | 1);
+ float num2 = (float)this._buffer.Width * 0.5f + (float)(this._frequency - this._displayCenterFrequency) * this._xIncrement;
+ switch (this._bandType)
+ {
+ case BandType.Upper:
+ this._lower = num2;
+ break;
+ case BandType.Lower:
+ this._lower = num2 - num;
+ break;
+ case BandType.Center:
+ this._lower = num2 - num * 0.5f;
+ break;
+ }
+ this._lower += (float)this._filterOffset * this._xIncrement;
+ this._upper = this._lower + num;
+ using (SolidBrush brush = new SolidBrush(Color.FromArgb(80, Color.DarkGray)))
+ {
+ using (SolidBrush brush2 = new SolidBrush(Color.FromArgb(200, 50, 50, 50)))
+ {
+ using (Pen pen5 = new Pen(Color.FromArgb(200, Color.Gray)))
+ {
+ using (Pen pen3 = new Pen(Color.DodgerBlue))
+ {
+ using (Pen pen2 = new Pen(Color.LimeGreen))
+ {
+ using (Pen pen = new Pen(Color.Red))
+ {
+ using (FontFamily family = new FontFamily("Verdana"))
+ {
+ using (GraphicsPath graphicsPath = new GraphicsPath())
+ {
+ using (Pen pen4 = new Pen(Color.Black))
+ {
+ if (this._enableFilter && num < (float)this._buffer.Width)
+ {
+ float num3 = this._lower;
+ float num4 = num;
+ if (this._lower < 30f)
+ {
+ num3 = 31f;
+ num4 -= num3 - this._lower;
+ }
+ if (this._upper > (float)(this._buffer.Width - 30))
+ {
+ num4 -= this._upper - (float)(this._buffer.Width - 30);
+ }
+ this._graphics2.FillRectangle(brush, num3, 0f, num4, (float)this._buffer.Height);
+ }
+ if (this._enableFrequencyMarker && num2 > 30f && num2 < (float)(this._buffer.Width - 30))
+ {
+ pen.Width = 1f;
+ this._graphics2.DrawLine(pen, num2, 0f, num2, (float)this._buffer.Height);
+ }
+ if (this._enableHotTracking && this._cursorPosition.X >= 30 && this._cursorPosition.X <= this._buffer.Width - 30)
+ {
+ if (this.Cursor != Cursors.SizeWE && ((float)this._cursorPosition.X < this._lower || (float)this._cursorPosition.X > this._upper) && !this._changingFrequency && !this._changingCenterFrequency && !this._changingBandwidth)
+ {
+ pen2.DashStyle = DashStyle.Dash;
+ this._graphics2.DrawLine(pen3, this._snappedX, 0f, this._snappedX, (float)this._buffer.Height);
+ float num5 = num / 2f;
+ switch (this._bandType)
+ {
+ case BandType.Center:
+ this._graphics2.DrawLine(pen2, this._snappedX - num5, 0f, this._snappedX - num5, (float)this._buffer.Height);
+ this._graphics2.DrawLine(pen2, this._snappedX + num5, 0f, this._snappedX + num5, (float)this._buffer.Height);
+ break;
+ case BandType.Lower:
+ this._graphics2.DrawLine(pen2, this._snappedX - num, 0f, this._snappedX - num, (float)this._buffer.Height);
+ break;
+ case BandType.Upper:
+ this._graphics2.DrawLine(pen2, this._snappedX + num, 0f, this._snappedX + num, (float)this._buffer.Height);
+ break;
+ }
+ }
+ string s = (this._changingBandwidth || this.Cursor == Cursors.SizeWE) ? ("Bandwidth: " + SpectrumAnalyzer.GetFrequencyDisplay(this._filterBandwidth)) : (string.IsNullOrEmpty(this._customTitle) ? ((this._changingFrequency || ((float)this._cursorPosition.X >= this._lower && (float)this._cursorPosition.X <= this._upper)) ? ("VFO: " + SpectrumAnalyzer.GetFrequencyDisplay(this._frequency)) : ((!this._changingCenterFrequency) ? SpectrumAnalyzer.GetFrequencyDisplay(this._trackingFrequency) : ("Center Frequency: " + SpectrumAnalyzer.GetFrequencyDisplay(this._centerFrequency)))) : this._customTitle);
+ graphicsPath.AddString(s, family, 0, 16f, Point.Empty, StringFormat.GenericTypographic);
+ RectangleF bounds = graphicsPath.GetBounds();
+ Cursor current = Cursor.Current;
+ float val = (float)this._cursorPosition.X + 30f;
+ float val2 = (float)this._cursorPosition.Y + ((current == (Cursor)null) ? 32f : ((float)current.Size.Height)) - 8f;
+ val = Math.Min(val, (float)this._buffer.Width - bounds.Width - 30f - 20f);
+ val2 = Math.Min(val2, (float)this._buffer.Height - bounds.Height - 20f);
+ graphicsPath.Reset();
+ graphicsPath.AddString(s, family, 0, 16f, new Point((int)val, (int)val2), StringFormat.GenericTypographic);
+ SmoothingMode smoothingMode = this._graphics2.SmoothingMode;
+ InterpolationMode interpolationMode = this._graphics2.InterpolationMode;
+ this._graphics2.SmoothingMode = SmoothingMode.AntiAlias;
+ this._graphics2.InterpolationMode = InterpolationMode.HighQualityBilinear;
+ pen4.Width = 2f;
+ RectangleF bounds2 = graphicsPath.GetBounds();
+ bounds2.X -= 10f;
+ bounds2.Y -= 10f;
+ bounds2.Width += 20f;
+ bounds2.Height += 20f;
+ this._graphics2.FillRoundedRectangle(brush2, bounds2, 6);
+ this._graphics2.DrawRoundedRectangle(pen5, bounds2, 6);
+ this._graphics2.DrawPath(pen4, graphicsPath);
+ this._graphics2.FillPath(Brushes.White, graphicsPath);
+ this._graphics2.SmoothingMode = smoothingMode;
+ this._graphics2.InterpolationMode = interpolationMode;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private unsafe void CopyMainBuffer()
+ {
+ if (this._buffer.Width > 0 && this._buffer.Height > 0)
+ {
+ Rectangle rect = new Rectangle(0, 0, this._buffer.Width, this._buffer.Height);
+ BitmapData bitmapData = this._buffer.LockBits(rect, ImageLockMode.ReadOnly, this._buffer.PixelFormat);
+ BitmapData bitmapData2 = this._buffer2.LockBits(rect, ImageLockMode.WriteOnly, this._buffer2.PixelFormat);
+ Utils.Memcpy((void*)bitmapData2.Scan0, (void*)bitmapData.Scan0, Math.Abs(bitmapData.Stride) * bitmapData.Height);
+ this._buffer.UnlockBits(bitmapData);
+ this._buffer2.UnlockBits(bitmapData2);
+ }
+ }
+
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ Rectangle clientRectangle = base.ClientRectangle;
+ if (clientRectangle.Width > 60)
+ {
+ clientRectangle = base.ClientRectangle;
+ if (clientRectangle.Height <= 60)
+ {
+ goto IL_0024;
+ }
+ SpectrumAnalyzer.ConfigureGraphics(e.Graphics, false);
+ e.Graphics.DrawImageUnscaled(this._buffer2, 0, 0);
+ return;
+ }
+ goto IL_0024;
+ IL_0024:
+ e.Graphics.Clear(Color.Black);
+ }
+
+ protected override void OnPaintBackground(PaintEventArgs e)
+ {
+ }
+
+ protected unsafe override void OnResize(EventArgs e)
+ {
+ base.OnResize(e);
+ this._performNeeded = true;
+ Rectangle clientRectangle = base.ClientRectangle;
+ if (clientRectangle.Width > 60)
+ {
+ clientRectangle = base.ClientRectangle;
+ if (clientRectangle.Height > 60)
+ {
+ Bitmap buffer = this._buffer;
+ clientRectangle = base.ClientRectangle;
+ int width = clientRectangle.Width;
+ clientRectangle = base.ClientRectangle;
+ this._buffer = new Bitmap(width, clientRectangle.Height, PixelFormat.Format32bppPArgb);
+ Bitmap buffer2 = this._buffer2;
+ clientRectangle = base.ClientRectangle;
+ int width2 = clientRectangle.Width;
+ clientRectangle = base.ClientRectangle;
+ this._buffer2 = new Bitmap(width2, clientRectangle.Height, PixelFormat.Format32bppPArgb);
+ int num = this._buffer.Width - 60;
+ float[] smoothedSpectrum = this._smoothedSpectrum;
+ this._scaledSpectrum = new byte[num];
+ this._smoothedSpectrum = new float[num];
+ this._temp = new float[num];
+ float[] array = smoothedSpectrum;
+ fixed (float* powerSpectrum = array)
+ {
+ this.ExtractSpectrum(powerSpectrum, smoothedSpectrum.Length, 0f, 1f, false);
+ }
+ this._graphics.Dispose();
+ this._graphics = Graphics.FromImage(this._buffer);
+ SpectrumAnalyzer.ConfigureGraphics(this._graphics, false);
+ this._graphics2.Dispose();
+ this._graphics2 = Graphics.FromImage(this._buffer2);
+ SpectrumAnalyzer.ConfigureGraphics(this._graphics2, false);
+ this._graphics.Clear(Color.Black);
+ Rectangle destRect = new Rectangle(30, 0, this._buffer.Width - 60, buffer.Height);
+ this._graphics.DrawImage(buffer, destRect, 30, 0, buffer.Width - 60, buffer.Height, GraphicsUnit.Pixel);
+ buffer.Dispose();
+ buffer2.Dispose();
+ if (this._spectrumWidth > 0)
+ {
+ this._xIncrement = this._scale * (float)(this._buffer.Width - 60) / (float)this._spectrumWidth;
+ }
+ this._gradientBrush.Dispose();
+ this._gradientBrush = new LinearGradientBrush(new RectangleF(15f, 15f, (float)base.Width - 15f, (float)this._buffer.Height - 15f), Color.White, Color.Black, LinearGradientMode.Vertical);
+ this._gradientBrush.InterpolationColors = this._gradientColorBlend;
+ this.Perform(true);
+ }
+ }
+ }
+
+ private void DrawGradient()
+ {
+ using (Pen pen = new Pen(this._gradientBrush, 10f))
+ {
+ this._graphics.FillRectangle(Brushes.Black, this._buffer.Width - 30, 0, 30, this._buffer.Height);
+ this._graphics.DrawLine(pen, (float)this._buffer.Width - 15f, (float)this._buffer.Height - 15f, (float)this._buffer.Width - 15f, 15f);
+ }
+ }
+
+ private void BuildGradientVector()
+ {
+ using (Bitmap bitmap = new Bitmap(1, this._gradientPixels.Length))
+ {
+ using (Graphics graphics = Graphics.FromImage(bitmap))
+ {
+ using (LinearGradientBrush linearGradientBrush = new LinearGradientBrush(new Rectangle(0, 0, 1, this._gradientPixels.Length - 1), Color.White, Color.Black, LinearGradientMode.Vertical))
+ {
+ linearGradientBrush.InterpolationColors = this._gradientColorBlend;
+ Pen pen = new Pen(linearGradientBrush);
+ graphics.DrawLine(pen, 0, 0, 0, this._gradientPixels.Length - 1);
+ for (int i = 0; i < this._gradientPixels.Length; i++)
+ {
+ this._gradientPixels[this._gradientPixels.Length - 1 - i] = bitmap.GetPixel(0, i).ToArgb();
+ }
+ }
+ }
+ }
+ }
+
+ public float FrequencyToPoint(long frequency)
+ {
+ return (float)this._buffer.Width * 0.5f + (float)(frequency - this._displayCenterFrequency) * this._xIncrement;
+ }
+
+ public long PointToFrequency(float point)
+ {
+ return (long)((point - (float)this._buffer.Width * 0.5f) / this._xIncrement) + this._displayCenterFrequency;
+ }
+
+ protected virtual void OnFrequencyChanged(FrequencyEventArgs e)
+ {
+ ManualFrequencyChangeEventHandler frequencyChanged = this.FrequencyChanged;
+ if (frequencyChanged != null)
+ {
+ frequencyChanged(this, e);
+ }
+ }
+
+ protected virtual void OnCenterFrequencyChanged(FrequencyEventArgs e)
+ {
+ ManualFrequencyChangeEventHandler centerFrequencyChanged = this.CenterFrequencyChanged;
+ if (centerFrequencyChanged != null)
+ {
+ centerFrequencyChanged(this, e);
+ }
+ }
+
+ protected virtual void OnBandwidthChanged(BandwidthEventArgs e)
+ {
+ ManualBandwidthChangeEventHandler bandwidthChanged = this.BandwidthChanged;
+ if (bandwidthChanged != null)
+ {
+ bandwidthChanged(this, e);
+ }
+ }
+
+ private bool UpdateFrequency(long f, FrequencyChangeSource source)
+ {
+ if (this._useSnap)
+ {
+ f = (long)((float)f + (float)(Math.Sign(f) * this._stepSize) * 0.5f) / this._stepSize * this._stepSize;
+ }
+ long num = (long)((float)this._displayCenterFrequency - (float)this._spectrumWidth / this._scale * 0.5f);
+ long num2 = (long)((float)this._displayCenterFrequency + (float)this._spectrumWidth / this._scale * 0.5f);
+ if (source == FrequencyChangeSource.Scroll)
+ {
+ long num3 = 0L;
+ if (f < num)
+ {
+ num3 = f - num;
+ }
+ else if (f > num2)
+ {
+ num3 = f - num2;
+ }
+ if (num3 != 0L && !this.UpdateCenterFrequency(this._centerFrequency + num3, source))
+ {
+ return false;
+ }
+ }
+ else if (f < num)
+ {
+ f = num;
+ }
+ else if (f > num2)
+ {
+ f = num2;
+ }
+ if (f != this._frequency)
+ {
+ FrequencyEventArgs frequencyEventArgs = new FrequencyEventArgs(f, source);
+ this.OnFrequencyChanged(frequencyEventArgs);
+ if (!frequencyEventArgs.Cancel)
+ {
+ this._frequency = frequencyEventArgs.Frequency;
+ this._performNeeded = true;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private bool UpdateCenterFrequency(long f, FrequencyChangeSource source)
+ {
+ if (f < 0)
+ {
+ f = 0L;
+ }
+ if (this._useSnap)
+ {
+ f = (long)((float)f + (float)(Math.Sign(f) * this._stepSize) * 0.5f) / this._stepSize * this._stepSize;
+ }
+ if (f != this._centerFrequency)
+ {
+ FrequencyEventArgs frequencyEventArgs = new FrequencyEventArgs(f, source);
+ this.OnCenterFrequencyChanged(frequencyEventArgs);
+ if (!frequencyEventArgs.Cancel)
+ {
+ long num = frequencyEventArgs.Frequency - this._centerFrequency;
+ this._displayCenterFrequency += num;
+ this._centerFrequency = frequencyEventArgs.Frequency;
+ this._performNeeded = true;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void UpdateBandwidth(int bw)
+ {
+ bw = 10 * (bw / 10);
+ if (bw < 10)
+ {
+ bw = 10;
+ }
+ int num = (int)((float)(18 * this._spectrumWidth) / this._scale / (float)(this._buffer.Width - 60));
+ if (bw < num)
+ {
+ bw = num;
+ }
+ if (bw != this._filterBandwidth)
+ {
+ int num2 = this._enableSideFilterResize ? ((int)((float)(bw - this._filterBandwidth) * 0.5f)) : 0;
+ int offset = this._filterOffset + ((this._side == BandType.Upper) ? num2 : (-num2));
+ BandwidthEventArgs bandwidthEventArgs = new BandwidthEventArgs(bw, offset, this._side);
+ this.OnBandwidthChanged(bandwidthEventArgs);
+ if (!bandwidthEventArgs.Cancel)
+ {
+ this._filterOffset = bandwidthEventArgs.Offset;
+ this._filterBandwidth = bandwidthEventArgs.Bandwidth;
+ this._performNeeded = true;
+ }
+ }
+ }
+
+ protected virtual void OnCustomPaint(CustomPaintEventArgs e)
+ {
+ CustomPaintEventHandler customPaint = this.CustomPaint;
+ if (customPaint != null)
+ {
+ customPaint(this, e);
+ this._customTitle = e.CustomTitle;
+ }
+ }
+
+ protected virtual void OnLineInserted(LineInsertEventArgs e)
+ {
+ LineInsertEventHandler lineInserted = this.LineInserted;
+ if (lineInserted != null)
+ {
+ lineInserted(this, e);
+ }
+ }
+
+ protected override void OnMouseDown(MouseEventArgs e)
+ {
+ base.OnMouseDown(e);
+ if (e.X >= 30 && e.X <= this._buffer.Width - 30)
+ {
+ if (e.Button == MouseButtons.Left)
+ {
+ float num = Math.Max((float)this._filterBandwidth * this._xIncrement, 2f);
+ if (this._enableFilter)
+ {
+ if (this._enableFilterMove && (float)e.X > this._lower && (float)e.X < this._upper && num < (float)this._buffer.Width)
+ {
+ this._oldX = e.X;
+ this._oldFrequency = this._frequency;
+ this._changingFrequency = true;
+ }
+ else if (this._upper - this._lower > 12f)
+ {
+ if (Math.Abs((float)e.X - this._upper - 6f) <= 6f && (this._bandType == BandType.Center || this._bandType == BandType.Upper))
+ {
+ this._side = BandType.Upper;
+ this._oldX = e.X;
+ this._oldFilterBandwidth = this._filterBandwidth;
+ this._changingBandwidth = true;
+ }
+ else if (Math.Abs((float)e.X - this._lower + 6f) <= 6f && (this._bandType == BandType.Center || this._bandType == BandType.Lower))
+ {
+ this._side = BandType.Lower;
+ this._oldX = e.X;
+ this._oldFilterBandwidth = this._filterBandwidth;
+ this._changingBandwidth = true;
+ }
+ }
+ }
+ if (!this._changingBandwidth && !this._changingFrequency)
+ {
+ this._oldX = e.X;
+ this._oldCenterFrequency = this._centerFrequency;
+ this._changingCenterFrequency = true;
+ }
+ }
+ else if (e.Button == MouseButtons.Right)
+ {
+ this.UpdateFrequency(this._frequency / 500 * 500, FrequencyChangeSource.Click);
+ }
+ }
+ }
+
+ protected override void OnMouseUp(MouseEventArgs e)
+ {
+ base.OnMouseUp(e);
+ if (this._enableFilterMove && this._changingCenterFrequency && e.X == this._oldX)
+ {
+ long f = (long)(((float)this._oldX - (float)this._buffer.Width * 0.5f) * (float)this._spectrumWidth / this._scale / (float)(this._buffer.Width - 60) + (float)this._displayCenterFrequency);
+ this.UpdateFrequency(f, FrequencyChangeSource.Click);
+ }
+ this._changingCenterFrequency = false;
+ this._changingBandwidth = false;
+ this._changingFrequency = false;
+ this._performNeeded = true;
+ }
+
+ protected override void OnMouseMove(MouseEventArgs e)
+ {
+ base.OnMouseMove(e);
+ this._cursorPosition.X = e.X;
+ this._cursorPosition.Y = e.Y;
+ if (this._enableHotTracking)
+ {
+ this._snappedX = (float)e.X;
+ this._trackingFrequency = this.PointToFrequency((float)e.X);
+ if (this._useSnap)
+ {
+ this._trackingFrequency = (long)((float)this._trackingFrequency + (float)(Math.Sign(this._trackingFrequency) * this._stepSize) * 0.5f) / this._stepSize * this._stepSize;
+ this._snappedX = this.FrequencyToPoint(this._trackingFrequency);
+ }
+ }
+ if (this._changingFrequency)
+ {
+ long f = (long)((float)((e.X - this._oldX) * this._spectrumWidth) / this._scale / (float)(this._buffer.Width - 60) + (float)this._oldFrequency);
+ this.UpdateFrequency(f, FrequencyChangeSource.Drag);
+ }
+ else if (this._changingCenterFrequency)
+ {
+ long f2 = (long)((float)((this._oldX - e.X) * this._spectrumWidth) / this._scale / (float)(this._buffer.Width - 60) + (float)this._oldCenterFrequency);
+ this.UpdateCenterFrequency(f2, FrequencyChangeSource.Drag);
+ }
+ else if (this._changingBandwidth)
+ {
+ int num = 0;
+ switch (this._side)
+ {
+ case BandType.Upper:
+ num = e.X - this._oldX;
+ break;
+ case BandType.Lower:
+ num = this._oldX - e.X;
+ break;
+ }
+ if (this._bandType == BandType.Center && !this._enableSideFilterResize)
+ {
+ num *= 2;
+ }
+ num = (int)((float)(num * this._spectrumWidth) / this._scale / (float)(this._buffer.Width - 60) + (float)this._oldFilterBandwidth);
+ this.UpdateBandwidth(num);
+ }
+ else if (this._enableFilter)
+ {
+ if (e.X >= 30 && e.X <= this._buffer.Width - 30 && this._upper - this._lower > 12f)
+ {
+ if (Math.Abs((float)e.X - this._lower + 6f) <= 6f && (this._bandType == BandType.Center || this._bandType == BandType.Lower))
+ {
+ goto IL_0267;
+ }
+ if (Math.Abs((float)e.X - this._upper - 6f) <= 6f && (this._bandType == BandType.Center || this._bandType == BandType.Upper))
+ {
+ goto IL_0267;
+ }
+ this.Cursor = Cursors.Default;
+ }
+ else
+ {
+ this.Cursor = Cursors.Default;
+ }
+ }
+ goto IL_028c;
+ IL_028c:
+ this._performNeeded = this._enableHotTracking;
+ return;
+ IL_0267:
+ this.Cursor = Cursors.SizeWE;
+ goto IL_028c;
+ }
+
+ protected override void OnMouseEnter(EventArgs e)
+ {
+ base.Focus();
+ base.OnMouseEnter(e);
+ this._mouseIn = true;
+ this._performNeeded = true;
+ }
+
+ protected override void OnMouseLeave(EventArgs e)
+ {
+ base.OnMouseLeave(e);
+ this._mouseIn = false;
+ this._performNeeded = true;
+ this._cursorPosition = Point.Empty;
+ }
+
+ protected override void OnMouseWheel(MouseEventArgs e)
+ {
+ base.OnMouseWheel(e);
+ if (this._enableFilter)
+ {
+ this.UpdateFrequency(this._frequency + this._stepSize * Math.Sign(e.Delta), FrequencyChangeSource.Scroll);
+ }
+ }
+ }
+}
diff --git a/SDRSharp.PanView/refs Radio.txt b/SDRSharp.PanView/refs Radio.txt
new file mode 100644
index 0000000..e69de29
diff --git a/SDRSharp.Radio/PortAudioSharp/PaDeviceIndex.cs b/SDRSharp.Radio/PortAudioSharp/PaDeviceIndex.cs
new file mode 100644
index 0000000..4e9c484
--- /dev/null
+++ b/SDRSharp.Radio/PortAudioSharp/PaDeviceIndex.cs
@@ -0,0 +1,8 @@
+namespace PortAudioSharp
+{
+ internal enum PaDeviceIndex
+ {
+ PaNoDevice = -1,
+ PaUseHostApiSpecificDeviceSpecification = -2
+ }
+}
diff --git a/SDRSharp.Radio/PortAudioSharp/PaDeviceInfo.cs b/SDRSharp.Radio/PortAudioSharp/PaDeviceInfo.cs
new file mode 100644
index 0000000..ec36b61
--- /dev/null
+++ b/SDRSharp.Radio/PortAudioSharp/PaDeviceInfo.cs
@@ -0,0 +1,33 @@
+using System.Runtime.InteropServices;
+
+namespace PortAudioSharp
+{
+ internal struct PaDeviceInfo
+ {
+ public int structVersion;
+
+ [MarshalAs(UnmanagedType.LPStr)]
+ public string name;
+
+ public int hostApi;
+
+ public int maxInputChannels;
+
+ public int maxOutputChannels;
+
+ public double defaultLowInputLatency;
+
+ public double defaultLowOutputLatency;
+
+ public double defaultHighInputLatency;
+
+ public double defaultHighOutputLatency;
+
+ public double defaultSampleRate;
+
+ public override string ToString()
+ {
+ return "[" + ((object)this).GetType().Name + "]\nname: " + this.name + "\nhostApi: " + this.hostApi + "\nmaxInputChannels: " + this.maxInputChannels + "\nmaxOutputChannels: " + this.maxOutputChannels + "\ndefaultLowInputLatency: " + this.defaultLowInputLatency + "\ndefaultLowOutputLatency: " + this.defaultLowOutputLatency + "\ndefaultHighInputLatency: " + this.defaultHighInputLatency + "\ndefaultHighOutputLatency: " + this.defaultHighOutputLatency + "\ndefaultSampleRate: " + this.defaultSampleRate;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/PortAudioSharp/PaError.cs b/SDRSharp.Radio/PortAudioSharp/PaError.cs
new file mode 100644
index 0000000..7d59089
--- /dev/null
+++ b/SDRSharp.Radio/PortAudioSharp/PaError.cs
@@ -0,0 +1,36 @@
+namespace PortAudioSharp
+{
+ internal enum PaError
+ {
+ paNoError,
+ paNotInitialized = -10000,
+ paUnanticipatedHostError,
+ paInvalidChannelCount,
+ paInvalidSampleRate,
+ paInvalidDevice,
+ paInvalidFlag,
+ paSampleFormatNotSupported,
+ paBadIODeviceCombination,
+ paInsufficientMemory,
+ paBufferTooBig,
+ paBufferTooSmall,
+ paNullCallback,
+ paBadStreamPtr,
+ paTimedOut,
+ paInternalError,
+ paDeviceUnavailable,
+ paIncompatibleHostApiSpecificStreamInfo,
+ paStreamIsStopped,
+ paStreamIsNotStopped,
+ paInputOverflowed,
+ paOutputUnderflowed,
+ paHostApiNotFound,
+ paInvalidHostApi,
+ paCanNotReadFromACallbackStream,
+ paCanNotWriteToACallbackStream,
+ paCanNotReadFromAnOutputOnlyStream,
+ paCanNotWriteToAnInputOnlyStream,
+ paIncompatibleStreamHostApi,
+ paBadBufferPtr
+ }
+}
diff --git a/SDRSharp.Radio/PortAudioSharp/PaHostApiInfo.cs b/SDRSharp.Radio/PortAudioSharp/PaHostApiInfo.cs
new file mode 100644
index 0000000..458532b
--- /dev/null
+++ b/SDRSharp.Radio/PortAudioSharp/PaHostApiInfo.cs
@@ -0,0 +1,25 @@
+using System.Runtime.InteropServices;
+
+namespace PortAudioSharp
+{
+ internal struct PaHostApiInfo
+ {
+ public int structVersion;
+
+ public PaHostApiTypeId type;
+
+ [MarshalAs(UnmanagedType.LPStr)]
+ public string name;
+
+ public int deviceCount;
+
+ public int defaultInputDevice;
+
+ public int defaultOutputDevice;
+
+ public override string ToString()
+ {
+ return "[" + ((object)this).GetType().Name + "]\nstructVersion: " + this.structVersion + "\ntype: " + this.type + "\nname: " + this.name + "\ndeviceCount: " + this.deviceCount + "\ndefaultInputDevice: " + this.defaultInputDevice + "\ndefaultOutputDevice: " + this.defaultOutputDevice;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/PortAudioSharp/PaHostApiTypeId.cs b/SDRSharp.Radio/PortAudioSharp/PaHostApiTypeId.cs
new file mode 100644
index 0000000..bad7979
--- /dev/null
+++ b/SDRSharp.Radio/PortAudioSharp/PaHostApiTypeId.cs
@@ -0,0 +1,20 @@
+namespace PortAudioSharp
+{
+ internal enum PaHostApiTypeId : uint
+ {
+ paInDevelopment,
+ paDirectSound,
+ paMME,
+ paASIO,
+ paSoundManager,
+ paCoreAudio,
+ paOSS = 7u,
+ paALSA,
+ paAL,
+ paBeOS,
+ paWDMKS,
+ paJACK,
+ paWASAPI,
+ paAudioScienceHPI
+ }
+}
diff --git a/SDRSharp.Radio/PortAudioSharp/PaHostErrorInfo.cs b/SDRSharp.Radio/PortAudioSharp/PaHostErrorInfo.cs
new file mode 100644
index 0000000..6b06da1
--- /dev/null
+++ b/SDRSharp.Radio/PortAudioSharp/PaHostErrorInfo.cs
@@ -0,0 +1,19 @@
+using System.Runtime.InteropServices;
+
+namespace PortAudioSharp
+{
+ internal struct PaHostErrorInfo
+ {
+ public PaHostApiTypeId hostApiType;
+
+ public int errorCode;
+
+ [MarshalAs(UnmanagedType.LPStr)]
+ public string errorText;
+
+ public override string ToString()
+ {
+ return "[" + ((object)this).GetType().Name + "]\nhostApiType: " + this.hostApiType + "\nerrorCode: " + this.errorCode + "\nerrorText: " + this.errorText;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/PortAudioSharp/PaSampleFormat.cs b/SDRSharp.Radio/PortAudioSharp/PaSampleFormat.cs
new file mode 100644
index 0000000..f4200c8
--- /dev/null
+++ b/SDRSharp.Radio/PortAudioSharp/PaSampleFormat.cs
@@ -0,0 +1,14 @@
+namespace PortAudioSharp
+{
+ internal enum PaSampleFormat : uint
+ {
+ PaFloat32 = 1u,
+ PaInt32,
+ PaInt24 = 4u,
+ PaInt16 = 8u,
+ PaInt8 = 0x10,
+ PaUInt8 = 0x20,
+ PaCustomFormat = 0x10000,
+ PaNonInterleaved = 0x80000000
+ }
+}
diff --git a/SDRSharp.Radio/PortAudioSharp/PaStreamCallbackDelegate.cs b/SDRSharp.Radio/PortAudioSharp/PaStreamCallbackDelegate.cs
new file mode 100644
index 0000000..4ec6754
--- /dev/null
+++ b/SDRSharp.Radio/PortAudioSharp/PaStreamCallbackDelegate.cs
@@ -0,0 +1,8 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace PortAudioSharp
+{
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal unsafe delegate PaStreamCallbackResult PaStreamCallbackDelegate(float* input, float* output, uint frameCount, ref PaStreamCallbackTimeInfo timeInfo, PaStreamCallbackFlags statusFlags, IntPtr userData);
+}
diff --git a/SDRSharp.Radio/PortAudioSharp/PaStreamCallbackFlags.cs b/SDRSharp.Radio/PortAudioSharp/PaStreamCallbackFlags.cs
new file mode 100644
index 0000000..3537556
--- /dev/null
+++ b/SDRSharp.Radio/PortAudioSharp/PaStreamCallbackFlags.cs
@@ -0,0 +1,11 @@
+namespace PortAudioSharp
+{
+ internal enum PaStreamCallbackFlags : uint
+ {
+ PaInputUnderflow = 1u,
+ PaInputOverflow,
+ PaOutputUnderflow = 4u,
+ PaOutputOverflow = 8u,
+ PaPrimingOutput = 0x10
+ }
+}
diff --git a/SDRSharp.Radio/PortAudioSharp/PaStreamCallbackResult.cs b/SDRSharp.Radio/PortAudioSharp/PaStreamCallbackResult.cs
new file mode 100644
index 0000000..363c251
--- /dev/null
+++ b/SDRSharp.Radio/PortAudioSharp/PaStreamCallbackResult.cs
@@ -0,0 +1,9 @@
+namespace PortAudioSharp
+{
+ internal enum PaStreamCallbackResult : uint
+ {
+ PaContinue,
+ PaComplete,
+ PaAbort
+ }
+}
diff --git a/SDRSharp.Radio/PortAudioSharp/PaStreamCallbackTimeInfo.cs b/SDRSharp.Radio/PortAudioSharp/PaStreamCallbackTimeInfo.cs
new file mode 100644
index 0000000..463f160
--- /dev/null
+++ b/SDRSharp.Radio/PortAudioSharp/PaStreamCallbackTimeInfo.cs
@@ -0,0 +1,16 @@
+namespace PortAudioSharp
+{
+ internal struct PaStreamCallbackTimeInfo
+ {
+ public double inputBufferAdcTime;
+
+ public double currentTime;
+
+ public double outputBufferDacTime;
+
+ public override string ToString()
+ {
+ return "[" + ((object)this).GetType().Name + "]\ncurrentTime: " + this.currentTime + "\ninputBufferAdcTime: " + this.inputBufferAdcTime + "\noutputBufferDacTime: " + this.outputBufferDacTime;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/PortAudioSharp/PaStreamFinishedCallbackDelegate.cs b/SDRSharp.Radio/PortAudioSharp/PaStreamFinishedCallbackDelegate.cs
new file mode 100644
index 0000000..82da18c
--- /dev/null
+++ b/SDRSharp.Radio/PortAudioSharp/PaStreamFinishedCallbackDelegate.cs
@@ -0,0 +1,8 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace PortAudioSharp
+{
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void PaStreamFinishedCallbackDelegate(IntPtr userData);
+}
diff --git a/SDRSharp.Radio/PortAudioSharp/PaStreamFlags.cs b/SDRSharp.Radio/PortAudioSharp/PaStreamFlags.cs
new file mode 100644
index 0000000..11b9627
--- /dev/null
+++ b/SDRSharp.Radio/PortAudioSharp/PaStreamFlags.cs
@@ -0,0 +1,12 @@
+namespace PortAudioSharp
+{
+ internal enum PaStreamFlags : uint
+ {
+ PaNoFlag,
+ PaClipOff,
+ PaDitherOff,
+ PaNeverDropInput = 4u,
+ PaPrimeOutputBuffersUsingStreamCallback = 8u,
+ PaPlatformSpecificFlags = 4294901760u
+ }
+}
diff --git a/SDRSharp.Radio/PortAudioSharp/PaStreamInfo.cs b/SDRSharp.Radio/PortAudioSharp/PaStreamInfo.cs
new file mode 100644
index 0000000..64aa21f
--- /dev/null
+++ b/SDRSharp.Radio/PortAudioSharp/PaStreamInfo.cs
@@ -0,0 +1,18 @@
+namespace PortAudioSharp
+{
+ internal struct PaStreamInfo
+ {
+ public int structVersion;
+
+ public double inputLatency;
+
+ public double outputLatency;
+
+ public double sampleRate;
+
+ public override string ToString()
+ {
+ return "[" + ((object)this).GetType().Name + "]\nstructVersion: " + this.structVersion + "\ninputLatency: " + this.inputLatency + "\noutputLatency: " + this.outputLatency + "\nsampleRate: " + this.sampleRate;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/PortAudioSharp/PaStreamParameters.cs b/SDRSharp.Radio/PortAudioSharp/PaStreamParameters.cs
new file mode 100644
index 0000000..8a9ca6d
--- /dev/null
+++ b/SDRSharp.Radio/PortAudioSharp/PaStreamParameters.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace PortAudioSharp
+{
+ internal struct PaStreamParameters
+ {
+ public int device;
+
+ public int channelCount;
+
+ public PaSampleFormat sampleFormat;
+
+ public double suggestedLatency;
+
+ public IntPtr hostApiSpecificStreamInfo;
+
+ public override string ToString()
+ {
+ return "[" + ((object)this).GetType().Name + "]\ndevice: " + this.device + "\nchannelCount: " + this.channelCount + "\nsampleFormat: " + this.sampleFormat + "\nsuggestedLatency: " + this.suggestedLatency;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/PortAudioSharp/PortAudioAPI.cs b/SDRSharp.Radio/PortAudioSharp/PortAudioAPI.cs
new file mode 100644
index 0000000..4dba8fc
--- /dev/null
+++ b/SDRSharp.Radio/PortAudioSharp/PortAudioAPI.cs
@@ -0,0 +1,202 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace PortAudioSharp
+{
+ internal static class PortAudioAPI
+ {
+ public const int PaFormatIsSupported = 0;
+
+ public const int PaFramesPerBufferUnspecified = 0;
+
+ private const string PortAudioLibrary = "portaudio";
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int Pa_GetVersion();
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl, EntryPoint = "Pa_GetVersionText")]
+ private static extern IntPtr IntPtr_Pa_GetVersionText();
+
+ public static string Pa_GetVersionText()
+ {
+ return Marshal.PtrToStringAnsi(PortAudioAPI.IntPtr_Pa_GetVersionText());
+ }
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl, EntryPoint = "Pa_GetErrorText")]
+ public static extern IntPtr IntPtr_Pa_GetErrorText(PaError errorCode);
+
+ public static string Pa_GetErrorText(PaError errorCode)
+ {
+ return Marshal.PtrToStringAnsi(PortAudioAPI.IntPtr_Pa_GetErrorText(errorCode));
+ }
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_Initialize();
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_Terminate();
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int Pa_GetHostApiCount();
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int Pa_GetDefaultHostApi();
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl, EntryPoint = "Pa_GetHostApiInfo")]
+ public static extern IntPtr IntPtr_Pa_GetHostApiInfo(int hostApi);
+
+ public static PaHostApiInfo Pa_GetHostApiInfo(int hostApi)
+ {
+ return (PaHostApiInfo)Marshal.PtrToStructure(PortAudioAPI.IntPtr_Pa_GetHostApiInfo(hostApi), typeof(PaHostApiInfo));
+ }
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int Pa_HostApiTypeIdToHostApiIndex(PaHostApiTypeId type);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int Pa_HostApiDeviceIndexToDeviceIndex(int hostApi, int hostApiDeviceIndex);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl, EntryPoint = "Pa_GetLastHostErrorInfo")]
+ public static extern IntPtr IntPtr_Pa_GetLastHostErrorInfo();
+
+ public static PaHostErrorInfo Pa_GetLastHostErrorInfo()
+ {
+ return (PaHostErrorInfo)Marshal.PtrToStructure(PortAudioAPI.IntPtr_Pa_GetLastHostErrorInfo(), typeof(PaHostErrorInfo));
+ }
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int Pa_GetDeviceCount();
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int Pa_GetDefaultInputDevice();
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int Pa_GetDefaultOutputDevice();
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl, EntryPoint = "Pa_GetDeviceInfo")]
+ public static extern IntPtr IntPtr_Pa_GetDeviceInfo(int device);
+
+ public static PaDeviceInfo Pa_GetDeviceInfo(int device)
+ {
+ return (PaDeviceInfo)Marshal.PtrToStructure(PortAudioAPI.IntPtr_Pa_GetDeviceInfo(device), typeof(PaDeviceInfo));
+ }
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_IsFormatSupported(ref PaStreamParameters inputParameters, ref PaStreamParameters outputParameters, double sampleRate);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_IsFormatSupported(IntPtr inputParameters, ref PaStreamParameters outputParameters, double sampleRate);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_IsFormatSupported(ref PaStreamParameters inputParameters, IntPtr outputParameters, double sampleRate);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_OpenStream(out IntPtr stream, ref PaStreamParameters inputParameters, ref PaStreamParameters outputParameters, double sampleRate, uint framesPerBuffer, PaStreamFlags streamFlags, PaStreamCallbackDelegate streamCallback, IntPtr userData);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_OpenStream(out IntPtr stream, IntPtr inputParameters, ref PaStreamParameters outputParameters, double sampleRate, uint framesPerBuffer, PaStreamFlags streamFlags, PaStreamCallbackDelegate streamCallback, IntPtr userData);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_OpenStream(out IntPtr stream, ref PaStreamParameters inputParameters, IntPtr outputParameters, double sampleRate, uint framesPerBuffer, PaStreamFlags streamFlags, PaStreamCallbackDelegate streamCallback, IntPtr userData);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_OpenDefaultStream(out IntPtr stream, int numInputChannels, int numOutputChannels, uint sampleFormat, double sampleRate, uint framesPerBuffer, PaStreamCallbackDelegate streamCallback, IntPtr userData);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_CloseStream(IntPtr stream);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_SetStreamFinishedCallback(ref IntPtr stream, [MarshalAs(UnmanagedType.FunctionPtr)] PaStreamFinishedCallbackDelegate streamFinishedCallback);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_StartStream(IntPtr stream);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_StopStream(IntPtr stream);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_AbortStream(IntPtr stream);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_IsStreamStopped(IntPtr stream);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_IsStreamActive(IntPtr stream);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl, EntryPoint = "Pa_GetStreamInfo")]
+ public static extern IntPtr IntPtr_Pa_GetStreamInfo(IntPtr stream);
+
+ public static PaStreamInfo Pa_GetStreamInfo(IntPtr stream)
+ {
+ return (PaStreamInfo)Marshal.PtrToStructure(PortAudioAPI.IntPtr_Pa_GetStreamInfo(stream), typeof(PaStreamInfo));
+ }
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern double Pa_GetStreamTime(IntPtr stream);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern double Pa_GetStreamCpuLoad(IntPtr stream);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_ReadStream(IntPtr stream, [Out] float[] buffer, uint frames);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_ReadStream(IntPtr stream, [Out] byte[] buffer, uint frames);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_ReadStream(IntPtr stream, [Out] sbyte[] buffer, uint frames);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_ReadStream(IntPtr stream, [Out] ushort[] buffer, uint frames);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_ReadStream(IntPtr stream, [Out] short[] buffer, uint frames);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_ReadStream(IntPtr stream, [Out] uint[] buffer, uint frames);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_ReadStream(IntPtr stream, [Out] int[] buffer, uint frames);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_ReadStream(IntPtr stream, IntPtr buffer, uint frames);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_WriteStream(IntPtr stream, [In] float[] buffer, uint frames);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_WriteStream(IntPtr stream, [In] byte[] buffer, uint frames);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_WriteStream(IntPtr stream, [In] sbyte[] buffer, uint frames);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_WriteStream(IntPtr stream, [In] ushort[] buffer, uint frames);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_WriteStream(IntPtr stream, [In] short[] buffer, uint frames);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_WriteStream(IntPtr stream, [In] uint[] buffer, uint frames);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_WriteStream(IntPtr stream, [In] int[] buffer, uint frames);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int Pa_GetStreamReadAvailable(IntPtr stream);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int Pa_GetStreamWriteAvailable(IntPtr stream);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern PaError Pa_GetSampleSize(PaSampleFormat format);
+
+ [DllImport("portaudio", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void Pa_Sleep(int msec);
+
+ static PortAudioAPI()
+ {
+ PortAudioAPI.Pa_Initialize();
+ }
+ }
+}
diff --git a/SDRSharp.Radio/Properties/AssemblyInfo.cs b/SDRSharp.Radio/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..2454348
--- /dev/null
+++ b/SDRSharp.Radio/Properties/AssemblyInfo.cs
@@ -0,0 +1,15 @@
+using System.Diagnostics;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.Versioning;
+using System.Security.Permissions;
+
+[assembly: CompilationRelaxations(8)]
+[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
+[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
+[assembly: AssemblyTitle("Radio DSP")]
+[assembly: AssemblyDescription("Radio DSP")]
+[assembly: AssemblyProduct("SDR#")]
+[assembly: AssemblyCopyright("Copyright © Youssef TOUIL 2012")]
+[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
+[assembly: AssemblyVersion("0.0.0.0")]
diff --git a/SDRSharp.Radio/SDRSharp.Radio.PortAudio/AudioBufferAvailableDelegate.cs b/SDRSharp.Radio/SDRSharp.Radio.PortAudio/AudioBufferAvailableDelegate.cs
new file mode 100644
index 0000000..01c1618
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio.PortAudio/AudioBufferAvailableDelegate.cs
@@ -0,0 +1,4 @@
+namespace SDRSharp.Radio.PortAudio
+{
+ public unsafe delegate void AudioBufferAvailableDelegate(float* buffer, int length);
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio.PortAudio/AudioBufferNeededDelegate.cs b/SDRSharp.Radio/SDRSharp.Radio.PortAudio/AudioBufferNeededDelegate.cs
new file mode 100644
index 0000000..eda7799
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio.PortAudio/AudioBufferNeededDelegate.cs
@@ -0,0 +1,4 @@
+namespace SDRSharp.Radio.PortAudio
+{
+ public unsafe delegate void AudioBufferNeededDelegate(float* buffer, int length);
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio.PortAudio/AudioDevice.cs b/SDRSharp.Radio/SDRSharp.Radio.PortAudio/AudioDevice.cs
new file mode 100644
index 0000000..6a112a8
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio.PortAudio/AudioDevice.cs
@@ -0,0 +1,68 @@
+using PortAudioSharp;
+using System.Collections.Generic;
+
+namespace SDRSharp.Radio.PortAudio
+{
+ public class AudioDevice
+ {
+ public int Index
+ {
+ get;
+ set;
+ }
+
+ public string Name
+ {
+ get;
+ set;
+ }
+
+ public string Host
+ {
+ get;
+ set;
+ }
+
+ public DeviceDirection Direction
+ {
+ get;
+ set;
+ }
+
+ public bool IsDefault
+ {
+ get;
+ set;
+ }
+
+ public static List GetDevices(DeviceDirection direction)
+ {
+ List list = new List();
+ int num = PortAudioAPI.Pa_GetDefaultInputDevice();
+ int num2 = PortAudioAPI.Pa_GetDefaultOutputDevice();
+ int num3 = PortAudioAPI.Pa_GetDeviceCount();
+ for (int i = 0; i < num3; i++)
+ {
+ PaDeviceInfo paDeviceInfo = PortAudioAPI.Pa_GetDeviceInfo(i);
+ DeviceDirection deviceDirection = (paDeviceInfo.maxInputChannels <= 0) ? DeviceDirection.Output : ((paDeviceInfo.maxOutputChannels > 0) ? DeviceDirection.InputOutput : DeviceDirection.Input);
+ if (deviceDirection == direction || deviceDirection == DeviceDirection.InputOutput)
+ {
+ PaHostApiInfo paHostApiInfo = PortAudioAPI.Pa_GetHostApiInfo(paDeviceInfo.hostApi);
+ AudioDevice audioDevice = new AudioDevice();
+ audioDevice.Name = paDeviceInfo.name;
+ audioDevice.Host = paHostApiInfo.name;
+ audioDevice.Index = i;
+ audioDevice.Direction = deviceDirection;
+ audioDevice.IsDefault = (i == num || i == num2);
+ list.Add(audioDevice);
+ }
+ }
+ return list;
+ }
+
+ public override string ToString()
+ {
+ return "[" + this.Host + "] " + this.Name;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio.PortAudio/DeviceDirection.cs b/SDRSharp.Radio/SDRSharp.Radio.PortAudio/DeviceDirection.cs
new file mode 100644
index 0000000..04575fa
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio.PortAudio/DeviceDirection.cs
@@ -0,0 +1,9 @@
+namespace SDRSharp.Radio.PortAudio
+{
+ public enum DeviceDirection
+ {
+ Input,
+ Output,
+ InputOutput
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio.PortAudio/Int24.cs b/SDRSharp.Radio/SDRSharp.Radio.PortAudio/Int24.cs
new file mode 100644
index 0000000..7ce3951
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio.PortAudio/Int24.cs
@@ -0,0 +1,16 @@
+namespace SDRSharp.Radio.PortAudio
+{
+ public struct Int24
+ {
+ public byte C;
+
+ public byte B;
+
+ public sbyte A;
+
+ public static implicit operator float(Int24 i)
+ {
+ return (float)((i.C << 8 | i.B << 16 | i.A << 24) >> 8);
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio.PortAudio/WaveDuplex.cs b/SDRSharp.Radio/SDRSharp.Radio.PortAudio/WaveDuplex.cs
new file mode 100644
index 0000000..227cfd6
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio.PortAudio/WaveDuplex.cs
@@ -0,0 +1,83 @@
+using PortAudioSharp;
+using System;
+using System.Runtime.InteropServices;
+
+namespace SDRSharp.Radio.PortAudio
+{
+ public class WaveDuplex : IDisposable
+ {
+ private IntPtr _streamHandle;
+
+ private GCHandle _gcHandle;
+
+ private readonly AudioBufferAvailableDelegate _bufferAvailable;
+
+ private unsafe readonly PaStreamCallbackDelegate _paCallback = WaveDuplex.PaStreamCallback;
+
+ public unsafe WaveDuplex(int deviceIndex, double sampleRate, int framesPerBuffer, AudioBufferAvailableDelegate bufferNeededDelegate)
+ {
+ this._bufferAvailable = bufferNeededDelegate;
+ PaStreamParameters paStreamParameters = new PaStreamParameters
+ {
+ device = deviceIndex,
+ channelCount = 2,
+ suggestedLatency = 0.0,
+ sampleFormat = PaSampleFormat.PaFloat32
+ };
+ PaError paError = PortAudioAPI.Pa_IsFormatSupported(ref paStreamParameters, ref paStreamParameters, sampleRate);
+ if (paError != 0)
+ {
+ throw new ApplicationException(paError.ToString());
+ }
+ this._gcHandle = GCHandle.Alloc(this);
+ paError = PortAudioAPI.Pa_OpenStream(out this._streamHandle, ref paStreamParameters, ref paStreamParameters, sampleRate, (uint)framesPerBuffer, PaStreamFlags.PaNoFlag, this._paCallback, (IntPtr)this._gcHandle);
+ if (paError != 0)
+ {
+ this._gcHandle.Free();
+ throw new ApplicationException(paError.ToString());
+ }
+ paError = PortAudioAPI.Pa_StartStream(this._streamHandle);
+ if (paError == PaError.paNoError)
+ {
+ return;
+ }
+ PortAudioAPI.Pa_CloseStream(this._streamHandle);
+ this._gcHandle.Free();
+ throw new ApplicationException(paError.ToString());
+ }
+
+ private unsafe static PaStreamCallbackResult PaStreamCallback(float* input, float* output, uint frameCount, ref PaStreamCallbackTimeInfo timeInfo, PaStreamCallbackFlags statusFlags, IntPtr userData)
+ {
+ GCHandle gCHandle = GCHandle.FromIntPtr(userData);
+ if (!gCHandle.IsAllocated)
+ {
+ return PaStreamCallbackResult.PaAbort;
+ }
+ WaveDuplex waveDuplex = (WaveDuplex)gCHandle.Target;
+ try
+ {
+ Utils.Memcpy(output, input, (int)(frameCount * 2 * 4));
+ if (waveDuplex._bufferAvailable != null)
+ {
+ waveDuplex._bufferAvailable(output, (int)frameCount);
+ }
+ }
+ catch
+ {
+ return PaStreamCallbackResult.PaAbort;
+ }
+ return PaStreamCallbackResult.PaContinue;
+ }
+
+ public void Dispose()
+ {
+ if (this._streamHandle != IntPtr.Zero)
+ {
+ PortAudioAPI.Pa_StopStream(this._streamHandle);
+ PortAudioAPI.Pa_CloseStream(this._streamHandle);
+ this._streamHandle = IntPtr.Zero;
+ }
+ this._gcHandle.Free();
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio.PortAudio/WaveFile.cs b/SDRSharp.Radio/SDRSharp.Radio.PortAudio/WaveFile.cs
new file mode 100644
index 0000000..acb668d
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio.PortAudio/WaveFile.cs
@@ -0,0 +1,298 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace SDRSharp.Radio.PortAudio
+{
+ public sealed class WaveFile : IDisposable
+ {
+ private unsafe static readonly float* _lutu8;
+
+ private static readonly UnsafeBuffer _lutu8Buffer;
+
+ private unsafe static readonly float* _lut16;
+
+ private static readonly UnsafeBuffer _lut16Buffer;
+
+ private readonly Stream _stream;
+
+ private bool _isPCM;
+
+ private long _dataPos;
+
+ private short _formatTag;
+
+ private int _sampleRate;
+
+ private int _avgBytesPerSec;
+
+ private int _length;
+
+ private short _blockAlign;
+
+ private short _bitsPerSample;
+
+ private UnsafeBuffer _tempBuffer;
+
+ private byte[] _temp;
+
+ private unsafe byte* _tempPtr;
+
+ public long Position
+ {
+ get
+ {
+ return this._stream.Position - this._dataPos;
+ }
+ set
+ {
+ this._stream.Seek(value + this._dataPos, SeekOrigin.Begin);
+ }
+ }
+
+ public short FormatTag
+ {
+ get
+ {
+ return this._formatTag;
+ }
+ }
+
+ public int SampleRate
+ {
+ get
+ {
+ return this._sampleRate;
+ }
+ }
+
+ public int AvgBytesPerSec
+ {
+ get
+ {
+ return this._avgBytesPerSec;
+ }
+ }
+
+ public short BlockAlign
+ {
+ get
+ {
+ return this._blockAlign;
+ }
+ }
+
+ public short BitsPerSample
+ {
+ get
+ {
+ return this._bitsPerSample;
+ }
+ }
+
+ public int Length
+ {
+ get
+ {
+ return this._length;
+ }
+ }
+
+ unsafe static WaveFile()
+ {
+ WaveFile._lutu8Buffer = UnsafeBuffer.Create(256, 4);
+ WaveFile._lut16Buffer = UnsafeBuffer.Create(65536, 4);
+ WaveFile._lutu8 = (float*)(void*)WaveFile._lutu8Buffer;
+ for (int i = 0; i < 256; i++)
+ {
+ WaveFile._lutu8[i] = (float)(i - 128) * 0.007874016f;
+ }
+ WaveFile._lut16 = (float*)(void*)WaveFile._lut16Buffer;
+ for (int j = 0; j < 65536; j++)
+ {
+ WaveFile._lut16[j] = (float)(j - 32768) * 3.051851E-05f;
+ }
+ }
+
+ ~WaveFile()
+ {
+ this.Dispose();
+ }
+
+ public WaveFile(string fileName)
+ {
+ this._stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
+ this.ReadHeader();
+ }
+
+ public void Dispose()
+ {
+ this.Close();
+ GC.SuppressFinalize(this);
+ }
+
+ public void Close()
+ {
+ if (this._stream != null)
+ {
+ this._stream.Close();
+ }
+ }
+
+ private static string ReadChunk(BinaryReader reader)
+ {
+ byte[] array = new byte[4];
+ reader.Read(array, 0, array.Length);
+ return Encoding.ASCII.GetString(array);
+ }
+
+ private void ReadHeader()
+ {
+ BinaryReader binaryReader = new BinaryReader(this._stream);
+ if (WaveFile.ReadChunk(binaryReader) != "RIFF")
+ {
+ throw new Exception("Invalid file format");
+ }
+ binaryReader.ReadInt32();
+ if (WaveFile.ReadChunk(binaryReader) != "WAVE")
+ {
+ throw new Exception("Invalid file format");
+ }
+ if (WaveFile.ReadChunk(binaryReader) != "fmt ")
+ {
+ throw new Exception("Invalid file format");
+ }
+ int num = binaryReader.ReadInt32();
+ if (num < 16)
+ {
+ throw new Exception("Invalid file format");
+ }
+ this._formatTag = binaryReader.ReadInt16();
+ this._isPCM = (this._formatTag == 1);
+ if (binaryReader.ReadInt16() != 2)
+ {
+ throw new Exception("Invalid file format");
+ }
+ this._sampleRate = binaryReader.ReadInt32();
+ this._avgBytesPerSec = binaryReader.ReadInt32();
+ this._blockAlign = binaryReader.ReadInt16();
+ this._bitsPerSample = binaryReader.ReadInt16();
+ for (num -= 16; num > 0; num--)
+ {
+ binaryReader.ReadByte();
+ }
+ while (this._stream.Position < this._stream.Length && WaveFile.ReadChunk(binaryReader) != "data")
+ {
+ num = binaryReader.ReadInt32();
+ while (this._stream.Position < this._stream.Length && num > 0)
+ {
+ binaryReader.ReadByte();
+ num--;
+ }
+ }
+ if (this._stream.Position >= this._stream.Length)
+ {
+ throw new Exception("Invalid file format");
+ }
+ this._length = binaryReader.ReadInt32();
+ this._dataPos = this._stream.Position;
+ }
+
+ public unsafe void Read(Complex* iqBuffer, int length)
+ {
+ if (this._temp == null || this._temp.Length != this._blockAlign * length)
+ {
+ this._temp = new byte[this._blockAlign * length];
+ this._tempBuffer = UnsafeBuffer.Create(this._temp);
+ this._tempPtr = (byte*)(void*)this._tempBuffer;
+ }
+ int i = 0;
+ int num2;
+ for (int length2 = this._tempBuffer.Length; i < length2; i += num2)
+ {
+ int num = length2 - i;
+ num2 = this._stream.Read(this._temp, i, num);
+ if (num2 < num)
+ {
+ this._stream.Position = this._dataPos;
+ }
+ }
+ this.FillIQ(iqBuffer, length);
+ }
+
+ private unsafe void FillIQ(Complex* iqPtr, int length)
+ {
+ if (this._isPCM)
+ {
+ if (this._blockAlign == 6)
+ {
+ Int24* ptr = (Int24*)this._tempPtr;
+ for (int i = 0; i < length; i++)
+ {
+ Complex* intPtr = iqPtr;
+ Int24* intPtr2 = ptr;
+ ptr = intPtr2 + 1;
+ intPtr->Real = (float)(*intPtr2) * 1.192093E-07f;
+ Complex* intPtr3 = iqPtr;
+ Int24* intPtr4 = ptr;
+ ptr = intPtr4 + 1;
+ intPtr3->Imag = (float)(*intPtr4) * 1.192093E-07f;
+ iqPtr++;
+ }
+ }
+ else if (this._blockAlign == 4)
+ {
+ short* ptr2 = (short*)this._tempPtr;
+ for (int j = 0; j < length; j++)
+ {
+ Complex* intPtr5 = iqPtr;
+ float* lut = WaveFile._lut16;
+ short* intPtr6 = ptr2;
+ ptr2 = intPtr6 + 1;
+ intPtr5->Real = lut[*intPtr6 + 32768];
+ Complex* intPtr7 = iqPtr;
+ float* lut2 = WaveFile._lut16;
+ short* intPtr8 = ptr2;
+ ptr2 = intPtr8 + 1;
+ intPtr7->Imag = lut2[*intPtr8 + 32768];
+ iqPtr++;
+ }
+ }
+ else if (this._blockAlign == 2)
+ {
+ byte* ptr3 = this._tempPtr;
+ for (int k = 0; k < length; k++)
+ {
+ Complex* intPtr9 = iqPtr;
+ float* lutu = WaveFile._lutu8;
+ byte* intPtr10 = ptr3;
+ ptr3 = intPtr10 + 1;
+ intPtr9->Real = lutu[(int)(*intPtr10)];
+ Complex* intPtr11 = iqPtr;
+ float* lutu2 = WaveFile._lutu8;
+ byte* intPtr12 = ptr3;
+ ptr3 = intPtr12 + 1;
+ intPtr11->Imag = lutu2[(int)(*intPtr12)];
+ iqPtr++;
+ }
+ }
+ }
+ else
+ {
+ float* ptr4 = (float*)this._tempPtr;
+ for (int l = 0; l < length; l++)
+ {
+ Complex* intPtr13 = iqPtr;
+ float* intPtr14 = ptr4;
+ ptr4 = intPtr14 + 1;
+ intPtr13->Real = *intPtr14;
+ Complex* intPtr15 = iqPtr;
+ float* intPtr16 = ptr4;
+ ptr4 = intPtr16 + 1;
+ intPtr15->Imag = *intPtr16;
+ iqPtr++;
+ }
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio.PortAudio/WavePlayer.cs b/SDRSharp.Radio/SDRSharp.Radio.PortAudio/WavePlayer.cs
new file mode 100644
index 0000000..8336e74
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio.PortAudio/WavePlayer.cs
@@ -0,0 +1,82 @@
+using PortAudioSharp;
+using System;
+using System.Runtime.InteropServices;
+
+namespace SDRSharp.Radio.PortAudio
+{
+ public class WavePlayer : IDisposable
+ {
+ private IntPtr _streamHandle;
+
+ private GCHandle _gcHandle;
+
+ private readonly AudioBufferNeededDelegate _bufferNeeded;
+
+ private unsafe readonly PaStreamCallbackDelegate _paCallback = WavePlayer.PaStreamCallback;
+
+ public unsafe WavePlayer(int deviceIndex, double sampleRate, int framesPerBuffer, AudioBufferNeededDelegate bufferNeededDelegate)
+ {
+ this._bufferNeeded = bufferNeededDelegate;
+ PaStreamParameters paStreamParameters = new PaStreamParameters
+ {
+ device = deviceIndex,
+ channelCount = 2,
+ suggestedLatency = 0.0,
+ sampleFormat = PaSampleFormat.PaFloat32
+ };
+ PaError paError = PortAudioAPI.Pa_IsFormatSupported(IntPtr.Zero, ref paStreamParameters, sampleRate);
+ if (paError != 0)
+ {
+ throw new ApplicationException(paError.ToString());
+ }
+ this._gcHandle = GCHandle.Alloc(this);
+ paError = PortAudioAPI.Pa_OpenStream(out this._streamHandle, IntPtr.Zero, ref paStreamParameters, sampleRate, (uint)framesPerBuffer, PaStreamFlags.PaNoFlag, this._paCallback, (IntPtr)this._gcHandle);
+ if (paError != 0)
+ {
+ this._gcHandle.Free();
+ throw new ApplicationException(paError.ToString());
+ }
+ paError = PortAudioAPI.Pa_StartStream(this._streamHandle);
+ if (paError == PaError.paNoError)
+ {
+ return;
+ }
+ PortAudioAPI.Pa_CloseStream(this._streamHandle);
+ this._gcHandle.Free();
+ throw new ApplicationException(paError.ToString());
+ }
+
+ private unsafe static PaStreamCallbackResult PaStreamCallback(float* input, float* output, uint frameCount, ref PaStreamCallbackTimeInfo timeInfo, PaStreamCallbackFlags statusFlags, IntPtr userData)
+ {
+ GCHandle gCHandle = GCHandle.FromIntPtr(userData);
+ if (!gCHandle.IsAllocated)
+ {
+ return PaStreamCallbackResult.PaAbort;
+ }
+ WavePlayer wavePlayer = (WavePlayer)gCHandle.Target;
+ try
+ {
+ if (wavePlayer._bufferNeeded != null)
+ {
+ wavePlayer._bufferNeeded(output, (int)frameCount);
+ }
+ }
+ catch
+ {
+ return PaStreamCallbackResult.PaAbort;
+ }
+ return PaStreamCallbackResult.PaContinue;
+ }
+
+ public void Dispose()
+ {
+ if (this._streamHandle != IntPtr.Zero)
+ {
+ PortAudioAPI.Pa_StopStream(this._streamHandle);
+ PortAudioAPI.Pa_CloseStream(this._streamHandle);
+ this._streamHandle = IntPtr.Zero;
+ }
+ this._gcHandle.Free();
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio.PortAudio/WaveRecorder.cs b/SDRSharp.Radio/SDRSharp.Radio.PortAudio/WaveRecorder.cs
new file mode 100644
index 0000000..86de588
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio.PortAudio/WaveRecorder.cs
@@ -0,0 +1,82 @@
+using PortAudioSharp;
+using System;
+using System.Runtime.InteropServices;
+
+namespace SDRSharp.Radio.PortAudio
+{
+ public class WaveRecorder : IDisposable
+ {
+ private IntPtr _streamHandle;
+
+ private GCHandle _gcHandle;
+
+ private readonly AudioBufferAvailableDelegate _bufferAvailable;
+
+ private unsafe readonly PaStreamCallbackDelegate _paCallback = WaveRecorder.PaStreamCallback;
+
+ public unsafe WaveRecorder(int deviceIndex, double sampleRate, int framesPerBuffer, AudioBufferAvailableDelegate bufferAvailable)
+ {
+ this._bufferAvailable = bufferAvailable;
+ PaStreamParameters paStreamParameters = new PaStreamParameters
+ {
+ device = deviceIndex,
+ channelCount = 2,
+ suggestedLatency = 0.0,
+ sampleFormat = PaSampleFormat.PaFloat32
+ };
+ PaError paError = PortAudioAPI.Pa_IsFormatSupported(ref paStreamParameters, IntPtr.Zero, sampleRate);
+ if (paError != 0)
+ {
+ throw new ApplicationException(paError.ToString());
+ }
+ this._gcHandle = GCHandle.Alloc(this);
+ paError = PortAudioAPI.Pa_OpenStream(out this._streamHandle, ref paStreamParameters, IntPtr.Zero, sampleRate, (uint)framesPerBuffer, PaStreamFlags.PaNoFlag, this._paCallback, (IntPtr)this._gcHandle);
+ if (paError != 0)
+ {
+ this._gcHandle.Free();
+ throw new ApplicationException(paError.ToString());
+ }
+ paError = PortAudioAPI.Pa_StartStream(this._streamHandle);
+ if (paError == PaError.paNoError)
+ {
+ return;
+ }
+ PortAudioAPI.Pa_CloseStream(this._streamHandle);
+ this._gcHandle.Free();
+ throw new ApplicationException(paError.ToString());
+ }
+
+ private unsafe static PaStreamCallbackResult PaStreamCallback(float* input, float* output, uint frameCount, ref PaStreamCallbackTimeInfo timeInfo, PaStreamCallbackFlags statusFlags, IntPtr userData)
+ {
+ GCHandle gCHandle = GCHandle.FromIntPtr(userData);
+ if (!gCHandle.IsAllocated)
+ {
+ return PaStreamCallbackResult.PaAbort;
+ }
+ WaveRecorder waveRecorder = (WaveRecorder)gCHandle.Target;
+ try
+ {
+ if (waveRecorder._bufferAvailable != null)
+ {
+ waveRecorder._bufferAvailable(input, (int)frameCount);
+ }
+ }
+ catch
+ {
+ return PaStreamCallbackResult.PaAbort;
+ }
+ return PaStreamCallbackResult.PaContinue;
+ }
+
+ public void Dispose()
+ {
+ if (this._streamHandle != IntPtr.Zero)
+ {
+ PortAudioAPI.Pa_StopStream(this._streamHandle);
+ PortAudioAPI.Pa_CloseStream(this._streamHandle);
+ this._streamHandle = IntPtr.Zero;
+ }
+ this._gcHandle.Free();
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio.csproj b/SDRSharp.Radio/SDRSharp.Radio.csproj
new file mode 100644
index 0000000..c5038c9
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio.csproj
@@ -0,0 +1,149 @@
+
+
+
+ {A24DD927-34EF-4301-BDF3-42C872BC944E}
+ Debug
+ AnyCPU
+ Library
+ SDRSharp.Radio
+ .NETFramework
+ v4.6
+ 4
+ True
+
+
+ AnyCPU
+
+
+ bin\Debug\
+ true
+ full
+ false
+
+
+ bin\Release\
+ true
+ pdbonly
+ true
+
+
+
+ C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll
+
+
+ C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089\System.Windows.Forms.dll
+
+
+ C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Drawing\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll
+
+
+ C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll
+
+
+ C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SDRSharp.Radio/SDRSharp.Radio.sln b/SDRSharp.Radio/SDRSharp.Radio.sln
new file mode 100644
index 0000000..1d9e5df
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27428.2015
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SDRSharp.Radio", "SDRSharp.Radio.csproj", "{A24DD927-34EF-4301-BDF3-42C872BC944E}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A24DD927-34EF-4301-BDF3-42C872BC944E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A24DD927-34EF-4301-BDF3-42C872BC944E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A24DD927-34EF-4301-BDF3-42C872BC944E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A24DD927-34EF-4301-BDF3-42C872BC944E}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {6BA50BE1-BD57-44A9-8AD3-3F881F01F86F}
+ EndGlobalSection
+EndGlobal
diff --git a/SDRSharp.Radio/SDRSharp.Radio/AmAntiFading.cs b/SDRSharp.Radio/SDRSharp.Radio/AmAntiFading.cs
new file mode 100644
index 0000000..a6332af
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/AmAntiFading.cs
@@ -0,0 +1,29 @@
+namespace SDRSharp.Radio
+{
+ public class AmAntiFading : OverlapAddProcessor
+ {
+ public AmAntiFading()
+ : base(4096)
+ {
+ }
+
+ protected unsafe override void ProcessFft(Complex* buffer, int length)
+ {
+ for (int i = 1; i < length / 2 - 1; i++)
+ {
+ int num = i;
+ int num2 = length - i;
+ float num3 = buffer[num].ModulusSquared();
+ float num4 = buffer[num2].ModulusSquared();
+ if (num3 > num4)
+ {
+ buffer[num2] = buffer[num].Conjugate();
+ }
+ else
+ {
+ buffer[num] = buffer[num2].Conjugate();
+ }
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/AmDetector.cs b/SDRSharp.Radio/SDRSharp.Radio/AmDetector.cs
new file mode 100644
index 0000000..e9a5bc0
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/AmDetector.cs
@@ -0,0 +1,62 @@
+using System;
+
+namespace SDRSharp.Radio
+{
+ public sealed class AmDetector
+ {
+ private float _avg;
+
+ private float _powerThreshold;
+
+ private int _squelchThreshold;
+
+ private bool _isSquelchOpen;
+
+ public int SquelchThreshold
+ {
+ get
+ {
+ return this._squelchThreshold;
+ }
+ set
+ {
+ if (this._squelchThreshold != value)
+ {
+ this._squelchThreshold = value;
+ this._powerThreshold = ((float)this._squelchThreshold / 100f - 1f) * 100f - 50f;
+ }
+ }
+ }
+
+ public bool IsSquelchOpen
+ {
+ get
+ {
+ return this._isSquelchOpen;
+ }
+ }
+
+ public unsafe void Demodulate(Complex* iq, float* audio, int length)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ float num = iq[i].Modulus();
+ if (this._squelchThreshold == 0)
+ {
+ audio[i] = num;
+ }
+ else
+ {
+ float num2 = (float)(20.0 * Math.Log10(1E-60 + (double)num));
+ this._avg = 0.99f * this._avg + 0.01f * num2;
+ this._isSquelchOpen = (this._avg > this._powerThreshold);
+ audio[i] = num;
+ if (!this._isSquelchOpen)
+ {
+ audio[i] *= 1E-15f;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/AutomaticGainControl.cs b/SDRSharp.Radio/SDRSharp.Radio/AutomaticGainControl.cs
new file mode 100644
index 0000000..c1bac24
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/AutomaticGainControl.cs
@@ -0,0 +1,287 @@
+using System;
+
+namespace SDRSharp.Radio
+{
+ public sealed class AutomaticGainControl
+ {
+ private const float DelayTimeconst = 0.015f;
+
+ private const float WindowTimeconst = 0.018f;
+
+ private const float AttackRiseTimeconst = 0.002f;
+
+ private const float AttackFallTimeconst = 0.005f;
+
+ private const float DecayRisefallRatio = 0.3f;
+
+ private const float ReleaseTimeconst = 0.05f;
+
+ private const float AGCOutscale = 0.7f;
+
+ private UnsafeBuffer _sigDelayBuf;
+
+ private unsafe float* _sigDelayBufPtr;
+
+ private UnsafeBuffer _magBuf;
+
+ private unsafe float* _magBufPtr;
+
+ private float _decayAve;
+
+ private float _attackAve;
+
+ private float _attackRiseAlpha;
+
+ private float _attackFallAlpha;
+
+ private float _decayRiseAlpha;
+
+ private float _decayFallAlpha;
+
+ private float _fixedGain;
+
+ private float _knee;
+
+ private float _gainSlope;
+
+ private float _peak;
+
+ private int _sigDelayPtr;
+
+ private int _magBufPos;
+
+ private int _delaySamples;
+
+ private int _windowSamples;
+
+ private int _hangTime;
+
+ private int _hangTimer;
+
+ private float _threshold;
+
+ private float _slopeFactor;
+
+ private float _decay;
+
+ private double _sampleRate;
+
+ private bool _useHang;
+
+ public double SampleRate
+ {
+ get
+ {
+ return this._sampleRate;
+ }
+ set
+ {
+ if (this._sampleRate != value)
+ {
+ this._sampleRate = value;
+ this.Configure(true);
+ }
+ }
+ }
+
+ public bool UseHang
+ {
+ get
+ {
+ return this._useHang;
+ }
+ set
+ {
+ if (this._useHang != value)
+ {
+ this._useHang = value;
+ this.Configure(false);
+ }
+ }
+ }
+
+ public float Threshold
+ {
+ get
+ {
+ return this._threshold;
+ }
+ set
+ {
+ if (this._threshold != value)
+ {
+ this._threshold = value;
+ this.Configure(false);
+ }
+ }
+ }
+
+ public float Slope
+ {
+ get
+ {
+ return this._slopeFactor;
+ }
+ set
+ {
+ if (this._slopeFactor != value)
+ {
+ this._slopeFactor = value;
+ this.Configure(false);
+ }
+ }
+ }
+
+ public float Decay
+ {
+ get
+ {
+ return this._decay;
+ }
+ set
+ {
+ if (this._decay != value)
+ {
+ this._decay = value;
+ this.Configure(false);
+ }
+ }
+ }
+
+ private unsafe void Configure(bool resetBuffers)
+ {
+ if (resetBuffers)
+ {
+ this._sigDelayPtr = 0;
+ this._hangTimer = 0;
+ this._peak = -16f;
+ this._decayAve = -5f;
+ this._attackAve = -5f;
+ this._magBufPos = 0;
+ if (this._sampleRate > 0.0)
+ {
+ this._delaySamples = (int)(this._sampleRate * 0.014999999664723873);
+ this._windowSamples = (int)(this._sampleRate * 0.017999999225139618);
+ this._sigDelayBuf = UnsafeBuffer.Create(this._delaySamples, 4);
+ this._sigDelayBufPtr = (float*)(void*)this._sigDelayBuf;
+ this._magBuf = UnsafeBuffer.Create(this._windowSamples, 4);
+ this._magBufPtr = (float*)(void*)this._magBuf;
+ for (int i = 0; i < this._windowSamples; i++)
+ {
+ this._magBufPtr[i] = -16f;
+ }
+ if (this._delaySamples >= this._sigDelayBuf.Length - 1)
+ {
+ this._delaySamples = this._sigDelayBuf.Length - 1;
+ }
+ }
+ }
+ this._knee = this._threshold / 20f;
+ this._gainSlope = this._slopeFactor / 100f;
+ this._fixedGain = 0.7f * (float)Math.Pow(10.0, (double)this._knee * ((double)this._gainSlope - 1.0));
+ this._attackRiseAlpha = 1f - (float)Math.Exp(-1.0 / (this._sampleRate * 0.0020000000949949026));
+ this._attackFallAlpha = 1f - (float)Math.Exp(-1.0 / (this._sampleRate * 0.004999999888241291));
+ this._decayRiseAlpha = 1f - (float)Math.Exp(-1.0 / (this._sampleRate * (double)this.Decay * 0.001 * 0.30000001192092896));
+ this._hangTime = (int)(this._sampleRate * (double)this.Decay * 0.001);
+ if (this._useHang)
+ {
+ this._decayFallAlpha = 1f - (float)Math.Exp(-1.0 / (this._sampleRate * 0.05000000074505806));
+ }
+ else
+ {
+ this._decayFallAlpha = 1f - (float)Math.Exp(-1.0 / (this._sampleRate * (double)this.Decay * 0.001));
+ }
+ }
+
+ public unsafe void Process(float* buffer, int length)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ float num = buffer[i];
+ if ((double)num != 0.0)
+ {
+ num *= 1000f;
+ float num2 = this._sigDelayBufPtr[this._sigDelayPtr];
+ this._sigDelayBufPtr[this._sigDelayPtr++] = num;
+ if (this._sigDelayPtr >= this._delaySamples)
+ {
+ this._sigDelayPtr = 0;
+ }
+ float num3 = (float)Math.Log10((double)Math.Abs(num));
+ if (float.IsNaN(num3) || float.IsInfinity(num3))
+ {
+ num3 = -8f;
+ }
+ float num4 = this._magBufPtr[this._magBufPos];
+ this._magBufPtr[this._magBufPos++] = num3;
+ if (this._magBufPos >= this._windowSamples)
+ {
+ this._magBufPos = 0;
+ }
+ if (num3 > this._peak)
+ {
+ this._peak = num3;
+ }
+ else if (num4 == this._peak)
+ {
+ this._peak = -8f;
+ for (int j = 0; j < this._windowSamples; j++)
+ {
+ num4 = this._magBufPtr[j];
+ if (num4 > this._peak)
+ {
+ this._peak = num4;
+ }
+ }
+ }
+ if (this.UseHang)
+ {
+ if (this._peak > this._attackAve)
+ {
+ this._attackAve = (1f - this._attackRiseAlpha) * this._attackAve + this._attackRiseAlpha * this._peak;
+ }
+ else
+ {
+ this._attackAve = (1f - this._attackFallAlpha) * this._attackAve + this._attackFallAlpha * this._peak;
+ }
+ if (this._peak > this._decayAve)
+ {
+ this._decayAve = (1f - this._decayRiseAlpha) * this._decayAve + this._decayRiseAlpha * this._peak;
+ this._hangTimer = 0;
+ }
+ else if (this._hangTimer < this._hangTime)
+ {
+ this._hangTimer++;
+ }
+ else
+ {
+ this._decayAve = (1f - this._decayFallAlpha) * this._decayAve + this._decayFallAlpha * this._peak;
+ }
+ }
+ else
+ {
+ if (this._peak > this._attackAve)
+ {
+ this._attackAve = (1f - this._attackRiseAlpha) * this._attackAve + this._attackRiseAlpha * this._peak;
+ }
+ else
+ {
+ this._attackAve = (1f - this._attackFallAlpha) * this._attackAve + this._attackFallAlpha * this._peak;
+ }
+ if (this._peak > this._decayAve)
+ {
+ this._decayAve = (1f - this._decayRiseAlpha) * this._decayAve + this._decayRiseAlpha * this._peak;
+ }
+ else
+ {
+ this._decayAve = (1f - this._decayFallAlpha) * this._decayAve + this._decayFallAlpha * this._peak;
+ }
+ }
+ num3 = ((this._attackAve > this._decayAve) ? this._attackAve : this._decayAve);
+ float num5 = (!(num3 <= this._knee)) ? (0.7f * (float)Math.Pow(10.0, (double)num3 * ((double)this._gainSlope - 1.0))) : this._fixedGain;
+ buffer[i] = num2 * num5 * 1E-05f;
+ }
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/BlockMode.cs b/SDRSharp.Radio/SDRSharp.Radio/BlockMode.cs
new file mode 100644
index 0000000..5442bce
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/BlockMode.cs
@@ -0,0 +1,10 @@
+namespace SDRSharp.Radio
+{
+ public enum BlockMode
+ {
+ None,
+ BlockingRead,
+ BlockingWrite,
+ BlockingReadWrite
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/BufferNeededDelegate.cs b/SDRSharp.Radio/SDRSharp.Radio/BufferNeededDelegate.cs
new file mode 100644
index 0000000..636fa33
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/BufferNeededDelegate.cs
@@ -0,0 +1,4 @@
+namespace SDRSharp.Radio
+{
+ public unsafe delegate void BufferNeededDelegate(Complex* iqBuffer, float* audioBuffer, int length);
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/CarrierLocker.cs b/SDRSharp.Radio/SDRSharp.Radio/CarrierLocker.cs
new file mode 100644
index 0000000..012931e
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/CarrierLocker.cs
@@ -0,0 +1,120 @@
+using System;
+
+namespace SDRSharp.Radio
+{
+ public class CarrierLocker
+ {
+ private const int PllRange = 2000;
+
+ private const int PllBandwith = 10;
+
+ private const float PllThreshold = 2f;
+
+ private const float PllLockTime = 0.5f;
+
+ private const float PllResumeDelay = 0.5f;
+
+ private const float PllZeta = 1.5f;
+
+ private const float PllPhaseAdjM = 0f;
+
+ private const float PllPhaseAdjB = 0f;
+
+ private const float TimeConst = 0.003f;
+
+ private Pll _pll;
+
+ private float _iavg;
+
+ private float _qavg;
+
+ private float _alpha;
+
+ private int _unlockedCount;
+
+ private int _maxUnlockedTicks;
+
+ private bool _resetNeeded;
+
+ private double _sampleRate;
+
+ public double SampleRate
+ {
+ get
+ {
+ return this._sampleRate;
+ }
+ set
+ {
+ if (value != this._sampleRate)
+ {
+ this._sampleRate = value;
+ this.Configure();
+ }
+ }
+ }
+
+ public bool IsLocked
+ {
+ get
+ {
+ return this._pll.IsLocked;
+ }
+ }
+
+ private void Configure()
+ {
+ this._pll.SampleRate = (float)this._sampleRate;
+ this._pll.DefaultFrequency = 0f;
+ this._pll.Range = 2000f;
+ this._pll.Bandwidth = 10f;
+ this._pll.Zeta = 1.5f;
+ this._pll.PhaseAdjM = 0f;
+ this._pll.PhaseAdjB = 0f;
+ this._pll.LockTime = 0.5f;
+ this._pll.LockThreshold = 2f;
+ this._alpha = (float)(1.0 - Math.Exp(-1.0 / (this._sampleRate * 0.0030000000260770321)));
+ this._maxUnlockedTicks = (int)(this._sampleRate * 0.5);
+ }
+
+ public void Reset()
+ {
+ this._resetNeeded = true;
+ }
+
+ public unsafe void Process(Complex* buffer, int length)
+ {
+ if (this._resetNeeded)
+ {
+ this._pll.Reset();
+ this._resetNeeded = false;
+ }
+ for (int i = 0; i < length; i++)
+ {
+ Complex b = Complex.FromAngleFast(this._pll.Phase);
+ Complex* intPtr = buffer + i;
+ *intPtr *= b;
+ Complex complex = buffer[i];
+ if (this._pll.StickOnFrequencyIfNotLocked || this._pll.IsLocked)
+ {
+ this._iavg += this._alpha * (complex.Real - this._iavg);
+ this._qavg += this._alpha * (complex.Imag - this._qavg);
+ complex.Real = this._iavg;
+ complex.Imag = this._qavg;
+ this._pll.StickOnFrequencyIfNotLocked = true;
+ if (this._pll.IsLocked)
+ {
+ this._unlockedCount = 0;
+ }
+ else if (++this._unlockedCount > this._maxUnlockedTicks)
+ {
+ this._pll.StickOnFrequencyIfNotLocked = false;
+ this._unlockedCount = 0;
+ }
+ }
+ complex *= b.Conjugate();
+ this._pll.Process(complex);
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/CircularBuffer.cs b/SDRSharp.Radio/SDRSharp.Radio/CircularBuffer.cs
new file mode 100644
index 0000000..adb7fde
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/CircularBuffer.cs
@@ -0,0 +1,169 @@
+using System;
+using System.Collections.Generic;
+
+namespace SDRSharp.Radio
+{
+ public class CircularBuffer : IDisposable
+ {
+ private readonly int _bufferSize;
+
+ private readonly int _elementSize;
+
+ private readonly int _maxBufferCount;
+
+ private readonly SharpEvent _readEvent = new SharpEvent(false);
+
+ private readonly SharpEvent _writeEvent = new SharpEvent(false);
+
+ private int _count;
+
+ private int _len;
+
+ private int _head;
+
+ private int _tail;
+
+ private bool _closed;
+
+ private List _buffers = new List();
+
+ public int BufferSize
+ {
+ get
+ {
+ return this._bufferSize;
+ }
+ }
+
+ public int BufferCount
+ {
+ get
+ {
+ return this._maxBufferCount;
+ }
+ }
+
+ public int AvailableCount
+ {
+ get
+ {
+ return this._count;
+ }
+ }
+
+ protected CircularBuffer(int bufferSize, int elementSize, int maxBufferCount)
+ {
+ this._bufferSize = bufferSize;
+ this._elementSize = elementSize;
+ this._maxBufferCount = maxBufferCount;
+ for (int i = 0; i < this._maxBufferCount; i++)
+ {
+ this._buffers.Add(UnsafeBuffer.Create(this._bufferSize, this._elementSize));
+ }
+ }
+
+ ~CircularBuffer()
+ {
+ this.Dispose();
+ }
+
+ public void Dispose()
+ {
+ this.Close();
+ GC.SuppressFinalize(this);
+ }
+
+ protected unsafe bool Write(byte* buffer, int len, bool block)
+ {
+ int num = len;
+ while (num > 0 && !this._closed)
+ {
+ if (block)
+ {
+ while (this._count >= this._maxBufferCount && !this._closed)
+ {
+ this._writeEvent.WaitOne();
+ }
+ }
+ else if (this._count >= this._maxBufferCount)
+ {
+ return false;
+ }
+ if (this._closed)
+ {
+ return false;
+ }
+ byte* ptr = (byte*)(void*)this._buffers[this._head];
+ int num2 = Math.Min(this._bufferSize - this._len, num);
+ int num3 = num2 * this._elementSize;
+ Utils.Memcpy(ptr + this._len * this._elementSize, buffer, num3);
+ buffer += num3;
+ this._len += num2;
+ num -= num2;
+ if (this._len == this._bufferSize)
+ {
+ this._len = 0;
+ this._head = (this._head + 1) % this._maxBufferCount;
+ lock (this)
+ {
+ this._count++;
+ this._readEvent.Set();
+ }
+ }
+ }
+ return true;
+ }
+
+ protected unsafe void* AcquireRawBuffer(bool block)
+ {
+ if (this._closed)
+ {
+ return null;
+ }
+ if (block)
+ {
+ while (this._count == 0 && !this._closed)
+ {
+ this._readEvent.WaitOne();
+ }
+ }
+ if (!this._closed && this._count != 0)
+ {
+ return this._buffers[this._tail];
+ }
+ return null;
+ }
+
+ public void Release()
+ {
+ if (!this._closed && this._count != 0)
+ {
+ this._tail = (this._tail + 1) % this._maxBufferCount;
+ lock (this)
+ {
+ this._count--;
+ this._writeEvent.Set();
+ }
+ }
+ }
+
+ public void Close()
+ {
+ this._closed = true;
+ this._readEvent.Set();
+ this._writeEvent.Set();
+ this._head = 0;
+ this._tail = 0;
+ this._count = 0;
+ this._len = 0;
+ lock (this)
+ {
+ foreach (UnsafeBuffer buffer in this._buffers)
+ {
+ buffer.Dispose();
+ }
+ this._buffers.Clear();
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/Complex.cs b/SDRSharp.Radio/SDRSharp.Radio/Complex.cs
new file mode 100644
index 0000000..5b44fe1
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/Complex.cs
@@ -0,0 +1,183 @@
+using System;
+
+namespace SDRSharp.Radio
+{
+ public struct Complex
+ {
+ public float Real;
+
+ public float Imag;
+
+ public Complex(float real, float imaginary)
+ {
+ this.Real = real;
+ this.Imag = imaginary;
+ }
+
+ public Complex(Complex c)
+ {
+ this.Real = c.Real;
+ this.Imag = c.Imag;
+ }
+
+ public float Modulus()
+ {
+ return (float)Math.Sqrt((double)this.ModulusSquared());
+ }
+
+ public float ModulusSquared()
+ {
+ return this.Real * this.Real + this.Imag * this.Imag;
+ }
+
+ public float Argument()
+ {
+ return (float)Math.Atan2((double)this.Imag, (double)this.Real);
+ }
+
+ public float ArgumentFast()
+ {
+ return Trig.Atan2(this.Imag, this.Real);
+ }
+
+ public Complex Conjugate()
+ {
+ Complex result = default(Complex);
+ result.Real = this.Real;
+ result.Imag = 0f - this.Imag;
+ return result;
+ }
+
+ public Complex Normalize()
+ {
+ float b = 1f / this.Modulus();
+ return this * b;
+ }
+
+ public Complex NormalizeFast()
+ {
+ float b = 1.95f - this.ModulusSquared();
+ return this * b;
+ }
+
+ public override string ToString()
+ {
+ return string.Format("real {0}, imag {1}", this.Real, this.Imag);
+ }
+
+ public static Complex FromAngle(double angle)
+ {
+ Complex result = default(Complex);
+ result.Real = (float)Math.Cos(angle);
+ result.Imag = (float)Math.Sin(angle);
+ return result;
+ }
+
+ public static Complex FromAngleFast(float angle)
+ {
+ return Trig.SinCos(angle);
+ }
+
+ public static bool operator ==(Complex leftHandSide, Complex rightHandSide)
+ {
+ if (leftHandSide.Real != rightHandSide.Real)
+ {
+ return false;
+ }
+ return leftHandSide.Imag == rightHandSide.Imag;
+ }
+
+ public static bool operator !=(Complex leftHandSide, Complex rightHandSide)
+ {
+ if (leftHandSide.Real != rightHandSide.Real)
+ {
+ return true;
+ }
+ return leftHandSide.Imag != rightHandSide.Imag;
+ }
+
+ public static Complex operator +(Complex a, Complex b)
+ {
+ Complex result = default(Complex);
+ result.Real = a.Real + b.Real;
+ result.Imag = a.Imag + b.Imag;
+ return result;
+ }
+
+ public static Complex operator -(Complex a, Complex b)
+ {
+ Complex result = default(Complex);
+ result.Real = a.Real - b.Real;
+ result.Imag = a.Imag - b.Imag;
+ return result;
+ }
+
+ public static Complex operator *(Complex a, Complex b)
+ {
+ Complex result = default(Complex);
+ result.Real = a.Real * b.Real - a.Imag * b.Imag;
+ result.Imag = a.Imag * b.Real + a.Real * b.Imag;
+ return result;
+ }
+
+ public static Complex operator *(Complex a, float b)
+ {
+ Complex result = default(Complex);
+ result.Real = a.Real * b;
+ result.Imag = a.Imag * b;
+ return result;
+ }
+
+ public static Complex operator /(Complex a, Complex b)
+ {
+ float num = b.Real * b.Real + b.Imag * b.Imag;
+ num = 1f / num;
+ Complex result = default(Complex);
+ result.Real = (a.Real * b.Real + a.Imag * b.Imag) * num;
+ result.Imag = (a.Imag * b.Real - a.Real * b.Imag) * num;
+ return result;
+ }
+
+ public static Complex operator /(Complex a, float b)
+ {
+ b = 1f / b;
+ Complex result = default(Complex);
+ result.Real = a.Real * b;
+ result.Imag = a.Imag * b;
+ return result;
+ }
+
+ public static Complex operator ~(Complex a)
+ {
+ return a.Conjugate();
+ }
+
+ public static implicit operator Complex(float d)
+ {
+ return new Complex(d, 0f);
+ }
+
+ public override int GetHashCode()
+ {
+ return this.Real.GetHashCode() * 397 ^ this.Imag.GetHashCode();
+ }
+
+ public bool Equals(Complex obj)
+ {
+ if (obj.Real == this.Real)
+ {
+ return obj.Imag == this.Imag;
+ }
+ return false;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj.GetType() != typeof(Complex))
+ {
+ return false;
+ }
+ return this.Equals((Complex)obj);
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/ComplexCircularBuffer.cs b/SDRSharp.Radio/SDRSharp.Radio/ComplexCircularBuffer.cs
new file mode 100644
index 0000000..3eb41e5
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/ComplexCircularBuffer.cs
@@ -0,0 +1,30 @@
+namespace SDRSharp.Radio
+{
+ public class ComplexCircularBuffer : CircularBuffer
+ {
+ public unsafe ComplexCircularBuffer(int bufferSize, int maxBufferCount)
+ : base(bufferSize, sizeof(Complex), maxBufferCount)
+ {
+ }
+
+ public unsafe bool Write(Complex* buffer, int len, bool block)
+ {
+ return base.Write((byte*)buffer, len, block);
+ }
+
+ public unsafe bool Write(Complex* buffer, int len)
+ {
+ return base.Write((byte*)buffer, len, true);
+ }
+
+ public unsafe Complex* Acquire()
+ {
+ return this.Acquire(true);
+ }
+
+ public unsafe Complex* Acquire(bool block)
+ {
+ return (Complex*)base.AcquireRawBuffer(block);
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/ComplexDecimator.cs b/SDRSharp.Radio/SDRSharp.Radio/ComplexDecimator.cs
new file mode 100644
index 0000000..f9ab3b1
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/ComplexDecimator.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SDRSharp.Radio
+{
+ public sealed class ComplexDecimator : IDisposable
+ {
+ private IntPtr _dec;
+
+ private int _decimationRatio;
+
+ public int DecimationRatio
+ {
+ get
+ {
+ return this._decimationRatio;
+ }
+ }
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr complex_decimator_create(int decimation);
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private static extern void complex_decimator_destroy(IntPtr instance);
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private unsafe static extern int complex_decimator_process(IntPtr instance, Complex* buffer, int length);
+
+ public ComplexDecimator(int decimationRatio)
+ {
+ this._decimationRatio = decimationRatio;
+ this._dec = ComplexDecimator.complex_decimator_create(decimationRatio);
+ }
+
+ public void Dispose()
+ {
+ if (this._dec != IntPtr.Zero)
+ {
+ ComplexDecimator.complex_decimator_destroy(this._dec);
+ this._dec = IntPtr.Zero;
+ }
+ }
+
+ public unsafe int Process(Complex* buffer, int length)
+ {
+ return ComplexDecimator.complex_decimator_process(this._dec, buffer, length);
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/ComplexFifoStream.cs b/SDRSharp.Radio/SDRSharp.Radio/ComplexFifoStream.cs
new file mode 100644
index 0000000..ce8c640
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/ComplexFifoStream.cs
@@ -0,0 +1,285 @@
+using System;
+using System.Collections.Generic;
+
+namespace SDRSharp.Radio
+{
+ public sealed class ComplexFifoStream : IDisposable
+ {
+ private const int BlockSize = 8192;
+
+ private const int MaxBlocksInCache = 512;
+
+ private int _size;
+
+ private int _readPos;
+
+ private int _writePos;
+
+ private bool _terminated;
+
+ private readonly int _maxSize;
+
+ private readonly SharpEvent _writeEvent;
+
+ private readonly SharpEvent _readEvent;
+
+ private readonly Stack _usedBlocks = new Stack();
+
+ private readonly List _blocks = new List();
+
+ public int Length
+ {
+ get
+ {
+ return this._size;
+ }
+ }
+
+ public ComplexFifoStream()
+ : this(BlockMode.None)
+ {
+ }
+
+ public ComplexFifoStream(BlockMode blockMode)
+ : this(blockMode, 0)
+ {
+ }
+
+ public ComplexFifoStream(BlockMode blockMode, int maxSize)
+ {
+ if (blockMode == BlockMode.BlockingRead || blockMode == BlockMode.BlockingReadWrite)
+ {
+ this._readEvent = new SharpEvent(false);
+ }
+ if (blockMode == BlockMode.BlockingWrite || blockMode == BlockMode.BlockingReadWrite)
+ {
+ if (maxSize <= 0)
+ {
+ throw new ArgumentException("MaxSize should be greater than zero when in blocking write mode", "maxSize");
+ }
+ this._writeEvent = new SharpEvent(false);
+ }
+ this._maxSize = maxSize;
+ }
+
+ ~ComplexFifoStream()
+ {
+ this.Dispose();
+ }
+
+ public void Dispose()
+ {
+ this.Close();
+ GC.SuppressFinalize(this);
+ }
+
+ private unsafe UnsafeBuffer AllocBlock()
+ {
+ if (this._usedBlocks.Count <= 0)
+ {
+ return UnsafeBuffer.Create(8192, sizeof(Complex));
+ }
+ return this._usedBlocks.Pop();
+ }
+
+ private void FreeBlock(UnsafeBuffer block)
+ {
+ if (this._usedBlocks.Count < 512)
+ {
+ this._usedBlocks.Push(block);
+ }
+ }
+
+ private UnsafeBuffer GetWBlock()
+ {
+ UnsafeBuffer unsafeBuffer;
+ if (this._writePos < 8192 && this._blocks.Count > 0)
+ {
+ unsafeBuffer = this._blocks[this._blocks.Count - 1];
+ }
+ else
+ {
+ unsafeBuffer = this.AllocBlock();
+ this._blocks.Add(unsafeBuffer);
+ this._writePos = 0;
+ }
+ return unsafeBuffer;
+ }
+
+ public void Open()
+ {
+ this._terminated = false;
+ }
+
+ public void Close()
+ {
+ this._terminated = true;
+ this.Flush();
+ }
+
+ public void Flush()
+ {
+ lock (this)
+ {
+ foreach (UnsafeBuffer block in this._blocks)
+ {
+ this.FreeBlock(block);
+ }
+ this._blocks.Clear();
+ this._readPos = 0;
+ this._writePos = 0;
+ this._size = 0;
+ }
+ if (this._writeEvent != null)
+ {
+ this._writeEvent.Set();
+ }
+ if (this._readEvent != null)
+ {
+ this._readEvent.Set();
+ }
+ }
+
+ public unsafe int Read(Complex* buf, int ofs, int count)
+ {
+ if (this._readEvent != null)
+ {
+ while (this._size == 0 && !this._terminated)
+ {
+ this._readEvent.WaitOne();
+ }
+ if (this._terminated)
+ {
+ return 0;
+ }
+ }
+ int num = default(int);
+ lock (this)
+ {
+ num = this.DoPeek(buf, ofs, count);
+ this.DoAdvance(num);
+ }
+ if (this._writeEvent != null)
+ {
+ this._writeEvent.Set();
+ }
+ return num;
+ }
+
+ public unsafe void Write(Complex* buf, int ofs, int count)
+ {
+ if (this._writeEvent != null)
+ {
+ while (this._size >= this._maxSize && !this._terminated)
+ {
+ this._writeEvent.WaitOne();
+ }
+ if (this._terminated)
+ {
+ return;
+ }
+ }
+ lock (this)
+ {
+ int num2;
+ for (int num = count; num > 0; num -= num2)
+ {
+ num2 = Math.Min(8192 - this._writePos, num);
+ Complex* ptr = (Complex*)(void*)this.GetWBlock();
+ Utils.Memcpy(ptr + this._writePos, buf + ofs + count - num, num2 * sizeof(Complex));
+ this._writePos += num2;
+ }
+ this._size += count;
+ }
+ if (this._readEvent != null)
+ {
+ this._readEvent.Set();
+ }
+ }
+
+ public unsafe int Read(Complex* buf, int count)
+ {
+ return this.Read(buf, 0, count);
+ }
+
+ public unsafe void Write(Complex* buf, int count)
+ {
+ this.Write(buf, 0, count);
+ }
+
+ private int DoAdvance(int count)
+ {
+ int num = count;
+ while (num > 0 && this._size > 0)
+ {
+ if (this._readPos == 8192)
+ {
+ this._readPos = 0;
+ this.FreeBlock(this._blocks[0]);
+ this._blocks.RemoveAt(0);
+ }
+ int num2 = (this._blocks.Count == 1) ? Math.Min(this._writePos - this._readPos, num) : Math.Min(8192 - this._readPos, num);
+ this._readPos += num2;
+ num -= num2;
+ this._size -= num2;
+ }
+ return count - num;
+ }
+
+ public int Advance(int count)
+ {
+ if (this._readEvent != null)
+ {
+ while (this._size == 0 && !this._terminated)
+ {
+ this._readEvent.WaitOne();
+ }
+ if (this._terminated)
+ {
+ return 0;
+ }
+ }
+ int result = default(int);
+ lock (this)
+ {
+ result = this.DoAdvance(count);
+ }
+ if (this._writeEvent != null)
+ {
+ this._writeEvent.Set();
+ }
+ return result;
+ }
+
+ private unsafe int DoPeek(Complex* buf, int ofs, int count)
+ {
+ int num = count;
+ int num2 = this._readPos;
+ int num3 = this._size;
+ int num4 = 0;
+ while (num > 0 && num3 > 0)
+ {
+ if (num2 == 8192)
+ {
+ num2 = 0;
+ num4++;
+ }
+ int num5 = Math.Min(((num4 < this._blocks.Count - 1) ? 8192 : this._writePos) - num2, num);
+ Complex* ptr = (Complex*)(void*)this._blocks[num4];
+ Utils.Memcpy(buf + ofs + count - num, ptr + num2, num5 * sizeof(Complex));
+ num -= num5;
+ num2 += num5;
+ num3 -= num5;
+ }
+ return count - num;
+ }
+
+ public unsafe int Peek(Complex* buf, int ofs, int count)
+ {
+ lock (this)
+ {
+ return this.DoPeek(buf, ofs, count);
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/ComplexFilter.cs b/SDRSharp.Radio/SDRSharp.Radio/ComplexFilter.cs
new file mode 100644
index 0000000..2dd6fb4
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/ComplexFilter.cs
@@ -0,0 +1,67 @@
+namespace SDRSharp.Radio
+{
+ public class ComplexFilter : OverlapSaveProcessor
+ {
+ private UnsafeBuffer _kernelBuffer;
+
+ private unsafe Complex* _kernelPtr;
+
+ private int _actualKernelLength;
+
+ public int KernelSize
+ {
+ get
+ {
+ return this._actualKernelLength;
+ }
+ }
+
+ public unsafe ComplexFilter(Complex[] kernel)
+ : base(ComplexFilter.GetFFTSize(kernel.Length))
+ {
+ this._actualKernelLength = kernel.Length;
+ this._kernelBuffer = UnsafeBuffer.Create(base.FFTSize, sizeof(Complex));
+ this._kernelPtr = (Complex*)(void*)this._kernelBuffer;
+ this.SetKernel(kernel);
+ }
+
+ private static int GetFFTSize(int length)
+ {
+ int num;
+ for (num = 1; num < length; num <<= 1)
+ {
+ }
+ return num << 1;
+ }
+
+ public bool IsKernelLengthSupported(int length)
+ {
+ return length < base.FFTSize / 2;
+ }
+
+ public unsafe void SetKernel(Complex[] kernel)
+ {
+ if (this.IsKernelLengthSupported(kernel.Length))
+ {
+ fixed (Complex* src = kernel)
+ {
+ Utils.Memcpy(this._kernelPtr, src, kernel.Length * sizeof(Complex));
+ }
+ for (int i = kernel.Length; i < base.FFTSize; i++)
+ {
+ this._kernelPtr[i] = 0f;
+ }
+ Fourier.ForwardTransform(this._kernelPtr, base.FFTSize, false);
+ }
+ }
+
+ protected unsafe override void ProcessFft(Complex* buffer, int length)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ Complex* intPtr = buffer + i;
+ *intPtr *= this._kernelPtr[i];
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/CwDetector.cs b/SDRSharp.Radio/SDRSharp.Radio/CwDetector.cs
new file mode 100644
index 0000000..8ba914c
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/CwDetector.cs
@@ -0,0 +1,40 @@
+namespace SDRSharp.Radio
+{
+ public sealed class CwDetector
+ {
+ private Oscillator _bfo = new Oscillator();
+
+ public double SampleRate
+ {
+ get
+ {
+ return this._bfo.SampleRate;
+ }
+ set
+ {
+ this._bfo.SampleRate = value;
+ }
+ }
+
+ public int BfoFrequency
+ {
+ get
+ {
+ return (int)this._bfo.Frequency;
+ }
+ set
+ {
+ this._bfo.Frequency = (double)value;
+ }
+ }
+
+ public unsafe void Demodulate(Complex* iq, float* audio, int length)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ this._bfo.Tick();
+ audio[i] = (iq[i] * this._bfo.Phase).Real;
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/DSPThreadPool.cs b/SDRSharp.Radio/SDRSharp.Radio/DSPThreadPool.cs
new file mode 100644
index 0000000..dcc0f19
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/DSPThreadPool.cs
@@ -0,0 +1,52 @@
+using System.Threading;
+
+namespace SDRSharp.Radio
+{
+ public static class DSPThreadPool
+ {
+ private static SharpThreadPool _threadPool;
+
+ public static void Initialize()
+ {
+ if (DSPThreadPool._threadPool == null)
+ {
+ DSPThreadPool._threadPool = new SharpThreadPool();
+ }
+ }
+
+ public static void Initialize(int threadCount)
+ {
+ if (DSPThreadPool._threadPool == null)
+ {
+ DSPThreadPool._threadPool = new SharpThreadPool(threadCount);
+ }
+ }
+
+ public static void QueueUserWorkItem(WaitCallback callback)
+ {
+ if (DSPThreadPool._threadPool == null)
+ {
+ DSPThreadPool._threadPool = new SharpThreadPool();
+ }
+ DSPThreadPool._threadPool.QueueUserWorkItem(callback);
+ }
+
+ public static void QueueUserWorkItem(WaitCallback callback, object parameter)
+ {
+ if (DSPThreadPool._threadPool == null)
+ {
+ DSPThreadPool._threadPool = new SharpThreadPool();
+ }
+ DSPThreadPool._threadPool.QueueUserWorkItem(callback, parameter);
+ }
+
+ public static void Terminate()
+ {
+ if (DSPThreadPool._threadPool != null)
+ {
+ DSPThreadPool._threadPool.Dispose();
+ DSPThreadPool._threadPool = null;
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/DcRemover.cs b/SDRSharp.Radio/SDRSharp.Radio/DcRemover.cs
new file mode 100644
index 0000000..f87034d
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/DcRemover.cs
@@ -0,0 +1,56 @@
+using System.Runtime.InteropServices;
+
+namespace SDRSharp.Radio
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 16)]
+ public struct DcRemover
+ {
+ private float _average;
+
+ private float _ratio;
+
+ public float Offset
+ {
+ get
+ {
+ return this._average;
+ }
+ }
+
+ public DcRemover(float ratio)
+ {
+ this._ratio = ratio;
+ this._average = 0f;
+ }
+
+ public void Init(float ratio)
+ {
+ this._ratio = ratio;
+ this._average = 0f;
+ }
+
+ public unsafe void Process(float* buffer, int length)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ this._average += this._ratio * (buffer[i] - this._average);
+ buffer[i] -= this._average;
+ }
+ }
+
+ public unsafe void ProcessInterleaved(float* buffer, int length)
+ {
+ length *= 2;
+ for (int i = 0; i < length; i += 2)
+ {
+ this._average += this._ratio * (buffer[i] - this._average);
+ buffer[i] -= this._average;
+ }
+ }
+
+ public void Reset()
+ {
+ this._average = 0f;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/DetectorType.cs b/SDRSharp.Radio/SDRSharp.Radio/DetectorType.cs
new file mode 100644
index 0000000..4d8b70c
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/DetectorType.cs
@@ -0,0 +1,14 @@
+namespace SDRSharp.Radio
+{
+ public enum DetectorType
+ {
+ NFM,
+ WFM,
+ AM,
+ DSB,
+ LSB,
+ USB,
+ CW,
+ RAW
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/DownConverter.cs b/SDRSharp.Radio/SDRSharp.Radio/DownConverter.cs
new file mode 100644
index 0000000..e13e7b8
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/DownConverter.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SDRSharp.Radio
+{
+ public sealed class DownConverter : IDisposable
+ {
+ private IntPtr _dec;
+
+ private int _decimationRatio;
+
+ private double _sampleRate;
+
+ private double _frequency;
+
+ public double Frequency
+ {
+ get
+ {
+ return this._frequency;
+ }
+ set
+ {
+ if (this._frequency != value)
+ {
+ this._frequency = value;
+ DownConverter.ddc_tune(this._dec, this._frequency);
+ }
+ }
+ }
+
+ public int DecimationRatio
+ {
+ get
+ {
+ return this._decimationRatio;
+ }
+ }
+
+ public double SampleRate
+ {
+ get
+ {
+ return this._sampleRate;
+ }
+ }
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr ddc_create(double sample_rate, int decimation);
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private static extern void ddc_destroy(IntPtr instance);
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private static extern void ddc_tune(IntPtr instance, double frequency);
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private unsafe static extern int ddc_process(IntPtr instance, Complex* buffer, int length);
+
+ public DownConverter(double sampleRate, int decimationRatio)
+ {
+ this._sampleRate = sampleRate;
+ this._decimationRatio = decimationRatio;
+ this._dec = DownConverter.ddc_create(this._sampleRate, this._decimationRatio);
+ }
+
+ public void Dispose()
+ {
+ if (this._dec != IntPtr.Zero)
+ {
+ DownConverter.ddc_destroy(this._dec);
+ this._dec = IntPtr.Zero;
+ }
+ }
+
+ public unsafe int Process(Complex* buffer, int length)
+ {
+ return DownConverter.ddc_process(this._dec, buffer, length);
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/FilterBuilder.cs b/SDRSharp.Radio/SDRSharp.Radio/FilterBuilder.cs
new file mode 100644
index 0000000..e5985d0
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/FilterBuilder.cs
@@ -0,0 +1,387 @@
+using System;
+
+namespace SDRSharp.Radio
+{
+ public static class FilterBuilder
+ {
+ public const int DefaultFilterOrder = 500;
+
+ public static float[] MakeWindow(WindowType windowType, int length)
+ {
+ float[] array = new float[length];
+ length--;
+ for (int i = 0; i <= length; i++)
+ {
+ array[i] = 1f;
+ switch (windowType)
+ {
+ case WindowType.Hamming:
+ {
+ float num = 0.54f;
+ float num2 = 0.46f;
+ float num3 = 0f;
+ float num4 = 0f;
+ array[i] *= num - num2 * (float)Math.Cos(6.2831853071795862 * (double)i / (double)length) + num3 * (float)Math.Cos(12.566370614359172 * (double)i / (double)length) - num4 * (float)Math.Cos(18.849555921538759 * (double)i / (double)length);
+ break;
+ }
+ case WindowType.Blackman:
+ {
+ float num = 0.42f;
+ float num2 = 0.5f;
+ float num3 = 0.08f;
+ float num4 = 0f;
+ array[i] *= num - num2 * (float)Math.Cos(6.2831853071795862 * (double)i / (double)length) + num3 * (float)Math.Cos(12.566370614359172 * (double)i / (double)length) - num4 * (float)Math.Cos(18.849555921538759 * (double)i / (double)length);
+ break;
+ }
+ case WindowType.BlackmanHarris4:
+ {
+ float num = 0.35875f;
+ float num2 = 0.48829f;
+ float num3 = 0.14128f;
+ float num4 = 0.01168f;
+ array[i] *= num - num2 * (float)Math.Cos(6.2831853071795862 * (double)i / (double)length) + num3 * (float)Math.Cos(12.566370614359172 * (double)i / (double)length) - num4 * (float)Math.Cos(18.849555921538759 * (double)i / (double)length);
+ break;
+ }
+ case WindowType.BlackmanHarris7:
+ {
+ float num = 0.2710514f;
+ float num2 = 0.433297932f;
+ float num3 = 0.218123f;
+ float num4 = 0.06592545f;
+ float num6 = 0.0108117424f;
+ float num7 = 0.000776584842f;
+ float num8 = 1.38872174E-05f;
+ array[i] *= num - num2 * (float)Math.Cos(6.2831853071795862 * (double)i / (double)length) + num3 * (float)Math.Cos(12.566370614359172 * (double)i / (double)length) - num4 * (float)Math.Cos(18.849555921538759 * (double)i / (double)length) + num6 * (float)Math.Cos(25.132741228718345 * (double)i / (double)length) - num7 * (float)Math.Cos(31.415926535897931 * (double)i / (double)length) + num8 * (float)Math.Cos(37.699111843077517 * (double)i / (double)length);
+ break;
+ }
+ case WindowType.HannPoisson:
+ {
+ float value = (float)i - (float)length / 2f;
+ float num5 = 0.005f;
+ array[i] *= 0.5f * (float)((1.0 + Math.Cos(6.2831853071795862 * (double)value / (double)length)) * Math.Exp(-2.0 * (double)num5 * (double)Math.Abs(value) / (double)length));
+ break;
+ }
+ case WindowType.Youssef:
+ {
+ float num = 0.35875f;
+ float num2 = 0.48829f;
+ float num3 = 0.14128f;
+ float num4 = 0.01168f;
+ float value = (float)i - (float)length / 2f;
+ float num5 = 0.005f;
+ array[i] *= num - num2 * (float)Math.Cos(6.2831853071795862 * (double)i / (double)length) + num3 * (float)Math.Cos(12.566370614359172 * (double)i / (double)length) - num4 * (float)Math.Cos(18.849555921538759 * (double)i / (double)length);
+ array[i] *= (float)Math.Exp(-2.0 * (double)num5 * (double)Math.Abs(value) / (double)length);
+ break;
+ }
+ }
+ }
+ return array;
+ }
+
+ public static float[] MakeSinc(double sampleRate, double frequency, int length)
+ {
+ if (length % 2 == 0)
+ {
+ throw new ArgumentException("Length should be odd", "length");
+ }
+ double num = 6.2831853071795862 * frequency / sampleRate;
+ float[] array = new float[length];
+ for (int i = 0; i < length; i++)
+ {
+ int num2 = i - length / 2;
+ if (num2 == 0)
+ {
+ array[i] = (float)num;
+ }
+ else
+ {
+ array[i] = (float)(Math.Sin(num * (double)num2) / (double)num2);
+ }
+ }
+ return array;
+ }
+
+ public static float[] MakeSin(double sampleRate, double frequency, int length)
+ {
+ if (length % 2 == 0)
+ {
+ throw new ArgumentException("Length should be odd", "length");
+ }
+ double num = 6.2831853071795862 * frequency / sampleRate;
+ float[] array = new float[length];
+ int num2 = length / 2;
+ for (int i = 0; i <= num2; i++)
+ {
+ array[num2 - i] = 0f - (array[num2 + i] = (float)Math.Sin(num * (double)i));
+ }
+ return array;
+ }
+
+ public static float[] MakeLowPassKernel(double sampleRate, int filterOrder, double cutoffFrequency, WindowType windowType)
+ {
+ filterOrder |= 1;
+ float[] array = FilterBuilder.MakeSinc(sampleRate, cutoffFrequency, filterOrder);
+ float[] window = FilterBuilder.MakeWindow(windowType, filterOrder);
+ FilterBuilder.ApplyWindow(array, window);
+ FilterBuilder.Normalize(array);
+ return array;
+ }
+
+ public static float[] MakeHighPassKernel(double sampleRate, int filterOrder, double cutoffFrequency, WindowType windowType)
+ {
+ return FilterBuilder.InvertSpectrum(FilterBuilder.MakeLowPassKernel(sampleRate, filterOrder, cutoffFrequency, windowType));
+ }
+
+ public static float[] MakeBandPassKernel(double sampleRate, int filterOrder, double cutoff1, double cutoff2, WindowType windowType)
+ {
+ double num = (cutoff2 - cutoff1) / 2.0;
+ double num2 = cutoff2 - num;
+ double num3 = 6.2831853071795862 * num2 / sampleRate;
+ float[] array = FilterBuilder.MakeLowPassKernel(sampleRate, filterOrder, num, windowType);
+ for (int i = 0; i < array.Length; i++)
+ {
+ int num4 = i - array.Length / 2;
+ array[i] *= (float)(2.0 * Math.Cos(num3 * (double)num4));
+ }
+ return array;
+ }
+
+ public static Complex[] MakeComplexKernel(double sampleRate, int filterOrder, double bandwidth, double offset, WindowType windowType)
+ {
+ double num = -6.2831853071795862 * offset / sampleRate;
+ float[] array = FilterBuilder.MakeLowPassKernel(sampleRate, filterOrder, bandwidth * 0.5, windowType);
+ Complex[] array2 = new Complex[array.Length];
+ for (int i = 0; i < array2.Length; i++)
+ {
+ int num2 = i - array2.Length / 2;
+ double num3 = num * (double)num2;
+ array2[i].Real = (float)((double)array[i] * Math.Cos(num3));
+ array2[i].Imag = (float)((double)(0f - array[i]) * Math.Sin(num3));
+ }
+ return array2;
+ }
+
+ public static Complex[] MakeComplexKernel(double sampleRate, double passband, double transition, double ripple, double attenuation, double offset)
+ {
+ double num = -6.2831853071795862 * offset / sampleRate;
+ passband *= 0.5;
+ float[] array = FilterBuilder.MakeLowPassKernel(sampleRate, passband, passband + transition, ripple, attenuation);
+ if (array == null)
+ {
+ return null;
+ }
+ Complex[] array2 = new Complex[array.Length];
+ for (int i = 0; i < array2.Length; i++)
+ {
+ int num2 = i - array2.Length / 2;
+ double num3 = num * (double)num2;
+ array2[i].Real = (float)((double)array[i] * Math.Cos(num3));
+ array2[i].Imag = (float)((double)(0f - array[i]) * Math.Sin(num3));
+ }
+ return array2;
+ }
+
+ private static double ACosh(double x)
+ {
+ return Math.Log(x + Math.Sqrt(x * x - 1.0));
+ }
+
+ private static double ChebychevPoly(int n, double x)
+ {
+ if (Math.Abs(x) <= 1.0)
+ {
+ return Math.Cos((double)n * Math.Acos(x));
+ }
+ return Math.Cosh((double)n * FilterBuilder.ACosh(x));
+ }
+
+ private static float[] MakeChebychevWindow(int length, double attenuation)
+ {
+ double num = Math.Pow(10.0, attenuation / 20.0);
+ float[] array = new float[length];
+ float num2 = 0f;
+ double num3 = Math.Cosh(FilterBuilder.ACosh(num) / (double)(length - 1));
+ double num4 = (double)((length - 1) / 2);
+ if (length % 2 == 0)
+ {
+ num4 += 0.5;
+ }
+ for (int i = 0; i < length / 2 + 1; i++)
+ {
+ double num5 = (double)i - num4;
+ double num6 = 0.0;
+ for (int j = 1; (double)j <= num4; j++)
+ {
+ num6 += FilterBuilder.ChebychevPoly(length - 1, num3 * Math.Cos(3.1415926535897931 * (double)j / (double)length)) * Math.Cos(2.0 * num5 * 3.1415926535897931 * (double)j / (double)length);
+ }
+ array[i] = (float)(num + 2.0 * num6);
+ array[length - i - 1] = array[i];
+ if (array[i] > num2)
+ {
+ num2 = array[i];
+ }
+ }
+ float num7 = 1f / num2;
+ for (int k = 0; k < length; k++)
+ {
+ array[k] *= num7;
+ }
+ return array;
+ }
+
+ private static float[] MakeChebychevLowPass(int filterOrder, double normalizedCutoff, double attenuation)
+ {
+ float[] array = FilterBuilder.MakeChebychevWindow(filterOrder, attenuation);
+ float[] array2 = FilterBuilder.MakeSinc(2.0, normalizedCutoff, array.Length);
+ FilterBuilder.ApplyWindow(array2, array);
+ FilterBuilder.Normalize(array2);
+ return array2;
+ }
+
+ public static float[] MakeLowPassKernel(double sampleRate, double passband, double stopband, double ripple, double attenuation)
+ {
+ double num = 2.0 * passband / sampleRate;
+ double num2 = 2.0 * stopband / sampleRate;
+ int num3 = (int)((double)FilterBuilder.EstimateOrder(num, num2, ripple, attenuation) * 1.4) | 1;
+ int num4 = Math.Max(1, num3 - 20);
+ int num5 = num4 + 40;
+ for (int i = num4; i < num5; i += 2)
+ {
+ int num6 = 50;
+ double num7 = 0.0;
+ for (int j = 0; j < 100; j++)
+ {
+ double normalizedCutoff = (num * (double)num6 + num2 * (double)(100 - num6)) / 100.0;
+ float[] array = FilterBuilder.MakeChebychevLowPass(i, normalizedCutoff, attenuation + num7);
+ double num8;
+ double num9;
+ FilterBuilder.GetFilterSpecs(array, num, num2, out num8, out num9);
+ if (num8 <= ripple && num9 >= attenuation)
+ {
+ return array;
+ }
+ num6 += Math.Sign(ripple - num8);
+ num7 += (double)Math.Sign(attenuation - num9) * 0.15 - (double)Math.Sign(ripple - num8) * 2.5;
+ if (num7 < -20.0)
+ {
+ num7 = -20.0;
+ }
+ if (num7 > 20.0)
+ {
+ num7 = 20.0;
+ }
+ if (num6 < 0)
+ {
+ num6 = 0;
+ }
+ if (num6 > 100)
+ {
+ num6 = 100;
+ }
+ }
+ }
+ return null;
+ }
+
+ public static int EstimateOrder(double passband, double stopband, double ripple, double attenuation)
+ {
+ return (int)Math.Round(0.0010765282977552 * (52.0490855934073 * attenuation + 194.466145732802 / Math.Log10(1.0 + ripple)) / (stopband - passband) + 1.74881176954081);
+ }
+
+ public static double GetFilterError(float[] kernel, double passband, double stopband, double ripple, double attenuation)
+ {
+ double num;
+ double num2;
+ FilterBuilder.GetFilterSpecs(kernel, passband, stopband, out num, out num2);
+ return ((num2 >= attenuation) ? 0.0 : (attenuation - num2)) + 100.0 * ((num <= ripple) ? 0.0 : (num - ripple));
+ }
+
+ public unsafe static void GetFilterSpecs(float[] kernel, double passband, double stopband, out double ripple, out double attenuation)
+ {
+ int num = 0;
+ int num2 = 0;
+ int num3 = 2;
+ while (true)
+ {
+ if (num3 >= kernel.Length * 2 && num >= 4)
+ {
+ break;
+ }
+ num = (int)Math.Round((double)num3 * passband);
+ num2 = (int)Math.Ceiling((double)num3 * stopband);
+ num3 *= 2;
+ }
+ Complex[] obj = new Complex[num3];
+ float[] array = new float[num3 / 2];
+ Complex[] array2 = obj;
+ fixed (Complex* ptr = array2)
+ {
+ float[] array3 = array;
+ fixed (float* ptr2 = array3)
+ {
+ for (int i = 0; i < kernel.Length; i++)
+ {
+ ptr[i] = kernel[i];
+ }
+ Fourier.ForwardTransform(ptr, num3, false);
+ Fourier.SpectrumPower(ptr, ptr2, array.Length, 0f);
+ float num4 = float.PositiveInfinity;
+ float num5 = float.NegativeInfinity;
+ for (int j = 0; j <= num; j++)
+ {
+ if (num4 > ptr2[j])
+ {
+ num4 = ptr2[j];
+ }
+ if (num5 < ptr2[j])
+ {
+ num5 = ptr2[j];
+ }
+ }
+ ripple = (double)(num5 - num4);
+ float num6 = float.NegativeInfinity;
+ for (int k = num2; k < array.Length; k++)
+ {
+ if (num6 < ptr2[k])
+ {
+ num6 = ptr2[k];
+ }
+ }
+ attenuation = Math.Max(0.0, (double)(num4 + num5) * 0.5 - (double)num6);
+ }
+ }
+ }
+
+ public static void Normalize(float[] h)
+ {
+ double num = 0.0;
+ for (int i = 0; i < h.Length; i++)
+ {
+ num += (double)h[i];
+ }
+ double num2 = 1.0 / num;
+ for (int j = 0; j < h.Length; j++)
+ {
+ h[j] = (float)((double)h[j] * num2);
+ }
+ }
+
+ public static void ApplyWindow(float[] coefficients, float[] window)
+ {
+ for (int i = 0; i < coefficients.Length; i++)
+ {
+ coefficients[i] *= window[i];
+ }
+ }
+
+ private static float[] InvertSpectrum(float[] h)
+ {
+ for (int i = 0; i < h.Length; i++)
+ {
+ h[i] = 0f - h[i];
+ }
+ h[(h.Length - 1) / 2] += 1f;
+ return h;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/FirFilter.cs b/SDRSharp.Radio/SDRSharp.Radio/FirFilter.cs
new file mode 100644
index 0000000..cb9733a
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/FirFilter.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SDRSharp.Radio
+{
+ public sealed class FirFilter : IDisposable
+ {
+ private int _decimationRatio;
+
+ private int _length;
+
+ private IntPtr _fir;
+
+ public int Length
+ {
+ get
+ {
+ return this._length;
+ }
+ }
+
+ public int DecimationRatio
+ {
+ get
+ {
+ return this._decimationRatio;
+ }
+ }
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private unsafe static extern IntPtr float_fir_create(float* kernel, int length, int decimation);
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private static extern void float_fir_destroy(IntPtr instance);
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private unsafe static extern int float_fir_process(IntPtr instance, float* buffer, int length);
+
+ public unsafe FirFilter(float[] coefficients, int decimationRatio = 1)
+ {
+ if (decimationRatio <= 0)
+ {
+ throw new ArgumentException("The decimation factor must be greater than zero", "decimationRatio");
+ }
+ this._decimationRatio = decimationRatio;
+ this._length = coefficients.Length;
+ fixed (float* kernel = coefficients)
+ {
+ this._fir = FirFilter.float_fir_create(kernel, this._length, decimationRatio);
+ }
+ }
+
+ public void Dispose()
+ {
+ if (this._fir != IntPtr.Zero)
+ {
+ FirFilter.float_fir_destroy(this._fir);
+ this._fir = IntPtr.Zero;
+ }
+ GC.SuppressFinalize(this);
+ }
+
+ public unsafe int Process(float* buffer, int length)
+ {
+ return FirFilter.float_fir_process(this._fir, buffer, length);
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/FloatCircularBuffer.cs b/SDRSharp.Radio/SDRSharp.Radio/FloatCircularBuffer.cs
new file mode 100644
index 0000000..f19489b
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/FloatCircularBuffer.cs
@@ -0,0 +1,30 @@
+namespace SDRSharp.Radio
+{
+ public class FloatCircularBuffer : CircularBuffer
+ {
+ public FloatCircularBuffer(int bufferSize, int maxBufferCount)
+ : base(bufferSize, 4, maxBufferCount)
+ {
+ }
+
+ public unsafe bool Write(float* buffer, int len, bool block)
+ {
+ return base.Write((byte*)buffer, len, block);
+ }
+
+ public unsafe bool Write(float* buffer, int len)
+ {
+ return base.Write((byte*)buffer, len, true);
+ }
+
+ public unsafe float* Acquire()
+ {
+ return (float*)base.AcquireRawBuffer(true);
+ }
+
+ public unsafe float* Acquire(bool block)
+ {
+ return (float*)base.AcquireRawBuffer(block);
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/FloatDecimator.cs b/SDRSharp.Radio/SDRSharp.Radio/FloatDecimator.cs
new file mode 100644
index 0000000..dc3e708
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/FloatDecimator.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SDRSharp.Radio
+{
+ public sealed class FloatDecimator : IDisposable
+ {
+ private IntPtr _dec;
+
+ private int _decimationRatio;
+
+ public int DecimationRatio
+ {
+ get
+ {
+ return this._decimationRatio;
+ }
+ }
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr float_decimator_create(int decimation);
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private static extern void float_decimator_destroy(IntPtr instance);
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private unsafe static extern int float_decimator_process(IntPtr instance, float* buffer, int length);
+
+ public FloatDecimator(int decimationRatio)
+ {
+ this._decimationRatio = decimationRatio;
+ this._dec = FloatDecimator.float_decimator_create(decimationRatio);
+ }
+
+ public void Dispose()
+ {
+ if (this._dec != IntPtr.Zero)
+ {
+ FloatDecimator.float_decimator_destroy(this._dec);
+ this._dec = IntPtr.Zero;
+ }
+ }
+
+ public unsafe int Process(float* buffer, int length)
+ {
+ return FloatDecimator.float_decimator_process(this._dec, buffer, length);
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/FloatFifoStream.cs b/SDRSharp.Radio/SDRSharp.Radio/FloatFifoStream.cs
new file mode 100644
index 0000000..ba1b4ab
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/FloatFifoStream.cs
@@ -0,0 +1,285 @@
+using System;
+using System.Collections.Generic;
+
+namespace SDRSharp.Radio
+{
+ public sealed class FloatFifoStream : IDisposable
+ {
+ private const int BlockSize = 16384;
+
+ private const int MaxBlocksInCache = 128;
+
+ private int _size;
+
+ private int _readPos;
+
+ private int _writePos;
+
+ private bool _terminated;
+
+ private readonly int _maxSize;
+
+ private readonly SharpEvent _writeEvent;
+
+ private readonly SharpEvent _readEvent;
+
+ private readonly Stack _usedBlocks = new Stack();
+
+ private readonly List _blocks = new List();
+
+ public int Length
+ {
+ get
+ {
+ return this._size;
+ }
+ }
+
+ public FloatFifoStream()
+ : this(BlockMode.None)
+ {
+ }
+
+ public FloatFifoStream(BlockMode blockMode)
+ : this(blockMode, 0)
+ {
+ }
+
+ public FloatFifoStream(BlockMode blockMode, int maxSize)
+ {
+ if (blockMode == BlockMode.BlockingRead || blockMode == BlockMode.BlockingReadWrite)
+ {
+ this._readEvent = new SharpEvent(false);
+ }
+ if (blockMode == BlockMode.BlockingWrite || blockMode == BlockMode.BlockingReadWrite)
+ {
+ if (maxSize <= 0)
+ {
+ throw new ArgumentException("MaxSize should be greater than zero when in blocking write mode", "maxSize");
+ }
+ this._writeEvent = new SharpEvent(false);
+ }
+ this._maxSize = maxSize;
+ }
+
+ ~FloatFifoStream()
+ {
+ this.Dispose();
+ }
+
+ public void Dispose()
+ {
+ this.Close();
+ GC.SuppressFinalize(this);
+ }
+
+ private UnsafeBuffer AllocBlock()
+ {
+ if (this._usedBlocks.Count <= 0)
+ {
+ return UnsafeBuffer.Create(16384, 4);
+ }
+ return this._usedBlocks.Pop();
+ }
+
+ private void FreeBlock(UnsafeBuffer block)
+ {
+ if (this._usedBlocks.Count < 128)
+ {
+ this._usedBlocks.Push(block);
+ }
+ }
+
+ private UnsafeBuffer GetWBlock()
+ {
+ UnsafeBuffer unsafeBuffer;
+ if (this._writePos < 16384 && this._blocks.Count > 0)
+ {
+ unsafeBuffer = this._blocks[this._blocks.Count - 1];
+ }
+ else
+ {
+ unsafeBuffer = this.AllocBlock();
+ this._blocks.Add(unsafeBuffer);
+ this._writePos = 0;
+ }
+ return unsafeBuffer;
+ }
+
+ public void Open()
+ {
+ this._terminated = false;
+ }
+
+ public void Close()
+ {
+ this._terminated = true;
+ this.Flush();
+ }
+
+ public void Flush()
+ {
+ lock (this)
+ {
+ foreach (UnsafeBuffer block in this._blocks)
+ {
+ this.FreeBlock(block);
+ }
+ this._blocks.Clear();
+ this._readPos = 0;
+ this._writePos = 0;
+ this._size = 0;
+ }
+ if (this._writeEvent != null)
+ {
+ this._writeEvent.Set();
+ }
+ if (this._readEvent != null)
+ {
+ this._readEvent.Set();
+ }
+ }
+
+ public unsafe int Read(float* buf, int ofs, int count)
+ {
+ if (this._readEvent != null)
+ {
+ while (this._size == 0 && !this._terminated)
+ {
+ this._readEvent.WaitOne();
+ }
+ if (this._terminated)
+ {
+ return 0;
+ }
+ }
+ int num = default(int);
+ lock (this)
+ {
+ num = this.DoPeek(buf, ofs, count);
+ this.DoAdvance(num);
+ }
+ if (this._writeEvent != null)
+ {
+ this._writeEvent.Set();
+ }
+ return num;
+ }
+
+ public unsafe int Read(float* buf, int count)
+ {
+ return this.Read(buf, 0, count);
+ }
+
+ public unsafe void Write(float* buf, int ofs, int count)
+ {
+ if (this._writeEvent != null)
+ {
+ while (this._size >= this._maxSize && !this._terminated)
+ {
+ this._writeEvent.WaitOne();
+ }
+ if (this._terminated)
+ {
+ return;
+ }
+ }
+ lock (this)
+ {
+ int num2;
+ for (int num = count; num > 0; num -= num2)
+ {
+ num2 = Math.Min(16384 - this._writePos, num);
+ float* ptr = (float*)(void*)this.GetWBlock();
+ Utils.Memcpy(ptr + this._writePos, buf + ofs + count - num, num2 * 4);
+ this._writePos += num2;
+ }
+ this._size += count;
+ }
+ if (this._readEvent != null)
+ {
+ this._readEvent.Set();
+ }
+ }
+
+ public unsafe void Write(float* buf, int count)
+ {
+ this.Write(buf, 0, count);
+ }
+
+ private int DoAdvance(int count)
+ {
+ int num = count;
+ while (num > 0 && this._size > 0)
+ {
+ if (this._readPos == 16384)
+ {
+ this._readPos = 0;
+ this.FreeBlock(this._blocks[0]);
+ this._blocks.RemoveAt(0);
+ }
+ int num2 = (this._blocks.Count == 1) ? Math.Min(this._writePos - this._readPos, num) : Math.Min(16384 - this._readPos, num);
+ this._readPos += num2;
+ num -= num2;
+ this._size -= num2;
+ }
+ return count - num;
+ }
+
+ public int Advance(int count)
+ {
+ if (this._readEvent != null)
+ {
+ while (this._size == 0 && !this._terminated)
+ {
+ this._readEvent.WaitOne();
+ }
+ if (this._terminated)
+ {
+ return 0;
+ }
+ }
+ int result = default(int);
+ lock (this)
+ {
+ result = this.DoAdvance(count);
+ }
+ if (this._writeEvent != null)
+ {
+ this._writeEvent.Set();
+ }
+ return result;
+ }
+
+ private unsafe int DoPeek(float* buf, int ofs, int count)
+ {
+ int num = count;
+ int num2 = this._readPos;
+ int num3 = this._size;
+ int num4 = 0;
+ while (num > 0 && num3 > 0)
+ {
+ if (num2 == 16384)
+ {
+ num2 = 0;
+ num4++;
+ }
+ int num5 = Math.Min(((num4 < this._blocks.Count - 1) ? 16384 : this._writePos) - num2, num);
+ float* ptr = (float*)(void*)this._blocks[num4];
+ Utils.Memcpy(buf + ofs + count - num, ptr + num2, num5 * 4);
+ num -= num5;
+ num2 += num5;
+ num3 -= num5;
+ }
+ return count - num;
+ }
+
+ public unsafe int Peek(float* buf, int ofs, int count)
+ {
+ lock (this)
+ {
+ return this.DoPeek(buf, ofs, count);
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/FmDetector.cs b/SDRSharp.Radio/SDRSharp.Radio/FmDetector.cs
new file mode 100644
index 0000000..8076b27
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/FmDetector.cs
@@ -0,0 +1,157 @@
+using System;
+
+namespace SDRSharp.Radio
+{
+ public sealed class FmDetector
+ {
+ private const float NarrowAFGain = 0.5f;
+
+ private const float FMGain = 1E-05f;
+
+ private const int MinHissFrequency = 4000;
+
+ private const int MaxHissFrequency = 6000;
+
+ private const int HissFilterOrder = 20;
+
+ private const float HissFactor = 2E-05f;
+
+ private unsafe float* _hissPtr;
+
+ private UnsafeBuffer _hissBuffer;
+
+ private FirFilter _hissFilter;
+
+ private Complex _iqState;
+
+ private float _noiseLevel;
+
+ private double _sampleRate;
+
+ private float _noiseAveragingRatio;
+
+ private int _squelchThreshold;
+
+ private bool _isSquelchOpen;
+
+ private float _noiseThreshold;
+
+ private FmMode _mode;
+
+ public double SampleRate
+ {
+ get
+ {
+ return this._sampleRate;
+ }
+ set
+ {
+ if (value != this._sampleRate)
+ {
+ this._sampleRate = value;
+ this._noiseAveragingRatio = (float)(30.0 / this._sampleRate);
+ float[] coefficients = FilterBuilder.MakeBandPassKernel(this._sampleRate, 20, 4000.0, 6000.0, WindowType.BlackmanHarris4);
+ if (this._hissFilter != null)
+ {
+ this._hissFilter.Dispose();
+ }
+ this._hissFilter = new FirFilter(coefficients, 1);
+ }
+ }
+ }
+
+ public int SquelchThreshold
+ {
+ get
+ {
+ return this._squelchThreshold;
+ }
+ set
+ {
+ if (this._squelchThreshold != value)
+ {
+ this._squelchThreshold = value;
+ this._noiseThreshold = (float)Math.Log10(2.0 - (double)this._squelchThreshold / 100.0) * 2E-05f;
+ }
+ }
+ }
+
+ public bool IsSquelchOpen
+ {
+ get
+ {
+ return this._isSquelchOpen;
+ }
+ }
+
+ public FmMode Mode
+ {
+ get
+ {
+ return this._mode;
+ }
+ set
+ {
+ this._mode = value;
+ }
+ }
+
+ public unsafe void Demodulate(Complex* iq, float* audio, int length)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ Complex a = iq[i] * this._iqState.Conjugate();
+ float num = a.Modulus();
+ if (num > 0f)
+ {
+ a /= num;
+ }
+ float num2 = a.Argument();
+ if (!float.IsNaN(num2))
+ {
+ audio[i] = num2 * 1E-05f;
+ }
+ else
+ {
+ audio[i] = 0f;
+ }
+ this._iqState = iq[i];
+ }
+ if (this._mode == FmMode.Narrow)
+ {
+ this.ProcessSquelch(audio, length);
+ for (int j = 0; j < length; j++)
+ {
+ audio[j] *= 0.5f;
+ }
+ }
+ }
+
+ private unsafe void ProcessSquelch(float* audio, int length)
+ {
+ if (this._squelchThreshold > 0)
+ {
+ if (this._hissBuffer == null || this._hissBuffer.Length != length)
+ {
+ this._hissBuffer = UnsafeBuffer.Create(length, 4);
+ this._hissPtr = (float*)(void*)this._hissBuffer;
+ }
+ Utils.Memcpy(this._hissPtr, audio, length * 4);
+ this._hissFilter.Process(this._hissPtr, length);
+ for (int i = 0; i < this._hissBuffer.Length; i++)
+ {
+ this._noiseLevel = (1f - this._noiseAveragingRatio) * this._noiseLevel + this._noiseAveragingRatio * Math.Abs(this._hissPtr[i]);
+ if (this._noiseLevel > this._noiseThreshold)
+ {
+ audio[i] *= 1E-15f;
+ }
+ }
+ this._isSquelchOpen = (this._noiseLevel < this._noiseThreshold);
+ }
+ else
+ {
+ this._isSquelchOpen = true;
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/FmMode.cs b/SDRSharp.Radio/SDRSharp.Radio/FmMode.cs
new file mode 100644
index 0000000..115b00a
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/FmMode.cs
@@ -0,0 +1,8 @@
+namespace SDRSharp.Radio
+{
+ public enum FmMode
+ {
+ Narrow,
+ Wide
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/Fourier.cs b/SDRSharp.Radio/SDRSharp.Radio/Fourier.cs
new file mode 100644
index 0000000..686a2b1
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/Fourier.cs
@@ -0,0 +1,224 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SDRSharp.Radio
+{
+ public static class Fourier
+ {
+ private const int MaxLutBits = 16;
+
+ private const int MaxLutBins = 65536;
+
+ private const int LutSize = 32768;
+
+ private const double TwoPi = 6.2831853071795862;
+
+ private static UnsafeBuffer _lutBuffer;
+
+ private unsafe static Complex* _lut;
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private unsafe static extern void FourierForwardTransformLut(Complex* buffer, int length, Complex* lut, int lutbits);
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private unsafe static extern void FourierForwardTransformSinCos(Complex* buffer, int length);
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private unsafe static extern void FourierForwardTransformRotator(Complex* buffer, int length);
+
+ unsafe static Fourier()
+ {
+ Fourier._lutBuffer = UnsafeBuffer.Create(32768, sizeof(Complex));
+ Fourier._lut = (Complex*)(void*)Fourier._lutBuffer;
+ for (int i = 0; i < 32768; i++)
+ {
+ Fourier._lut[i] = Complex.FromAngle(9.5873799242852573E-05 * (double)i).Conjugate();
+ }
+ }
+
+ public static float DecibelToRatio(float db)
+ {
+ return (float)Math.Pow(10.0, (double)db * 0.1);
+ }
+
+ public unsafe static void SpectrumPower(Complex* buffer, float* power, int length, float offset = 0f)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ float num = buffer[i].ModulusSquared();
+ float num2 = power[i] = (float)(10.0 * Math.Log10(1E-60 + (double)num)) + offset;
+ }
+ }
+
+ public unsafe static void ScaleFFT(float* src, byte* dest, int length, float minPower, float maxPower)
+ {
+ float num = 255f / (maxPower - minPower);
+ for (int i = 0; i < length; i++)
+ {
+ float num2 = src[i];
+ if (num2 < minPower)
+ {
+ num2 = minPower;
+ }
+ else if (num2 > maxPower)
+ {
+ num2 = maxPower;
+ }
+ dest[i] = (byte)((num2 - minPower) * num);
+ }
+ }
+
+ public unsafe static void SmoothMaxCopy(float* srcPtr, float* dstPtr, int sourceLength, int destinationLength, float zoom = 1f, float offset = 0f)
+ {
+ if (zoom < 1f)
+ {
+ zoom = 1f;
+ }
+ float num = (float)sourceLength / (zoom * (float)destinationLength);
+ float num2 = (float)sourceLength * (offset + 0.5f * (1f - 1f / zoom));
+ if (num > 1f)
+ {
+ int num3 = (int)Math.Ceiling((double)num * 0.5);
+ int num4 = -1;
+ for (int i = 0; i < destinationLength; i++)
+ {
+ float num5 = -600f;
+ for (int j = -num3; j <= num3; j++)
+ {
+ int num6 = (int)Math.Round((double)(num2 + num * (float)i + (float)j));
+ if (num6 > num4 && num6 >= 0 && num6 < sourceLength && num5 < srcPtr[num6])
+ {
+ num5 = srcPtr[num6];
+ }
+ num4 = num6;
+ }
+ dstPtr[i] = num5;
+ }
+ }
+ else
+ {
+ int num7 = (int)Math.Ceiling((double)(1f / num));
+ float num8 = 1f / (float)num7;
+ int num9 = 0;
+ int num10 = (int)num2;
+ int num11 = num10 + 1;
+ int num12 = sourceLength - 1;
+ for (int k = 0; k < destinationLength; k++)
+ {
+ int num13 = (int)(num2 + (float)k * num);
+ if (num13 > num10)
+ {
+ num9 = 0;
+ if (num13 >= num12)
+ {
+ num10 = num12;
+ num11 = num12;
+ }
+ else
+ {
+ num10 = num13;
+ num11 = num13 + 1;
+ }
+ }
+ dstPtr[k] = (srcPtr[num10] * (float)(num7 - num9) + srcPtr[num11] * (float)num9) * num8;
+ num9++;
+ }
+ }
+ }
+
+ public unsafe static void SmoothMinCopy(float* srcPtr, float* dstPtr, int sourceLength, int destinationLength, float zoom = 1f, float offset = 0f)
+ {
+ if (zoom < 1f)
+ {
+ zoom = 1f;
+ }
+ float num = (float)sourceLength / (zoom * (float)destinationLength);
+ float num2 = (float)sourceLength * (offset + 0.5f * (1f - 1f / zoom));
+ if (num > 1f)
+ {
+ int num3 = (int)Math.Ceiling((double)num * 0.5);
+ int num4 = -1;
+ for (int i = 0; i < destinationLength; i++)
+ {
+ float num5 = 600f;
+ for (int j = -num3; j <= num3; j++)
+ {
+ int num6 = (int)Math.Round((double)(num2 + num * (float)i + (float)j));
+ if (num6 > num4 && num6 >= 0 && num6 < sourceLength && num5 > srcPtr[num6])
+ {
+ num5 = srcPtr[num6];
+ }
+ num4 = num6;
+ }
+ dstPtr[i] = num5;
+ }
+ }
+ else
+ {
+ for (int k = 0; k < destinationLength; k++)
+ {
+ int num7 = (int)(num * (float)k + num2);
+ if (num7 >= 0 && num7 < sourceLength)
+ {
+ dstPtr[k] = srcPtr[num7];
+ }
+ }
+ }
+ }
+
+ public unsafe static void ApplyFFTWindow(Complex* buffer, float* window, int length)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ buffer[i].Real *= window[i];
+ buffer[i].Imag *= window[i];
+ }
+ }
+
+ public unsafe static void ForwardTransform(Complex* buffer, int length, bool rearrange = true)
+ {
+ if (length <= 128)
+ {
+ Fourier.FourierForwardTransformRotator(buffer, length);
+ }
+ else if (length <= 65536)
+ {
+ Fourier.FourierForwardTransformLut(buffer, length, Fourier._lut, 16);
+ }
+ else if (rearrange)
+ {
+ Fourier.FourierForwardTransformRotator(buffer, length);
+ }
+ else
+ {
+ Fourier.FourierForwardTransformSinCos(buffer, length);
+ }
+ if (rearrange)
+ {
+ int num = length / 2;
+ for (int i = 0; i < num; i++)
+ {
+ int num2 = num + i;
+ Complex complex = buffer[i];
+ buffer[i] = buffer[num2];
+ buffer[num2] = complex;
+ }
+ }
+ }
+
+ public unsafe static void InverseTransform(Complex* samples, int length)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ samples[i].Imag = 0f - samples[i].Imag;
+ }
+ Fourier.ForwardTransform(samples, length, false);
+ float num = 1f / (float)length;
+ for (int j = 0; j < length; j++)
+ {
+ samples[j].Real *= num;
+ samples[j].Imag = (0f - samples[j].Imag) * num;
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/FrequencyTranslator.cs b/SDRSharp.Radio/SDRSharp.Radio/FrequencyTranslator.cs
new file mode 100644
index 0000000..aa9e05b
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/FrequencyTranslator.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SDRSharp.Radio
+{
+ public class FrequencyTranslator : IDisposable
+ {
+ private IntPtr _nco;
+
+ private double _sampleRate;
+
+ private double _frequency;
+
+ public double SampleRate
+ {
+ get
+ {
+ return this._sampleRate;
+ }
+ }
+
+ public double Frequency
+ {
+ get
+ {
+ return this._frequency;
+ }
+ set
+ {
+ if (this._frequency != value)
+ {
+ this._frequency = value;
+ FrequencyTranslator.nco_tune(this._nco, this._frequency);
+ }
+ }
+ }
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr nco_create(double sample_rate);
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private static extern void nco_destroy(IntPtr instance);
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private static extern void nco_tune(IntPtr instance, double frequency);
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private unsafe static extern void nco_process(IntPtr instance, Complex* buffer, int length);
+
+ public FrequencyTranslator(double sampleRate)
+ {
+ this._sampleRate = sampleRate;
+ this._nco = FrequencyTranslator.nco_create(sampleRate);
+ }
+
+ public void Dispose()
+ {
+ if (this._nco != IntPtr.Zero)
+ {
+ FrequencyTranslator.nco_destroy(this._nco);
+ this._nco = IntPtr.Zero;
+ }
+ }
+
+ public unsafe void Process(Complex* buffer, int length)
+ {
+ FrequencyTranslator.nco_process(this._nco, buffer, length);
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/HookManager.cs b/SDRSharp.Radio/SDRSharp.Radio/HookManager.cs
new file mode 100644
index 0000000..957623c
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/HookManager.cs
@@ -0,0 +1,221 @@
+using System.Collections.Generic;
+
+namespace SDRSharp.Radio
+{
+ public class HookManager
+ {
+ private readonly List _filteredAudioProcessors = new List();
+
+ private readonly List _demodulatorOutputProcessors = new List();
+
+ private readonly List _rawIQProcessors = new List();
+
+ private readonly List _decimatedAndFilteredIQProcessors = new List();
+
+ private readonly List _fmMPXProcessors = new List();
+
+ private readonly List _rdsBitStreamProcessors = new List();
+
+ public void RegisterStreamHook(object hook, ProcessorType processorType)
+ {
+ switch (processorType)
+ {
+ case ProcessorType.RawIQ:
+ lock (this._rawIQProcessors)
+ {
+ this._rawIQProcessors.Add((IIQProcessor)hook);
+ }
+ break;
+ case ProcessorType.DecimatedAndFilteredIQ:
+ lock (this._decimatedAndFilteredIQProcessors)
+ {
+ this._decimatedAndFilteredIQProcessors.Add((IIQProcessor)hook);
+ }
+ break;
+ case ProcessorType.DemodulatorOutput:
+ lock (this._demodulatorOutputProcessors)
+ {
+ this._demodulatorOutputProcessors.Add((IRealProcessor)hook);
+ }
+ break;
+ case ProcessorType.FilteredAudioOutput:
+ lock (this._filteredAudioProcessors)
+ {
+ this._filteredAudioProcessors.Add((IRealProcessor)hook);
+ }
+ break;
+ case ProcessorType.FMMPX:
+ lock (this._fmMPXProcessors)
+ {
+ this._fmMPXProcessors.Add((IRealProcessor)hook);
+ }
+ break;
+ case ProcessorType.RDSBitStream:
+ lock (this._rdsBitStreamProcessors)
+ {
+ this._rdsBitStreamProcessors.Add((IRdsBitStreamProcessor)hook);
+ }
+ break;
+ }
+ }
+
+ public void UnregisterStreamHook(object hook)
+ {
+ if (hook != null)
+ {
+ if (hook is IIQProcessor)
+ {
+ IIQProcessor item = (IIQProcessor)hook;
+ lock (this._rawIQProcessors)
+ {
+ this._rawIQProcessors.Remove(item);
+ }
+ lock (this._decimatedAndFilteredIQProcessors)
+ {
+ this._decimatedAndFilteredIQProcessors.Remove(item);
+ }
+ }
+ if (hook is IRealProcessor)
+ {
+ IRealProcessor item2 = (IRealProcessor)hook;
+ lock (this._demodulatorOutputProcessors)
+ {
+ this._demodulatorOutputProcessors.Remove(item2);
+ }
+ lock (this._filteredAudioProcessors)
+ {
+ this._filteredAudioProcessors.Remove(item2);
+ }
+ lock (this._fmMPXProcessors)
+ {
+ this._fmMPXProcessors.Remove(item2);
+ }
+ }
+ if (hook is IRdsBitStreamProcessor)
+ {
+ IRdsBitStreamProcessor item3 = (IRdsBitStreamProcessor)hook;
+ lock (this._rdsBitStreamProcessors)
+ {
+ this._rdsBitStreamProcessors.Remove(item3);
+ }
+ }
+ }
+ }
+
+ public void SetProcessorSampleRate(ProcessorType processorType, double sampleRate)
+ {
+ switch (processorType)
+ {
+ case ProcessorType.RawIQ:
+ this.SetSampleRate(this._rawIQProcessors, sampleRate);
+ break;
+ case ProcessorType.DecimatedAndFilteredIQ:
+ this.SetSampleRate(this._decimatedAndFilteredIQProcessors, sampleRate);
+ break;
+ case ProcessorType.DemodulatorOutput:
+ this.SetSampleRate(this._demodulatorOutputProcessors, sampleRate);
+ break;
+ case ProcessorType.FilteredAudioOutput:
+ this.SetSampleRate(this._filteredAudioProcessors, sampleRate);
+ break;
+ case ProcessorType.FMMPX:
+ this.SetSampleRate(this._fmMPXProcessors, sampleRate);
+ break;
+ }
+ }
+
+ public unsafe void ProcessRawIQ(Complex* buffer, int length)
+ {
+ this.ProcessHooks(this._rawIQProcessors, buffer, length);
+ }
+
+ public unsafe void ProcessDecimatedAndFilteredIQ(Complex* buffer, int length)
+ {
+ this.ProcessHooks(this._decimatedAndFilteredIQProcessors, buffer, length);
+ }
+
+ public unsafe void ProcessDemodulatorOutput(float* buffer, int length)
+ {
+ this.ProcessHooks(this._demodulatorOutputProcessors, buffer, length);
+ }
+
+ public unsafe void ProcessFilteredAudioOutput(float* buffer, int length)
+ {
+ this.ProcessHooks(this._filteredAudioProcessors, buffer, length);
+ }
+
+ public unsafe void ProcessFMMPX(float* buffer, int length)
+ {
+ this.ProcessHooks(this._fmMPXProcessors, buffer, length);
+ }
+
+ public void ProcessRdsBitStream(ref RdsFrame frame)
+ {
+ this.ProcessHooks(this._rdsBitStreamProcessors, ref frame);
+ }
+
+ private void SetSampleRate(List processors, double sampleRate)
+ {
+ lock (processors)
+ {
+ for (int i = 0; i < processors.Count; i++)
+ {
+ processors[i].SampleRate = sampleRate;
+ }
+ }
+ }
+
+ private void SetSampleRate(List processors, double sampleRate)
+ {
+ lock (processors)
+ {
+ for (int i = 0; i < processors.Count; i++)
+ {
+ processors[i].SampleRate = sampleRate;
+ }
+ }
+ }
+
+ private unsafe void ProcessHooks(List processors, Complex* buffer, int length)
+ {
+ lock (processors)
+ {
+ for (int i = 0; i < processors.Count; i++)
+ {
+ if (processors[i].Enabled)
+ {
+ processors[i].Process(buffer, length);
+ }
+ }
+ }
+ }
+
+ private unsafe void ProcessHooks(List processors, float* buffer, int length)
+ {
+ lock (processors)
+ {
+ for (int i = 0; i < processors.Count; i++)
+ {
+ if (processors[i].Enabled)
+ {
+ processors[i].Process(buffer, length);
+ }
+ }
+ }
+ }
+
+ private void ProcessHooks(List processors, ref RdsFrame frame)
+ {
+ lock (processors)
+ {
+ for (int i = 0; i < processors.Count; i++)
+ {
+ if (processors[i].Enabled)
+ {
+ processors[i].Process(ref frame);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/IBaseProcessor.cs b/SDRSharp.Radio/SDRSharp.Radio/IBaseProcessor.cs
new file mode 100644
index 0000000..169183a
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/IBaseProcessor.cs
@@ -0,0 +1,11 @@
+namespace SDRSharp.Radio
+{
+ public interface IBaseProcessor
+ {
+ bool Enabled
+ {
+ get;
+ set;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/IConfigurationPanelProvider.cs b/SDRSharp.Radio/SDRSharp.Radio/IConfigurationPanelProvider.cs
new file mode 100644
index 0000000..37999c2
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/IConfigurationPanelProvider.cs
@@ -0,0 +1,12 @@
+using System.Windows.Forms;
+
+namespace SDRSharp.Radio
+{
+ public interface IConfigurationPanelProvider
+ {
+ UserControl Gui
+ {
+ get;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/IConnectableSource.cs b/SDRSharp.Radio/SDRSharp.Radio/IConnectableSource.cs
new file mode 100644
index 0000000..6f08e55
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/IConnectableSource.cs
@@ -0,0 +1,14 @@
+namespace SDRSharp.Radio
+{
+ public interface IConnectableSource
+ {
+ bool Connected
+ {
+ get;
+ }
+
+ void Connect();
+
+ void Disconnect();
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/IControlAwareObject.cs b/SDRSharp.Radio/SDRSharp.Radio/IControlAwareObject.cs
new file mode 100644
index 0000000..d6bcd3d
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/IControlAwareObject.cs
@@ -0,0 +1,7 @@
+namespace SDRSharp.Radio
+{
+ public interface IControlAwareObject
+ {
+ void SetControl(object control);
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/IFloatingConfigDialogProvider.cs b/SDRSharp.Radio/SDRSharp.Radio/IFloatingConfigDialogProvider.cs
new file mode 100644
index 0000000..f55414a
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/IFloatingConfigDialogProvider.cs
@@ -0,0 +1,11 @@
+using System.Windows.Forms;
+
+namespace SDRSharp.Radio
+{
+ public interface IFloatingConfigDialogProvider
+ {
+ void ShowSettingGUI(IWin32Window parent);
+
+ void HideSettingGUI();
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/IFrontendController.cs b/SDRSharp.Radio/SDRSharp.Radio/IFrontendController.cs
new file mode 100644
index 0000000..230a738
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/IFrontendController.cs
@@ -0,0 +1,9 @@
+namespace SDRSharp.Radio
+{
+ public interface IFrontendController
+ {
+ void Open();
+
+ void Close();
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/IFrontendOffset.cs b/SDRSharp.Radio/SDRSharp.Radio/IFrontendOffset.cs
new file mode 100644
index 0000000..8683d7f
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/IFrontendOffset.cs
@@ -0,0 +1,10 @@
+namespace SDRSharp.Radio
+{
+ public interface IFrontendOffset
+ {
+ int Offset
+ {
+ get;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/IIQProcessor.cs b/SDRSharp.Radio/SDRSharp.Radio/IIQProcessor.cs
new file mode 100644
index 0000000..68efc90
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/IIQProcessor.cs
@@ -0,0 +1,7 @@
+namespace SDRSharp.Radio
+{
+ public interface IIQProcessor : IStreamProcessor, IBaseProcessor
+ {
+ unsafe void Process(Complex* buffer, int length);
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/IIQStreamController.cs b/SDRSharp.Radio/SDRSharp.Radio/IIQStreamController.cs
new file mode 100644
index 0000000..2be8804
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/IIQStreamController.cs
@@ -0,0 +1,14 @@
+namespace SDRSharp.Radio
+{
+ public interface IIQStreamController
+ {
+ double Samplerate
+ {
+ get;
+ }
+
+ void Start(SamplesAvailableDelegate callback);
+
+ void Stop();
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/INonBlockingController.cs b/SDRSharp.Radio/SDRSharp.Radio/INonBlockingController.cs
new file mode 100644
index 0000000..aab0f53
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/INonBlockingController.cs
@@ -0,0 +1,6 @@
+namespace SDRSharp.Radio
+{
+ public interface INonBlockingController
+ {
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/IQBalancer.cs b/SDRSharp.Radio/SDRSharp.Radio/IQBalancer.cs
new file mode 100644
index 0000000..16c78ce
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/IQBalancer.cs
@@ -0,0 +1,242 @@
+using System;
+
+namespace SDRSharp.Radio
+{
+ public class IQBalancer
+ {
+ private const int FFTBins = 128;
+
+ private const int SkippedBuffers = 5;
+
+ private const float DcTimeConst = 1E-05f;
+
+ private const float MaximumStep = 0.01f;
+
+ private const float MinimumStep = 1E-05f;
+
+ private const float StepIncrement = 1.1f;
+
+ private const float StepDecrement = 0.9090909f;
+
+ private const float MaxPhaseCorrection = 0.2f;
+
+ private const float PhaseAlpha = 0.01f;
+
+ private const float GainAlpha = 0.01f;
+
+ private bool _enabled;
+
+ private float _phase;
+
+ private float _lastPhase;
+
+ private float _step = 1E-05f;
+
+ private float _stepFactor = 0.9090909f;
+
+ private double _gain;
+
+ private double _iampavg;
+
+ private double _qampavg;
+
+ private unsafe readonly DcRemover* _dcRemoverI;
+
+ private readonly UnsafeBuffer _dcRemoverIBuffer;
+
+ private unsafe readonly DcRemover* _dcRemoverQ;
+
+ private readonly UnsafeBuffer _dcRemoverQBuffer;
+
+ private readonly bool _isMultithreaded;
+
+ private readonly SharpEvent _event = new SharpEvent(false);
+
+ private unsafe static readonly float* _windowPtr;
+
+ private static readonly UnsafeBuffer _windowBuffer;
+
+ public float Phase
+ {
+ get
+ {
+ return (float)Math.Asin((double)this._phase);
+ }
+ }
+
+ public float Gain
+ {
+ get
+ {
+ return (float)this._gain;
+ }
+ }
+
+ public bool Enabled
+ {
+ get
+ {
+ return this._enabled;
+ }
+ set
+ {
+ this._enabled = value;
+ }
+ }
+
+ public unsafe IQBalancer()
+ {
+ this._dcRemoverIBuffer = UnsafeBuffer.Create(sizeof(DcRemover));
+ this._dcRemoverI = (DcRemover*)(void*)this._dcRemoverIBuffer;
+ this._dcRemoverI->Init(1E-05f);
+ this._dcRemoverQBuffer = UnsafeBuffer.Create(sizeof(DcRemover));
+ this._dcRemoverQ = (DcRemover*)(void*)this._dcRemoverQBuffer;
+ this._dcRemoverQ->Init(1E-05f);
+ this._isMultithreaded = (Environment.ProcessorCount > 1);
+ }
+
+ unsafe static IQBalancer()
+ {
+ IQBalancer._windowBuffer = UnsafeBuffer.Create(FilterBuilder.MakeWindow(WindowType.BlackmanHarris7, 128));
+ IQBalancer._windowPtr = (float*)(void*)IQBalancer._windowBuffer;
+ }
+
+ public unsafe void Reset()
+ {
+ this._phase = 0f;
+ this._lastPhase = 0f;
+ this._gain = 1.0;
+ this._step = 1E-05f;
+ this._stepFactor = 1.1f;
+ this._iampavg = 1.0;
+ this._qampavg = 1.0;
+ this._dcRemoverI->Reset();
+ this._dcRemoverQ->Reset();
+ }
+
+ public unsafe void Process(Complex* iq, int length)
+ {
+ if (this._enabled)
+ {
+ this.RemoveDC(iq, length);
+ int num = 0;
+ while (length >= 128)
+ {
+ if (num % 5 == 0)
+ {
+ this.EstimatePhaseImbalance(iq);
+ }
+ this.Adjust(iq, 128);
+ iq += 128;
+ length -= 128;
+ num++;
+ }
+ this.Adjust(iq, length);
+ }
+ }
+
+ private unsafe void RemoveDC(Complex* iq, int length)
+ {
+ float* buffer = (float*)((byte*)iq + 4);
+ if (this._isMultithreaded)
+ {
+ DSPThreadPool.QueueUserWorkItem(delegate
+ {
+ this._dcRemoverI->ProcessInterleaved((float*)iq, length);
+ this._event.Set();
+ });
+ }
+ else
+ {
+ this._dcRemoverI->ProcessInterleaved((float*)iq, length);
+ }
+ this._dcRemoverQ->ProcessInterleaved(buffer, length);
+ if (this._isMultithreaded)
+ {
+ this._event.WaitOne();
+ }
+ }
+
+ private unsafe void EstimatePhaseImbalance(Complex* iq)
+ {
+ float num = this.Utility(iq, this._phase);
+ float num2 = this._phase + this._step;
+ if (num2 > 0.2f)
+ {
+ num2 = 0.2f;
+ }
+ if (num2 < -0.2f)
+ {
+ num2 = -0.2f;
+ }
+ if (this.Utility(iq, num2) > num)
+ {
+ this._phase += 0.01f * (num2 - this._phase);
+ }
+ else
+ {
+ if (Math.Abs(this._step) < 1E-05f)
+ {
+ this._stepFactor = -1.1f;
+ }
+ else if (Math.Abs(this._step) > 0.01f)
+ {
+ this._stepFactor = -0.9090909f;
+ }
+ this._step *= this._stepFactor;
+ }
+ }
+
+ private unsafe float Utility(Complex* iq, float phase)
+ {
+ Complex* ptr = stackalloc Complex[128 * sizeof(Complex)];
+ Utils.Memcpy(ptr, iq, 128 * sizeof(Complex));
+ this.AdjustPhase(ptr, phase);
+ Fourier.ApplyFFTWindow(ptr, IQBalancer._windowPtr, 128);
+ Fourier.ForwardTransform(ptr, 128, false);
+ float num = 0f;
+ int num2 = 1;
+ int num3 = 127;
+ while (num2 < 64)
+ {
+ float num4 = Math.Abs(ptr[num2].Real) + Math.Abs(ptr[num2].Imag);
+ float num5 = Math.Abs(ptr[num3].Real) + Math.Abs(ptr[num3].Imag);
+ num += Math.Abs(num4 - num5);
+ num2++;
+ num3--;
+ }
+ return num;
+ }
+
+ private unsafe void AdjustPhase(Complex* iq, float phase)
+ {
+ float num = (float)this._gain;
+ for (int i = 0; i < 128; i++)
+ {
+ iq[i].Real += phase * iq[i].Imag;
+ iq[i].Imag *= num;
+ }
+ }
+
+ private unsafe void Adjust(Complex* iq, int length)
+ {
+ float num = 1f / (float)(length - 1);
+ for (int i = 0; i < length; i++)
+ {
+ float num2 = ((float)i * this._lastPhase + (float)(length - 1 - i) * this._phase) * num;
+ iq[i].Real += num2 * iq[i].Imag;
+ float num3 = iq[i].Real * iq[i].Real;
+ float num4 = iq[i].Imag * iq[i].Imag;
+ this._iampavg += 9.9999997473787516E-06 * ((double)num3 - this._iampavg);
+ this._qampavg += 9.9999997473787516E-06 * ((double)num4 - this._qampavg);
+ if (this._qampavg != 0.0)
+ {
+ double num5 = Math.Sqrt(this._iampavg / this._qampavg);
+ this._gain += 0.0099999997764825821 * (num5 - this._gain);
+ }
+ iq[i].Imag *= (float)this._gain;
+ }
+ this._lastPhase = this._phase;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/IQFirFilter.cs b/SDRSharp.Radio/SDRSharp.Radio/IQFirFilter.cs
new file mode 100644
index 0000000..e1e1239
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/IQFirFilter.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SDRSharp.Radio
+{
+ public sealed class IQFirFilter : IDisposable
+ {
+ private int _decimationRatio;
+
+ private int _length;
+
+ private IntPtr _fir;
+
+ public int Length
+ {
+ get
+ {
+ return this._length;
+ }
+ }
+
+ public int DecimationRatio
+ {
+ get
+ {
+ return this._decimationRatio;
+ }
+ }
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private unsafe static extern IntPtr complex_fir_create(float* kernel, int length, int decimation);
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private static extern void complex_fir_destroy(IntPtr instance);
+
+ [DllImport("shark", CallingConvention = CallingConvention.Cdecl)]
+ private unsafe static extern int complex_fir_process(IntPtr instance, Complex* buffer, int length);
+
+ public unsafe IQFirFilter(float[] coefficients, int decimationRatio = 1)
+ {
+ if (decimationRatio <= 0)
+ {
+ throw new ArgumentException("The decimation factor must be greater than zero", "decimationRatio");
+ }
+ this._decimationRatio = decimationRatio;
+ this._length = coefficients.Length;
+ fixed (float* kernel = coefficients)
+ {
+ this._fir = IQFirFilter.complex_fir_create(kernel, this._length, decimationRatio);
+ }
+ }
+
+ public void Dispose()
+ {
+ if (this._fir != IntPtr.Zero)
+ {
+ IQFirFilter.complex_fir_destroy(this._fir);
+ this._fir = IntPtr.Zero;
+ }
+ GC.SuppressFinalize(this);
+ }
+
+ public unsafe int Process(Complex* buffer, int length)
+ {
+ return IQFirFilter.complex_fir_process(this._fir, buffer, length);
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/IRdsBitStreamProcessor.cs b/SDRSharp.Radio/SDRSharp.Radio/IRdsBitStreamProcessor.cs
new file mode 100644
index 0000000..36d5f43
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/IRdsBitStreamProcessor.cs
@@ -0,0 +1,7 @@
+namespace SDRSharp.Radio
+{
+ public interface IRdsBitStreamProcessor : IBaseProcessor
+ {
+ void Process(ref RdsFrame frame);
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/IRealProcessor.cs b/SDRSharp.Radio/SDRSharp.Radio/IRealProcessor.cs
new file mode 100644
index 0000000..202b29c
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/IRealProcessor.cs
@@ -0,0 +1,7 @@
+namespace SDRSharp.Radio
+{
+ public interface IRealProcessor : IStreamProcessor, IBaseProcessor
+ {
+ unsafe void Process(float* buffer, int length);
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/ISampleRateChangeSource.cs b/SDRSharp.Radio/SDRSharp.Radio/ISampleRateChangeSource.cs
new file mode 100644
index 0000000..3392071
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/ISampleRateChangeSource.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace SDRSharp.Radio
+{
+ public interface ISampleRateChangeSource
+ {
+ event EventHandler SampleRateChanged;
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/ISoundcardController.cs b/SDRSharp.Radio/SDRSharp.Radio/ISoundcardController.cs
new file mode 100644
index 0000000..1f07a2b
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/ISoundcardController.cs
@@ -0,0 +1,15 @@
+namespace SDRSharp.Radio
+{
+ public interface ISoundcardController
+ {
+ string SoundCardHint
+ {
+ get;
+ }
+
+ double SampleRateHint
+ {
+ get;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/ISpectrumProvider.cs b/SDRSharp.Radio/SDRSharp.Radio/ISpectrumProvider.cs
new file mode 100644
index 0000000..dc1ed0f
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/ISpectrumProvider.cs
@@ -0,0 +1,10 @@
+namespace SDRSharp.Radio
+{
+ public interface ISpectrumProvider
+ {
+ float UsableSpectrumRatio
+ {
+ get;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/IStreamProcessor.cs b/SDRSharp.Radio/SDRSharp.Radio/IStreamProcessor.cs
new file mode 100644
index 0000000..8d89cd1
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/IStreamProcessor.cs
@@ -0,0 +1,10 @@
+namespace SDRSharp.Radio
+{
+ public interface IStreamProcessor : IBaseProcessor
+ {
+ double SampleRate
+ {
+ set;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/ITunableSource.cs b/SDRSharp.Radio/SDRSharp.Radio/ITunableSource.cs
new file mode 100644
index 0000000..bada880
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/ITunableSource.cs
@@ -0,0 +1,26 @@
+namespace SDRSharp.Radio
+{
+ public interface ITunableSource
+ {
+ bool CanTune
+ {
+ get;
+ }
+
+ long Frequency
+ {
+ get;
+ set;
+ }
+
+ long MinimumTunableFrequency
+ {
+ get;
+ }
+
+ long MaximumTunableFrequency
+ {
+ get;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/IirFilter.cs b/SDRSharp.Radio/SDRSharp.Radio/IirFilter.cs
new file mode 100644
index 0000000..cd2977a
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/IirFilter.cs
@@ -0,0 +1,105 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SDRSharp.Radio
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 16)]
+ public struct IirFilter
+ {
+ private float _a0;
+
+ private float _a1;
+
+ private float _a2;
+
+ private float _b0;
+
+ private float _b1;
+
+ private float _b2;
+
+ private float _x1;
+
+ private float _x2;
+
+ private float _y1;
+
+ private float _y2;
+
+ public void Init(IirFilterType filterType, double frequency, double sampleRate, double qualityFactor)
+ {
+ double num = 6.2831853071795862 * frequency / sampleRate;
+ double num2 = Math.Sin(num) / (2.0 * qualityFactor);
+ switch (filterType)
+ {
+ case IirFilterType.LowPass:
+ this._b0 = (float)((1.0 - Math.Cos(num)) / 2.0);
+ this._b1 = (float)(1.0 - Math.Cos(num));
+ this._b2 = (float)((1.0 - Math.Cos(num)) / 2.0);
+ this._a0 = (float)(1.0 + num2);
+ this._a1 = (float)(-2.0 * Math.Cos(num));
+ this._a2 = (float)(1.0 - num2);
+ break;
+ case IirFilterType.HighPass:
+ this._b0 = (float)((1.0 + Math.Cos(num)) / 2.0);
+ this._b1 = (float)(0.0 - (1.0 + Math.Cos(num)));
+ this._b2 = (float)((1.0 + Math.Cos(num)) / 2.0);
+ this._a0 = (float)(1.0 + num2);
+ this._a1 = (float)(-2.0 * Math.Cos(num));
+ this._a2 = (float)(1.0 - num2);
+ break;
+ default:
+ this._b0 = (float)num2;
+ this._b1 = 0f;
+ this._b2 = (float)(0.0 - num2);
+ this._a0 = (float)(1.0 + num2);
+ this._a1 = (float)(-2.0 * Math.Cos(num));
+ this._a2 = (float)(1.0 - num2);
+ break;
+ case IirFilterType.Notch:
+ this._b0 = 1f;
+ this._b1 = (float)(-2.0 * Math.Cos(num));
+ this._b2 = 1f;
+ this._a0 = (float)(1.0 + num2);
+ this._a1 = (float)(-2.0 * Math.Cos(num));
+ this._a2 = (float)(1.0 - num2);
+ break;
+ }
+ this._b0 /= this._a0;
+ this._b1 /= this._a0;
+ this._b2 /= this._a0;
+ this._a1 /= this._a0;
+ this._a2 /= this._a0;
+ this._x1 = 0f;
+ this._x2 = 0f;
+ this._y1 = 0f;
+ this._y2 = 0f;
+ }
+
+ public void Reset()
+ {
+ this._x1 = 0f;
+ this._x2 = 0f;
+ this._y1 = 0f;
+ this._y2 = 0f;
+ }
+
+ public float Process(float sample)
+ {
+ float num = this._b0 * sample + this._b1 * this._x1 + this._b2 * this._x2 - this._a1 * this._y1 - this._a2 * this._y2;
+ this._x2 = this._x1;
+ this._x1 = sample;
+ this._y2 = this._y1;
+ this._y1 = num;
+ return num;
+ }
+
+ public unsafe void Process(float* buffer, int length)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ buffer[i] = this.Process(buffer[i]);
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/IirFilterType.cs b/SDRSharp.Radio/SDRSharp.Radio/IirFilterType.cs
new file mode 100644
index 0000000..de3860b
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/IirFilterType.cs
@@ -0,0 +1,10 @@
+namespace SDRSharp.Radio
+{
+ public enum IirFilterType
+ {
+ LowPass,
+ HighPass,
+ BandPass,
+ Notch
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/Oscillator.cs b/SDRSharp.Radio/SDRSharp.Radio/Oscillator.cs
new file mode 100644
index 0000000..a4100b5
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/Oscillator.cs
@@ -0,0 +1,100 @@
+namespace SDRSharp.Radio
+{
+ public class Oscillator
+ {
+ private Complex _vector;
+
+ private Complex _rotation;
+
+ private double _sampleRate;
+
+ private double _frequency;
+
+ public double SampleRate
+ {
+ get
+ {
+ return this._sampleRate;
+ }
+ set
+ {
+ if (this._sampleRate != value)
+ {
+ this._sampleRate = value;
+ this.Configure();
+ }
+ }
+ }
+
+ public double Frequency
+ {
+ get
+ {
+ return this._frequency;
+ }
+ set
+ {
+ if (this._frequency != value)
+ {
+ this._frequency = value;
+ this.Configure();
+ }
+ }
+ }
+
+ public Complex Phase
+ {
+ get
+ {
+ return this._vector;
+ }
+ set
+ {
+ this._vector = value;
+ }
+ }
+
+ public float Real
+ {
+ get
+ {
+ return this._vector.Real;
+ }
+ set
+ {
+ this._vector.Real = value;
+ }
+ }
+
+ public float Imag
+ {
+ get
+ {
+ return this._vector.Imag;
+ }
+ set
+ {
+ this._vector.Imag = value;
+ }
+ }
+
+ private void Configure()
+ {
+ if (this._vector.Real == 0f && this._vector.Imag == 0f)
+ {
+ this._vector.Real = 1f;
+ }
+ if (this._sampleRate != 0.0)
+ {
+ double angle = 6.2831853071795862 * this._frequency / this._sampleRate;
+ this._rotation = Complex.FromAngle(angle);
+ }
+ }
+
+ public void Tick()
+ {
+ this._vector *= this._rotation;
+ this._vector = this._vector.NormalizeFast();
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/OverlapAddProcessor.cs b/SDRSharp.Radio/SDRSharp.Radio/OverlapAddProcessor.cs
new file mode 100644
index 0000000..739d0c6
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/OverlapAddProcessor.cs
@@ -0,0 +1,121 @@
+using System;
+
+namespace SDRSharp.Radio
+{
+ public abstract class OverlapAddProcessor
+ {
+ private readonly int _fftSize;
+
+ private readonly int _halfSize;
+
+ private int _inputPos;
+
+ private int _outputPos;
+
+ private UnsafeBuffer _fftBuffer;
+
+ private unsafe Complex* _fftPtr;
+
+ private UnsafeBuffer _queuepBuffer;
+
+ private unsafe Complex* _queuePtr;
+
+ private UnsafeBuffer _outputBuffer;
+
+ private unsafe Complex* _outputPtr;
+
+ private UnsafeBuffer _overlapBuffer;
+
+ private unsafe Complex* _overlapPtr;
+
+ private UnsafeBuffer _windowBuffer;
+
+ private unsafe float* _windowPtr;
+
+ public int FFTSize
+ {
+ get
+ {
+ return this._fftSize;
+ }
+ }
+
+ public unsafe OverlapAddProcessor(int fftSize)
+ {
+ this._fftSize = fftSize;
+ this._halfSize = this._fftSize / 2;
+ this._inputPos = this._halfSize;
+ this._queuepBuffer = UnsafeBuffer.Create(this._fftSize, sizeof(Complex));
+ this._queuePtr = (Complex*)(void*)this._queuepBuffer;
+ this._windowBuffer = UnsafeBuffer.Create(this._fftSize, 4);
+ this._windowPtr = (float*)(void*)this._windowBuffer;
+ this._fftBuffer = UnsafeBuffer.Create(this._fftSize, sizeof(Complex));
+ this._fftPtr = (Complex*)(void*)this._fftBuffer;
+ this._outputBuffer = UnsafeBuffer.Create(this._halfSize, sizeof(Complex));
+ this._outputPtr = (Complex*)(void*)this._outputBuffer;
+ this._overlapBuffer = UnsafeBuffer.Create(this._halfSize, sizeof(Complex));
+ this._overlapPtr = (Complex*)(void*)this._overlapBuffer;
+ double num = 1.5707963267948966 / (double)(this._halfSize - 1);
+ for (int i = 0; i < this._halfSize; i++)
+ {
+ double a = (double)i * num;
+ this._windowPtr[i] = (float)Math.Sin(a);
+ this._windowPtr[this._fftSize - 1 - i] = this._windowPtr[i];
+ }
+ }
+
+ public unsafe virtual void Process(Complex* buffer, int length)
+ {
+ while (length > 0)
+ {
+ int num = Math.Min(this._fftSize - this._inputPos, length);
+ Utils.Memcpy(this._queuePtr + this._inputPos, buffer, num * sizeof(Complex));
+ Utils.Memcpy(buffer, this._outputPtr + this._outputPos, num * sizeof(Complex));
+ buffer += num;
+ this._inputPos += num;
+ this._outputPos += num;
+ length -= num;
+ if (this._inputPos == this._fftSize)
+ {
+ this.OverlapAdd();
+ this._inputPos = this._halfSize;
+ this._outputPos = 0;
+ }
+ }
+ }
+
+ public unsafe virtual void Process(float* buffer, int length, int step = 1)
+ {
+ for (int i = 0; i < length; i += step)
+ {
+ this._queuePtr[this._inputPos++] = buffer[i];
+ buffer[i] = this._outputPtr[this._outputPos++].Real;
+ if (this._inputPos == this._fftSize)
+ {
+ this.OverlapAdd();
+ this._inputPos = this._halfSize;
+ this._outputPos = 0;
+ }
+ }
+ }
+
+ private unsafe void OverlapAdd()
+ {
+ for (int i = 0; i < this._fftSize; i++)
+ {
+ this._fftPtr[i] = this._queuePtr[i] * this._windowPtr[i];
+ }
+ Fourier.ForwardTransform(this._fftPtr, this._fftSize, false);
+ this.ProcessFft(this._fftPtr, this._fftSize);
+ Fourier.InverseTransform(this._fftPtr, this._fftSize);
+ for (int j = 0; j < this._halfSize; j++)
+ {
+ this._outputPtr[j] = this._overlapPtr[j] * this._windowPtr[this._halfSize + j] + this._fftPtr[j] * this._windowPtr[j];
+ }
+ Utils.Memcpy(this._overlapPtr, this._fftPtr + this._halfSize, this._halfSize * sizeof(Complex));
+ Utils.Memcpy(this._queuePtr, this._queuePtr + this._halfSize, this._halfSize * sizeof(Complex));
+ }
+
+ protected unsafe abstract void ProcessFft(Complex* buffer, int length);
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/OverlapCrossfadeProcessor.cs b/SDRSharp.Radio/SDRSharp.Radio/OverlapCrossfadeProcessor.cs
new file mode 100644
index 0000000..dfa5a2a
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/OverlapCrossfadeProcessor.cs
@@ -0,0 +1,130 @@
+using System;
+
+namespace SDRSharp.Radio
+{
+ public abstract class OverlapCrossfadeProcessor
+ {
+ private readonly int _fftSize;
+
+ private readonly int _halfSize;
+
+ private readonly int _outputSize;
+
+ private readonly int _crossFadingSize;
+
+ private int _inputPos;
+
+ private int _outputPos;
+
+ private UnsafeBuffer _fftBuffer;
+
+ private unsafe Complex* _fftPtr;
+
+ private UnsafeBuffer _queuepBuffer;
+
+ private unsafe Complex* _queuePtr;
+
+ private UnsafeBuffer _outputBuffer;
+
+ private unsafe Complex* _outputPtr;
+
+ private UnsafeBuffer _crossFadingBuffer;
+
+ private unsafe Complex* _crossFadingPtr;
+
+ private UnsafeBuffer _windowBuffer;
+
+ private unsafe float* _windowPtr;
+
+ public int FFTSize
+ {
+ get
+ {
+ return this._fftSize;
+ }
+ }
+
+ public unsafe OverlapCrossfadeProcessor(int fftSize, float crossFadingRatio = 0f)
+ {
+ this._fftSize = fftSize;
+ this._halfSize = this._fftSize / 2;
+ this._crossFadingSize = (int)((float)this._halfSize * crossFadingRatio);
+ this._outputSize = this._halfSize - this._crossFadingSize;
+ this._inputPos = this._halfSize + this._crossFadingSize;
+ this._queuepBuffer = UnsafeBuffer.Create(this._fftSize, sizeof(Complex));
+ this._queuePtr = (Complex*)(void*)this._queuepBuffer;
+ this._windowBuffer = UnsafeBuffer.Create(this._crossFadingSize, 4);
+ this._windowPtr = (float*)(void*)this._windowBuffer;
+ this._fftBuffer = UnsafeBuffer.Create(this._fftSize, sizeof(Complex));
+ this._fftPtr = (Complex*)(void*)this._fftBuffer;
+ this._outputBuffer = UnsafeBuffer.Create(this._outputSize, sizeof(Complex));
+ this._outputPtr = (Complex*)(void*)this._outputBuffer;
+ this._crossFadingBuffer = UnsafeBuffer.Create(this._crossFadingSize, sizeof(Complex));
+ this._crossFadingPtr = (Complex*)(void*)this._crossFadingBuffer;
+ double num = 1.5707963267948966 / (double)(this._crossFadingSize - 1);
+ for (int i = 0; i < this._crossFadingSize; i++)
+ {
+ double a = (double)i * num;
+ this._windowPtr[i] = (float)Math.Pow(Math.Sin(a), 2.0);
+ }
+ }
+
+ public unsafe virtual void Process(Complex* buffer, int length)
+ {
+ while (length > 0)
+ {
+ int num = Math.Min(this._fftSize - this._inputPos, length);
+ Utils.Memcpy(this._queuePtr + this._inputPos, buffer, num * sizeof(Complex));
+ Utils.Memcpy(buffer, this._outputPtr + this._outputPos, num * sizeof(Complex));
+ buffer += num;
+ this._inputPos += num;
+ this._outputPos += num;
+ length -= num;
+ if (this._inputPos == this._fftSize)
+ {
+ this.OverlapCrossfade();
+ this._inputPos = this._halfSize + this._crossFadingSize;
+ this._outputPos = 0;
+ }
+ }
+ }
+
+ public unsafe virtual void Process(float* buffer, int length, int step = 1)
+ {
+ for (int i = 0; i < length; i += step)
+ {
+ this._queuePtr[this._inputPos++] = buffer[i];
+ buffer[i] = this._outputPtr[this._outputPos++].Real;
+ if (this._inputPos == this._fftSize)
+ {
+ this.OverlapCrossfade();
+ this._inputPos = this._halfSize + this._crossFadingSize;
+ this._outputPos = 0;
+ }
+ }
+ }
+
+ private unsafe void OverlapCrossfade()
+ {
+ Utils.Memcpy(this._fftPtr, this._queuePtr, this._fftSize * sizeof(Complex));
+ Fourier.ForwardTransform(this._fftPtr, this._fftSize, false);
+ this.ProcessFft(this._fftPtr, this._fftSize);
+ Fourier.InverseTransform(this._fftPtr, this._fftSize);
+ int num = 0;
+ int num2 = this._crossFadingSize - 1;
+ int num3 = this._halfSize;
+ while (num < this._crossFadingSize)
+ {
+ this._outputPtr[num] = this._fftPtr[num3] * this._windowPtr[num] + this._crossFadingPtr[num] * this._windowPtr[num2];
+ num++;
+ num2--;
+ num3++;
+ }
+ Utils.Memcpy(this._outputPtr + this._crossFadingSize, this._fftPtr + this._halfSize + this._crossFadingSize, (this._outputSize - this._crossFadingSize) * sizeof(Complex));
+ Utils.Memcpy(this._crossFadingPtr, this._fftPtr + this._halfSize + this._outputSize, this._crossFadingSize * sizeof(Complex));
+ Utils.Memcpy(this._queuePtr, this._queuePtr + this._outputSize, (this._fftSize - this._outputSize) * sizeof(Complex));
+ }
+
+ protected unsafe abstract void ProcessFft(Complex* buffer, int length);
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/OverlapSaveProcessor.cs b/SDRSharp.Radio/SDRSharp.Radio/OverlapSaveProcessor.cs
new file mode 100644
index 0000000..0eba2d2
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/OverlapSaveProcessor.cs
@@ -0,0 +1,95 @@
+using System;
+
+namespace SDRSharp.Radio
+{
+ public abstract class OverlapSaveProcessor
+ {
+ private readonly int _fftSize;
+
+ private readonly int _halfSize;
+
+ private int _inputPos;
+
+ private int _outputPos;
+
+ private UnsafeBuffer _fftBuffer;
+
+ private unsafe Complex* _fftPtr;
+
+ private UnsafeBuffer _queuepBuffer;
+
+ private unsafe Complex* _queuePtr;
+
+ private UnsafeBuffer _outputBuffer;
+
+ private unsafe Complex* _outputPtr;
+
+ public int FFTSize
+ {
+ get
+ {
+ return this._fftSize;
+ }
+ }
+
+ public unsafe OverlapSaveProcessor(int fftSize)
+ {
+ this._fftSize = fftSize;
+ this._halfSize = this._fftSize / 2;
+ this._inputPos = this._halfSize;
+ this._queuepBuffer = UnsafeBuffer.Create(this._fftSize, sizeof(Complex));
+ this._queuePtr = (Complex*)(void*)this._queuepBuffer;
+ this._fftBuffer = UnsafeBuffer.Create(this._fftSize, sizeof(Complex));
+ this._fftPtr = (Complex*)(void*)this._fftBuffer;
+ this._outputBuffer = UnsafeBuffer.Create(this._halfSize, sizeof(Complex));
+ this._outputPtr = (Complex*)(void*)this._outputBuffer;
+ }
+
+ public unsafe virtual void Process(Complex* buffer, int length)
+ {
+ while (length > 0)
+ {
+ int num = Math.Min(this._fftSize - this._inputPos, length);
+ Utils.Memcpy(this._queuePtr + this._inputPos, buffer, num * sizeof(Complex));
+ Utils.Memcpy(buffer, this._outputPtr + this._outputPos, num * sizeof(Complex));
+ buffer += num;
+ this._inputPos += num;
+ this._outputPos += num;
+ length -= num;
+ if (this._inputPos == this._fftSize)
+ {
+ this.OverlapSave();
+ this._inputPos = this._halfSize;
+ this._outputPos = 0;
+ }
+ }
+ }
+
+ public unsafe virtual void Process(float* buffer, int length, int step = 1)
+ {
+ for (int i = 0; i < length; i += step)
+ {
+ this._queuePtr[this._inputPos++] = buffer[i];
+ buffer[i] = this._outputPtr[this._outputPos++].Real;
+ if (this._inputPos == this._fftSize)
+ {
+ this.OverlapSave();
+ this._inputPos = this._halfSize;
+ this._outputPos = 0;
+ }
+ }
+ }
+
+ private unsafe void OverlapSave()
+ {
+ Utils.Memcpy(this._fftPtr, this._queuePtr, this._fftSize * sizeof(Complex));
+ Fourier.ForwardTransform(this._fftPtr, this._fftSize, false);
+ this.ProcessFft(this._fftPtr, this._fftSize);
+ Fourier.InverseTransform(this._fftPtr, this._fftSize);
+ Utils.Memcpy(this._outputPtr, this._fftPtr + this._halfSize, this._halfSize * sizeof(Complex));
+ Utils.Memcpy(this._queuePtr, this._queuePtr + this._halfSize, this._halfSize * sizeof(Complex));
+ }
+
+ protected unsafe abstract void ProcessFft(Complex* buffer, int length);
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/OverlapSlideProcessor.cs b/SDRSharp.Radio/SDRSharp.Radio/OverlapSlideProcessor.cs
new file mode 100644
index 0000000..4bf35e8
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/OverlapSlideProcessor.cs
@@ -0,0 +1,136 @@
+using System;
+
+namespace SDRSharp.Radio
+{
+ public abstract class OverlapSlideProcessor
+ {
+ private readonly int _fftSize;
+
+ private readonly int _outputSize;
+
+ private readonly float _overlapRatio;
+
+ private int _inputPos;
+
+ private int _outputPos;
+
+ private UnsafeBuffer _fftBuffer;
+
+ private unsafe Complex* _fftPtr;
+
+ private UnsafeBuffer _queuepBuffer;
+
+ private unsafe Complex* _queuePtr;
+
+ private UnsafeBuffer _outputBuffer;
+
+ private unsafe Complex* _outputPtr;
+
+ private UnsafeBuffer _overlapBuffer;
+
+ private unsafe Complex* _overlapPtr;
+
+ public int FFTSize
+ {
+ get
+ {
+ return this._fftSize;
+ }
+ }
+
+ public float OverlapRatio
+ {
+ get
+ {
+ return this._overlapRatio;
+ }
+ }
+
+ public OverlapSlideProcessor(int fftSize)
+ : this(fftSize, 0.75f)
+ {
+ }
+
+ public unsafe OverlapSlideProcessor(int fftSize, float overlapRatio)
+ {
+ if (overlapRatio < 0.75f)
+ {
+ throw new ArgumentException("Overlap ratio must be greater than or equal to 0.75", "overlapRatio");
+ }
+ if (overlapRatio > 1f)
+ {
+ throw new ArgumentException("Overlap ratio must be less than 1.0", "overlapRatio");
+ }
+ this._fftSize = fftSize;
+ this._outputSize = (int)Math.Round((double)((float)this._fftSize * (1f - overlapRatio)));
+ if (this._outputSize < 3)
+ {
+ this._outputSize = 3;
+ }
+ this._overlapRatio = 1f - (float)this._outputSize / (float)this._fftSize;
+ this._inputPos = this._fftSize - this._outputSize;
+ this._queuepBuffer = UnsafeBuffer.Create(this._fftSize, sizeof(Complex));
+ this._queuePtr = (Complex*)(void*)this._queuepBuffer;
+ this._fftBuffer = UnsafeBuffer.Create(this._fftSize, sizeof(Complex));
+ this._fftPtr = (Complex*)(void*)this._fftBuffer;
+ this._outputBuffer = UnsafeBuffer.Create(this._outputSize, sizeof(Complex));
+ this._outputPtr = (Complex*)(void*)this._outputBuffer;
+ this._overlapBuffer = UnsafeBuffer.Create(this._outputSize, sizeof(Complex));
+ this._overlapPtr = (Complex*)(void*)this._overlapBuffer;
+ }
+
+ public unsafe virtual void Process(Complex* buffer, int length)
+ {
+ while (length > 0)
+ {
+ int num = Math.Min(this._fftSize - this._inputPos, length);
+ Utils.Memcpy(this._queuePtr + this._inputPos, buffer, num * sizeof(Complex));
+ Utils.Memcpy(buffer, this._outputPtr + this._outputPos, num * sizeof(Complex));
+ buffer += num;
+ this._inputPos += num;
+ this._outputPos += num;
+ length -= num;
+ if (this._inputPos == this._fftSize)
+ {
+ this.OverlapAdd();
+ this._inputPos = this._fftSize - this._outputSize;
+ this._outputPos = 0;
+ }
+ }
+ }
+
+ public unsafe virtual void Process(float* buffer, int length, int step = 1)
+ {
+ for (int i = 0; i < length; i += step)
+ {
+ this._queuePtr[this._inputPos++] = buffer[i];
+ buffer[i] = this._outputPtr[this._outputPos++].Real;
+ if (this._inputPos == this._fftSize)
+ {
+ this.OverlapAdd();
+ this._inputPos = this._fftSize - this._outputSize;
+ this._outputPos = 0;
+ }
+ }
+ }
+
+ private unsafe void OverlapAdd()
+ {
+ Utils.Memcpy(this._fftPtr, this._queuePtr, this._fftSize * sizeof(Complex));
+ Fourier.ForwardTransform(this._fftPtr, this._fftSize, false);
+ this.ProcessFft(this._fftPtr, this._fftSize);
+ Fourier.InverseTransform(this._fftPtr, this._fftSize);
+ Complex* ptr = this._fftPtr + this._fftSize / 2;
+ float num = 1f / (float)(this._outputSize - 1);
+ for (int i = 0; i < this._outputSize; i++)
+ {
+ float num2 = num * (float)i;
+ this._outputPtr[i] = this._overlapPtr[i] * (1f - num2) + ptr[i] * num2;
+ }
+ Utils.Memmove(this._overlapPtr, ptr + this._outputSize, this._outputSize * sizeof(Complex));
+ Utils.Memmove(this._queuePtr, this._queuePtr + this._outputSize, (this._fftSize - this._outputSize) * sizeof(Complex));
+ }
+
+ protected unsafe abstract void ProcessFft(Complex* buffer, int length);
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/Pll.cs b/SDRSharp.Radio/SDRSharp.Radio/Pll.cs
new file mode 100644
index 0000000..b0eb75c
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/Pll.cs
@@ -0,0 +1,295 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SDRSharp.Radio
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 16)]
+ public struct Pll
+ {
+ private float _sampleRate;
+
+ private float _phase;
+
+ private float _frequencyRadian;
+
+ private float _minFrequencyRadian;
+
+ private float _maxFrequencyRadian;
+
+ private float _defaultFrequency;
+
+ private float _range;
+
+ private float _bandwidth;
+
+ private float _alpha;
+
+ private float _beta;
+
+ private float _zeta;
+
+ private float _phaseAdj;
+
+ private float _phaseAdjM;
+
+ private float _phaseAdjB;
+
+ private float _lockAlpha;
+
+ private float _lockOneMinusAlpha;
+
+ private float _lockTime;
+
+ private float _phaseErrorAvg;
+
+ private float _adjustedPhase;
+
+ private float _lockThreshold;
+
+ private bool _stickOnFrequencyIfNotLocked;
+
+ public float AdjustedPhase
+ {
+ get
+ {
+ return this._adjustedPhase;
+ }
+ }
+
+ public float Phase
+ {
+ get
+ {
+ return this._phase;
+ }
+ }
+
+ public float SampleRate
+ {
+ get
+ {
+ return this._sampleRate;
+ }
+ set
+ {
+ if (this._sampleRate != value)
+ {
+ this._sampleRate = value;
+ this.Configure();
+ }
+ }
+ }
+
+ public float DefaultFrequency
+ {
+ get
+ {
+ return this._defaultFrequency;
+ }
+ set
+ {
+ if (this._defaultFrequency != value)
+ {
+ this._defaultFrequency = value;
+ this.Configure();
+ }
+ }
+ }
+
+ public float Range
+ {
+ get
+ {
+ return this._range;
+ }
+ set
+ {
+ if (this._range != value)
+ {
+ this._range = value;
+ this.Configure();
+ }
+ }
+ }
+
+ public float Bandwidth
+ {
+ get
+ {
+ return this._bandwidth;
+ }
+ set
+ {
+ if (this._bandwidth != value)
+ {
+ this._bandwidth = value;
+ this.Configure();
+ }
+ }
+ }
+
+ public float LockTime
+ {
+ get
+ {
+ return this._lockTime;
+ }
+ set
+ {
+ if (this._lockTime != value)
+ {
+ this._lockTime = value;
+ this.Configure();
+ }
+ }
+ }
+
+ public float LockThreshold
+ {
+ get
+ {
+ return this._lockThreshold;
+ }
+ set
+ {
+ if (this._lockThreshold != value)
+ {
+ this._lockThreshold = value;
+ this.Configure();
+ }
+ }
+ }
+
+ public float Zeta
+ {
+ get
+ {
+ return this._zeta;
+ }
+ set
+ {
+ if (this._zeta != value)
+ {
+ this._zeta = value;
+ this.Configure();
+ }
+ }
+ }
+
+ public float PhaseAdjM
+ {
+ get
+ {
+ return this._phaseAdjM;
+ }
+ set
+ {
+ if (this._phaseAdjM != value)
+ {
+ this._phaseAdjM = value;
+ this.Configure();
+ }
+ }
+ }
+
+ public float PhaseAdjB
+ {
+ get
+ {
+ return this._phaseAdjB;
+ }
+ set
+ {
+ if (this._phaseAdjB != value)
+ {
+ this._phaseAdjB = value;
+ this.Configure();
+ }
+ }
+ }
+
+ public bool StickOnFrequencyIfNotLocked
+ {
+ get
+ {
+ return this._stickOnFrequencyIfNotLocked;
+ }
+ set
+ {
+ this._stickOnFrequencyIfNotLocked = value;
+ }
+ }
+
+ public bool IsLocked
+ {
+ get
+ {
+ return this._phaseErrorAvg < this._lockThreshold;
+ }
+ }
+
+ private void Configure()
+ {
+ this._phase = 0f;
+ float num = (float)(6.2831853071795862 / (double)this._sampleRate);
+ this._frequencyRadian = this._defaultFrequency * num;
+ this._minFrequencyRadian = (this._defaultFrequency - this._range) * num;
+ this._maxFrequencyRadian = (this._defaultFrequency + this._range) * num;
+ this._alpha = 2f * this._zeta * this._bandwidth * num;
+ this._beta = this._alpha * this._alpha / (4f * this._zeta * this._zeta);
+ this._phaseAdj = this._phaseAdjM * this._sampleRate + this._phaseAdjB;
+ this._lockAlpha = (float)(1.0 - Math.Exp(-1.0 / (double)(this._sampleRate * this._lockTime)));
+ this._lockOneMinusAlpha = 1f - this._lockAlpha;
+ }
+
+ public Complex Process(float sample)
+ {
+ Complex complex = Trig.SinCos(this._phase);
+ complex *= sample;
+ float phaseError = 0f - complex.ArgumentFast();
+ this.ProcessPhaseError(phaseError);
+ return complex;
+ }
+
+ public Complex Process(Complex sample)
+ {
+ Complex complex = Trig.SinCos(this._phase);
+ complex *= sample;
+ float phaseError = 0f - complex.ArgumentFast();
+ this.ProcessPhaseError(phaseError);
+ return complex;
+ }
+
+ private void ProcessPhaseError(float phaseError)
+ {
+ this._phaseErrorAvg = this._lockOneMinusAlpha * this._phaseErrorAvg + this._lockAlpha * phaseError * phaseError;
+ if (this._stickOnFrequencyIfNotLocked && !this.IsLocked)
+ {
+ this._phase += this._frequencyRadian;
+ }
+ else
+ {
+ this._frequencyRadian += this._beta * phaseError;
+ if (this._frequencyRadian > this._maxFrequencyRadian)
+ {
+ this._frequencyRadian = this._maxFrequencyRadian;
+ }
+ else if (this._frequencyRadian < this._minFrequencyRadian)
+ {
+ this._frequencyRadian = this._minFrequencyRadian;
+ }
+ this._phase += this._frequencyRadian + this._alpha * phaseError;
+ }
+ this._phase %= 6.28318548f;
+ this._adjustedPhase = this._phase + this._phaseAdj;
+ }
+
+ public void Reset()
+ {
+ this._phase = 0f;
+ this._frequencyRadian = 0f;
+ this._phaseErrorAvg = 10f;
+ this._stickOnFrequencyIfNotLocked = false;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/ProcessorType.cs b/SDRSharp.Radio/SDRSharp.Radio/ProcessorType.cs
new file mode 100644
index 0000000..005ff1c
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/ProcessorType.cs
@@ -0,0 +1,12 @@
+namespace SDRSharp.Radio
+{
+ public enum ProcessorType
+ {
+ RawIQ,
+ DecimatedAndFilteredIQ,
+ DemodulatorOutput,
+ FilteredAudioOutput,
+ FMMPX,
+ RDSBitStream
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/RdsDecoder.cs b/SDRSharp.Radio/SDRSharp.Radio/RdsDecoder.cs
new file mode 100644
index 0000000..255d547
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/RdsDecoder.cs
@@ -0,0 +1,229 @@
+using System;
+
+namespace SDRSharp.Radio
+{
+ public class RdsDecoder
+ {
+ private const int PllDefaultFrequency = 57000;
+
+ private const int PllRange = 12;
+
+ private const int PllBandwith = 1;
+
+ private const float PllZeta = 0.707f;
+
+ private const float PllLockTime = 0.5f;
+
+ private const float PllLockThreshold = 3.2f;
+
+ private const float RdsBitRate = 1187.5f;
+
+ private readonly RdsDetectorBank _bitDecoder;
+
+ private unsafe readonly Pll* _pll;
+
+ private readonly UnsafeBuffer _pllBuffer;
+
+ private readonly Oscillator _osc = new Oscillator();
+
+ private unsafe readonly IirFilter* _syncFilter;
+
+ private readonly UnsafeBuffer _syncFilterBuffer;
+
+ private UnsafeBuffer _rawBuffer;
+
+ private unsafe Complex* _rawPtr;
+
+ private UnsafeBuffer _magBuffer;
+
+ private unsafe float* _magPtr;
+
+ private UnsafeBuffer _dataBuffer;
+
+ private unsafe float* _dataPtr;
+
+ private DownConverter _decimator;
+
+ private FirFilter _matchedFilter;
+
+ private IQFirFilter _baseBandFilter;
+
+ private double _sampleRate;
+
+ private double _demodulationSampleRate;
+
+ private int _decimationFactor;
+
+ private bool _configureNeeded;
+
+ private float _lastSync;
+
+ private float _lastData;
+
+ private float _lastSyncSlope;
+
+ private bool _lastBit;
+
+ public double SampleRate
+ {
+ get
+ {
+ return this._sampleRate;
+ }
+ set
+ {
+ if (value != this._sampleRate)
+ {
+ this._sampleRate = value;
+ this._configureNeeded = true;
+ }
+ }
+ }
+
+ public string RadioText
+ {
+ get
+ {
+ return this._bitDecoder.RadioText;
+ }
+ }
+
+ public string ProgramService
+ {
+ get
+ {
+ return this._bitDecoder.ProgramService;
+ }
+ }
+
+ public ushort PICode
+ {
+ get
+ {
+ return this._bitDecoder.PICode;
+ }
+ }
+
+ public bool UseFEC
+ {
+ get
+ {
+ return this._bitDecoder.UseFEC;
+ }
+ set
+ {
+ this._bitDecoder.UseFEC = value;
+ }
+ }
+
+ public event RdsFrameAvailableDelegate RdsFrameAvailable;
+
+ public unsafe RdsDecoder()
+ {
+ this._pllBuffer = UnsafeBuffer.Create(sizeof(Pll));
+ this._pll = (Pll*)(void*)this._pllBuffer;
+ this._syncFilterBuffer = UnsafeBuffer.Create(sizeof(IirFilter));
+ this._syncFilter = (IirFilter*)(void*)this._syncFilterBuffer;
+ this._bitDecoder = new RdsDetectorBank();
+ this._bitDecoder.FrameAvailable += this.FrameAvailableHandler;
+ }
+
+ private unsafe void Configure()
+ {
+ this._osc.SampleRate = this._sampleRate;
+ this._osc.Frequency = 57000.0;
+ int i;
+ for (i = 0; this._sampleRate >= (double)(20000 << i); i++)
+ {
+ }
+ this._decimationFactor = 1 << i;
+ this._demodulationSampleRate = this._sampleRate / (double)this._decimationFactor;
+ this._decimator = new DownConverter(this._demodulationSampleRate, this._decimationFactor);
+ float[] coefficients = FilterBuilder.MakeLowPassKernel(this._demodulationSampleRate, 200, 2500.0, WindowType.BlackmanHarris4);
+ this._baseBandFilter = new IQFirFilter(coefficients, 1);
+ this._pll->SampleRate = (float)this._demodulationSampleRate;
+ this._pll->DefaultFrequency = 0f;
+ this._pll->Range = 12f;
+ this._pll->Bandwidth = 1f;
+ this._pll->Zeta = 0.707f;
+ this._pll->LockTime = 0.5f;
+ this._pll->LockThreshold = 3.2f;
+ int length = (int)(this._demodulationSampleRate / 1187.5) | 1;
+ coefficients = FilterBuilder.MakeSin(this._demodulationSampleRate, 1187.5, length);
+ this._matchedFilter = new FirFilter(coefficients, 1);
+ this._syncFilter->Init(IirFilterType.BandPass, 1187.5, this._demodulationSampleRate, 500.0);
+ }
+
+ public unsafe void Reset()
+ {
+ this._bitDecoder.Reset();
+ this._syncFilter->Reset();
+ }
+
+ public unsafe void Process(float* baseBand, int length)
+ {
+ if (this._configureNeeded)
+ {
+ this.Configure();
+ this._configureNeeded = false;
+ }
+ if (this._rawBuffer == null || this._rawBuffer.Length != length)
+ {
+ this._rawBuffer = UnsafeBuffer.Create(length, sizeof(Complex));
+ this._rawPtr = (Complex*)(void*)this._rawBuffer;
+ }
+ if (this._magBuffer == null || this._magBuffer.Length != length)
+ {
+ this._magBuffer = UnsafeBuffer.Create(length, 4);
+ this._magPtr = (float*)(void*)this._magBuffer;
+ }
+ if (this._dataBuffer == null || this._dataBuffer.Length != length)
+ {
+ this._dataBuffer = UnsafeBuffer.Create(length, 4);
+ this._dataPtr = (float*)(void*)this._dataBuffer;
+ }
+ for (int i = 0; i < length; i++)
+ {
+ this._osc.Tick();
+ this._rawPtr[i] = this._osc.Phase * baseBand[i];
+ }
+ this._decimator.Process(this._rawPtr, length);
+ length /= this._decimationFactor;
+ this._baseBandFilter.Process(this._rawPtr, length);
+ for (int j = 0; j < length; j++)
+ {
+ this._dataPtr[j] = this._pll->Process(this._rawPtr[j]).Imag;
+ }
+ this._matchedFilter.Process(this._dataPtr, length);
+ for (int k = 0; k < length; k++)
+ {
+ this._magPtr[k] = Math.Abs(this._dataPtr[k]);
+ }
+ this._syncFilter->Process(this._magPtr, length);
+ for (int l = 0; l < length; l++)
+ {
+ float lastData = this._dataPtr[l];
+ float num = this._magPtr[l];
+ float num2 = num - this._lastSync;
+ this._lastSync = num;
+ if (num2 < 0f && this._lastSyncSlope * num2 < 0f)
+ {
+ bool flag = this._lastData > 0f;
+ this._bitDecoder.Process(flag ^ this._lastBit);
+ this._lastBit = flag;
+ }
+ this._lastData = lastData;
+ this._lastSyncSlope = num2;
+ }
+ }
+
+ private void FrameAvailableHandler(ref RdsFrame frame)
+ {
+ RdsFrameAvailableDelegate rdsFrameAvailable = this.RdsFrameAvailable;
+ if (rdsFrameAvailable != null)
+ {
+ rdsFrameAvailable(ref frame);
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/RdsDetectorBank.cs b/SDRSharp.Radio/SDRSharp.Radio/RdsDetectorBank.cs
new file mode 100644
index 0000000..f5c38d5
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/RdsDetectorBank.cs
@@ -0,0 +1,73 @@
+namespace SDRSharp.Radio
+{
+ public class RdsDetectorBank
+ {
+ private readonly RdsDumpGroups _dumpGroups;
+
+ private readonly SyndromeDetector _detector;
+
+ public string RadioText
+ {
+ get
+ {
+ return this._dumpGroups.RadioText;
+ }
+ }
+
+ public string ProgramService
+ {
+ get
+ {
+ return this._dumpGroups.ProgramService;
+ }
+ }
+
+ public ushort PICode
+ {
+ get
+ {
+ return this._dumpGroups.PICode;
+ }
+ }
+
+ public bool UseFEC
+ {
+ get
+ {
+ return this._detector.UseFEC;
+ }
+ set
+ {
+ this._detector.UseFEC = value;
+ }
+ }
+
+ public event RdsFrameAvailableDelegate FrameAvailable;
+
+ public RdsDetectorBank()
+ {
+ this._dumpGroups = new RdsDumpGroups();
+ this._detector = new SyndromeDetector(this._dumpGroups);
+ this._detector.FrameAvailable += this.FrameAvailableHandler;
+ }
+
+ public void Process(bool b)
+ {
+ this._detector.Clock(b);
+ }
+
+ public void Reset()
+ {
+ this._dumpGroups.Reset();
+ }
+
+ private void FrameAvailableHandler(ref RdsFrame frame)
+ {
+ RdsFrameAvailableDelegate frameAvailable = this.FrameAvailable;
+ if (frameAvailable != null)
+ {
+ frameAvailable(ref frame);
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/RdsDumpGroups.cs b/SDRSharp.Radio/SDRSharp.Radio/RdsDumpGroups.cs
new file mode 100644
index 0000000..7d630cb
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/RdsDumpGroups.cs
@@ -0,0 +1,163 @@
+using System;
+using System.Linq;
+using System.Text;
+
+namespace SDRSharp.Radio
+{
+ public class RdsDumpGroups
+ {
+ private StringBuilder _radioTextSB = new StringBuilder(" ");
+
+ private StringBuilder _programServiceSB = new StringBuilder(" ");
+
+ private string _radioText = string.Empty;
+
+ private string _programService = " ";
+
+ private ushort _piCode;
+
+ private bool _radioTextABFlag;
+
+ public string RadioText
+ {
+ get
+ {
+ return this._radioText;
+ }
+ }
+
+ public string ProgramService
+ {
+ get
+ {
+ return this._programService;
+ }
+ }
+
+ public ushort PICode
+ {
+ get
+ {
+ return this._piCode;
+ }
+ }
+
+ public void Reset()
+ {
+ lock (this)
+ {
+ this._radioTextSB = new StringBuilder(" ");
+ this._programServiceSB = new StringBuilder(" ");
+ this._radioText = string.Empty;
+ this._programService = " ";
+ this._piCode = 0;
+ this._radioTextABFlag = false;
+ }
+ }
+
+ public bool AnalyseFrames(ref RdsFrame frame)
+ {
+ bool result = false;
+ if ((frame.GroupB & 0xF800) == 8192)
+ {
+ int num = (frame.GroupB & 0xF) * 4;
+ bool flag = (frame.GroupB >> 4 & 1) == 1;
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.Append((char)(frame.GroupC >> 8));
+ stringBuilder.Append((char)(frame.GroupC & 0xFF));
+ stringBuilder.Append((char)(frame.GroupD >> 8));
+ stringBuilder.Append((char)(frame.GroupD & 0xFF));
+ if (stringBuilder.ToString().Any(delegate(char ch)
+ {
+ if (ch >= ' ')
+ {
+ return ch > '\u007f';
+ }
+ return true;
+ }))
+ {
+ return false;
+ }
+ lock (this)
+ {
+ if (flag != this._radioTextABFlag)
+ {
+ for (int i = 0; i < this._radioTextSB.Length; i++)
+ {
+ this._radioTextSB[i] = ' ';
+ }
+ this._radioTextABFlag = flag;
+ }
+ else
+ {
+ this._radioTextSB.Remove(num, 4);
+ }
+ this._radioTextSB.Insert(num, stringBuilder.ToString());
+ this._radioText = this._radioTextSB.ToString().Trim();
+ this._piCode = frame.GroupA;
+ }
+ result = true;
+ }
+ if ((frame.GroupB & 0xF800) == 0)
+ {
+ int num2 = (frame.GroupB & 3) * 2;
+ StringBuilder stringBuilder2 = new StringBuilder();
+ stringBuilder2.Append((char)(frame.GroupD >> 8));
+ stringBuilder2.Append((char)(frame.GroupD & 0xFF));
+ if (stringBuilder2.ToString().Any(delegate(char ch)
+ {
+ if (ch >= ' ')
+ {
+ return ch > '\u007f';
+ }
+ return true;
+ }))
+ {
+ return false;
+ }
+ lock (this)
+ {
+ this._programServiceSB.Remove(num2, 2);
+ this._programServiceSB.Insert(num2, stringBuilder2.ToString());
+ this._programService = this._programServiceSB.ToString().Substring(0, 8);
+ this._piCode = frame.GroupA;
+ }
+ result = true;
+ }
+ return result;
+ }
+
+ private static string Dump4A(ushort blockB, ushort block3, ushort block4)
+ {
+ int num = block4 & 0x1F;
+ if ((block4 & 0x20) != 0)
+ {
+ num *= -1;
+ }
+ int minute = block4 >> 6 & 0x3F;
+ int hour = (block4 >> 12 & 0xF) | (block3 << 4 & 0x10);
+ int num2 = block3 >> 1 | (blockB << 15 & 0x18000);
+ int num3 = (int)(((double)num2 - 15078.2) / 365.25);
+ int num4 = (int)(((double)num2 - 14956.1 - (double)(int)((double)num3 * 365.25)) / 30.6001);
+ int day = num2 - 14956 - (int)((double)num3 * 365.25) - (int)((double)num4 * 30.6001);
+ int num5 = 0;
+ if (num4 == 14 || num4 == 15)
+ {
+ num5 = 1;
+ }
+ num3 = num3 + num5 + 1900;
+ num4 = num4 - 1 - num5 * 12;
+ try
+ {
+ DateTime d = new DateTime(num3, num4, day, hour, minute, 0);
+ TimeSpan t = new TimeSpan(num / 2, num * 30 % 60, 0);
+ d += t;
+ return "4A " + d.ToLongDateString() + " " + d.ToLongTimeString();
+ }
+ catch (Exception ex)
+ {
+ return ex.Message;
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/RdsFrame.cs b/SDRSharp.Radio/SDRSharp.Radio/RdsFrame.cs
new file mode 100644
index 0000000..96d973b
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/RdsFrame.cs
@@ -0,0 +1,24 @@
+namespace SDRSharp.Radio
+{
+ public struct RdsFrame
+ {
+ public ushort GroupA;
+
+ public ushort GroupB;
+
+ public ushort GroupC;
+
+ public ushort GroupD;
+
+ public bool Filter;
+
+ public RdsFrame(ushort groupA, ushort groupB, ushort groupC, ushort groupD)
+ {
+ this.GroupA = groupA;
+ this.GroupB = groupB;
+ this.GroupC = groupC;
+ this.GroupD = groupD;
+ this.Filter = false;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/RdsFrameAvailableDelegate.cs b/SDRSharp.Radio/SDRSharp.Radio/RdsFrameAvailableDelegate.cs
new file mode 100644
index 0000000..c8a6a8c
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/RdsFrameAvailableDelegate.cs
@@ -0,0 +1,4 @@
+namespace SDRSharp.Radio
+{
+ public delegate void RdsFrameAvailableDelegate(ref RdsFrame frame);
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/Resampler.cs b/SDRSharp.Radio/SDRSharp.Radio/Resampler.cs
new file mode 100644
index 0000000..ea3a814
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/Resampler.cs
@@ -0,0 +1,168 @@
+using System;
+
+namespace SDRSharp.Radio
+{
+ public class Resampler
+ {
+ public const double DefaultProtectedPassband = 0.45;
+
+ public const int DefaultTapsPerPhase = 160;
+
+ private int _phase;
+
+ private readonly int _interpolationFactor;
+
+ private readonly int _decimationFactor;
+
+ private readonly int _tapsPerPhase;
+
+ private readonly UnsafeBuffer _firKernelBuffer;
+
+ private unsafe readonly float* _firKernel;
+
+ private readonly UnsafeBuffer _firQueueBuffer;
+
+ private unsafe readonly float* _firQueue;
+
+ public unsafe Resampler(double inputSampleRate, double outputSampleRate, int tapsPerPhase = 160, double protectedPassband = 0.45)
+ {
+ Resampler.DoubleToFraction(outputSampleRate / inputSampleRate, out this._interpolationFactor, out this._decimationFactor);
+ this._tapsPerPhase = tapsPerPhase;
+ int num = tapsPerPhase * this._interpolationFactor;
+ this._firKernelBuffer = UnsafeBuffer.Create(num, 4);
+ this._firKernel = (float*)(void*)this._firKernelBuffer;
+ double cutoffFrequency = Math.Min(inputSampleRate, outputSampleRate) * protectedPassband;
+ float[] array = FilterBuilder.MakeLowPassKernel(inputSampleRate * (double)this._interpolationFactor, num - 1, cutoffFrequency, WindowType.BlackmanHarris4);
+ float[] array2 = array;
+ fixed (float* ptr = array2)
+ {
+ for (int i = 0; i < array.Length; i++)
+ {
+ ptr[i] *= (float)this._interpolationFactor;
+ }
+ Utils.Memcpy(this._firKernel, ptr, (num - 1) * 4);
+ this._firKernel[num - 1] = 0f;
+ }
+ this._firQueueBuffer = UnsafeBuffer.Create(num, 4);
+ this._firQueue = (float*)(void*)this._firQueueBuffer;
+ }
+
+ private static void DoubleToFraction(double value, out int num, out int den)
+ {
+ int num2 = 1;
+ int num3 = 1;
+ double num4 = 1.0;
+ while (Math.Abs(num4 - value) > 1E-15)
+ {
+ if (num4 > value)
+ {
+ num3++;
+ }
+ else
+ {
+ num2++;
+ }
+ num4 = (double)num2 / (double)num3;
+ }
+ num = num2;
+ den = num3;
+ }
+
+ public unsafe int Process(float* input, float* output, int inputLength)
+ {
+ int num = 0;
+ while (inputLength > 0)
+ {
+ int num2 = 0;
+ while (this._phase >= this._interpolationFactor)
+ {
+ this._phase -= this._interpolationFactor;
+ num2++;
+ if (--inputLength == 0)
+ {
+ break;
+ }
+ }
+ if (num2 >= this._tapsPerPhase)
+ {
+ input += num2 - this._tapsPerPhase;
+ num2 = this._tapsPerPhase;
+ }
+ for (int num3 = this._tapsPerPhase - 1; num3 >= num2; num3--)
+ {
+ this._firQueue[num3] = this._firQueue[num3 - num2];
+ }
+ for (int num4 = num2 - 1; num4 >= 0; num4--)
+ {
+ float* intPtr = this._firQueue + num4;
+ float* intPtr2 = input;
+ input = intPtr2 + 1;
+ *intPtr = *intPtr2;
+ }
+ while (this._phase < this._interpolationFactor)
+ {
+ float* ptr = this._firKernel + this._phase;
+ float num5 = 0f;
+ for (int i = 0; i < this._tapsPerPhase; i++)
+ {
+ num5 += *ptr * this._firQueue[i];
+ ptr += this._interpolationFactor;
+ }
+ float* intPtr3 = output;
+ output = intPtr3 + 1;
+ *intPtr3 = num5;
+ num++;
+ this._phase += this._decimationFactor;
+ }
+ }
+ return num;
+ }
+
+ public unsafe int ProcessInterleaved(float* input, float* output, int inputLength)
+ {
+ int num = 0;
+ while (inputLength > 0)
+ {
+ int num2 = 0;
+ while (this._phase >= this._interpolationFactor)
+ {
+ this._phase -= this._interpolationFactor;
+ num2++;
+ if (--inputLength == 0)
+ {
+ break;
+ }
+ }
+ if (num2 >= this._tapsPerPhase)
+ {
+ input += (num2 - this._tapsPerPhase) * 2;
+ num2 = this._tapsPerPhase;
+ }
+ for (int num3 = this._tapsPerPhase - 1; num3 >= num2; num3--)
+ {
+ this._firQueue[num3] = this._firQueue[num3 - num2];
+ }
+ for (int num4 = num2 - 1; num4 >= 0; num4--)
+ {
+ this._firQueue[num4] = *input;
+ input += 2;
+ }
+ while (this._phase < this._interpolationFactor)
+ {
+ float* ptr = this._firKernel + this._phase;
+ float num5 = 0f;
+ for (int i = 0; i < this._tapsPerPhase; i++)
+ {
+ num5 += *ptr * this._firQueue[i];
+ ptr += this._interpolationFactor;
+ }
+ *output = num5;
+ output += 2;
+ num++;
+ this._phase += this._decimationFactor;
+ }
+ }
+ return num;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/SamplesAvailableDelegate.cs b/SDRSharp.Radio/SDRSharp.Radio/SamplesAvailableDelegate.cs
new file mode 100644
index 0000000..fbf9964
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/SamplesAvailableDelegate.cs
@@ -0,0 +1,4 @@
+namespace SDRSharp.Radio
+{
+ public unsafe delegate void SamplesAvailableDelegate(IFrontendController sender, Complex* data, int len);
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/SharpEvent.cs b/SDRSharp.Radio/SDRSharp.Radio/SharpEvent.cs
new file mode 100644
index 0000000..a6a7a27
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/SharpEvent.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Threading;
+
+namespace SDRSharp.Radio
+{
+ public sealed class SharpEvent
+ {
+ private bool _state;
+
+ private bool _waiting;
+
+ public SharpEvent(bool initialState)
+ {
+ this._state = initialState;
+ }
+
+ public SharpEvent()
+ : this(false)
+ {
+ }
+
+ ~SharpEvent()
+ {
+ this.Dispose();
+ }
+
+ public void Dispose()
+ {
+ this.Set();
+ GC.SuppressFinalize(this);
+ }
+
+ public void Set()
+ {
+ lock (this)
+ {
+ this._state = true;
+ if (this._waiting)
+ {
+ Monitor.Pulse(this);
+ }
+ }
+ }
+
+ public void WaitOne()
+ {
+ lock (this)
+ {
+ if (!this._state)
+ {
+ this._waiting = true;
+ try
+ {
+ Monitor.Wait(this);
+ }
+ finally
+ {
+ this._waiting = false;
+ }
+ }
+ this._state = false;
+ }
+ }
+
+ public void Reset()
+ {
+ lock (this)
+ {
+ this._state = false;
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/SharpThreadPool.cs b/SDRSharp.Radio/SDRSharp.Radio/SharpThreadPool.cs
new file mode 100644
index 0000000..5c1bee9
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/SharpThreadPool.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace SDRSharp.Radio
+{
+ public class SharpThreadPool
+ {
+ private struct WorkItem
+ {
+ private readonly WaitCallback _callback;
+
+ private readonly object _parameter;
+
+ public WorkItem(WaitCallback callback, object parameter)
+ {
+ this._callback = callback;
+ this._parameter = parameter;
+ }
+
+ public void Invoke()
+ {
+ this._callback(this._parameter);
+ }
+ }
+
+ private readonly Queue _jobQueue = new Queue();
+
+ private readonly Thread[] _workerThreads;
+
+ private int _threadsWaiting;
+
+ private bool _terminated;
+
+ public SharpThreadPool()
+ : this(Environment.ProcessorCount)
+ {
+ }
+
+ public SharpThreadPool(int threadCount)
+ {
+ this._workerThreads = new Thread[threadCount];
+ for (int i = 0; i < this._workerThreads.Length; i++)
+ {
+ this._workerThreads[i] = new Thread(this.DispatchLoop);
+ this._workerThreads[i].Priority = ThreadPriority.Highest;
+ this._workerThreads[i].Start();
+ }
+ }
+
+ public void QueueUserWorkItem(WaitCallback callback)
+ {
+ this.QueueUserWorkItem(callback, null);
+ }
+
+ public void QueueUserWorkItem(WaitCallback callback, object parameter)
+ {
+ WorkItem item = new WorkItem(callback, parameter);
+ lock (this._jobQueue)
+ {
+ this._jobQueue.Enqueue(item);
+ if (this._threadsWaiting > 0)
+ {
+ Monitor.Pulse(this._jobQueue);
+ }
+ }
+ }
+
+ private void DispatchLoop()
+ {
+ while (true)
+ {
+ WorkItem workItem;
+ lock (this._jobQueue)
+ {
+ if (!this._terminated)
+ {
+ while (this._jobQueue.Count == 0)
+ {
+ this._threadsWaiting++;
+ try
+ {
+ Monitor.Wait(this._jobQueue);
+ }
+ finally
+ {
+ this._threadsWaiting--;
+ }
+ if (this._terminated)
+ {
+ return;
+ }
+ }
+ workItem = this._jobQueue.Dequeue();
+ goto end_IL_0009;
+ }
+ return;
+ end_IL_0009:;
+ }
+ workItem.Invoke();
+ }
+ }
+
+ public void Dispose()
+ {
+ this._terminated = true;
+ lock (this._jobQueue)
+ {
+ Monitor.PulseAll(this._jobQueue);
+ }
+ for (int i = 0; i < this._workerThreads.Length; i++)
+ {
+ this._workerThreads[i].Join();
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/SideBandDetector.cs b/SDRSharp.Radio/SDRSharp.Radio/SideBandDetector.cs
new file mode 100644
index 0000000..de071c8
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/SideBandDetector.cs
@@ -0,0 +1,13 @@
+namespace SDRSharp.Radio
+{
+ public sealed class SideBandDetector
+ {
+ public unsafe void Demodulate(Complex* iq, float* audio, int length)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ audio[i] = iq[i].Real;
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/StereoDecoder.cs b/SDRSharp.Radio/SDRSharp.Radio/StereoDecoder.cs
new file mode 100644
index 0000000..9809a56
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/StereoDecoder.cs
@@ -0,0 +1,214 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SDRSharp.Radio
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 16)]
+ public sealed class StereoDecoder
+ {
+ private const int DefaultPilotFrequency = 19000;
+
+ private const int PllRange = 20;
+
+ private const int PllBandwith = 10;
+
+ private const float PllThreshold = 1f;
+
+ private const float PllLockTime = 0.5f;
+
+ private const float PllZeta = 0.707f;
+
+ private const float AudioGain = 0.2f;
+
+ private static readonly float _deemphasisTime = (float)Utils.GetDoubleSetting("deemphasisTime", 50.0) * 1E-06f;
+
+ private static readonly float _pllPhaseAdjM = (float)Utils.GetDoubleSetting("pllPhaseAdjM", 0.0);
+
+ private static readonly float _pllPhaseAdjB = (float)Utils.GetDoubleSetting("pllPhaseAdjB", -1.75);
+
+ private unsafe readonly Pll* _pll;
+
+ private readonly UnsafeBuffer _pllBuffer;
+
+ private unsafe IirFilter* _pilotFilter;
+
+ private UnsafeBuffer _pilotFilterBuffer;
+
+ private UnsafeBuffer _channelABuffer;
+
+ private UnsafeBuffer _channelBBuffer;
+
+ private unsafe float* _channelAPtr;
+
+ private unsafe float* _channelBPtr;
+
+ private ComplexFilter _channelAFilter;
+
+ private ComplexFilter _channelBFilter;
+
+ private FloatDecimator _channelADecimator;
+
+ private FloatDecimator _channelBDecimator;
+
+ private double _sampleRate;
+
+ private int _audioDecimationRatio;
+
+ private float _deemphasisAlpha;
+
+ private float _deemphasisAvgL;
+
+ private float _deemphasisAvgR;
+
+ private bool _forceMono;
+
+ public bool ForceMono
+ {
+ get
+ {
+ return this._forceMono;
+ }
+ set
+ {
+ this._forceMono = value;
+ }
+ }
+
+ public unsafe bool IsPllLocked
+ {
+ get
+ {
+ return this._pll->IsLocked;
+ }
+ }
+
+ public unsafe StereoDecoder()
+ {
+ this._pllBuffer = UnsafeBuffer.Create(sizeof(Pll));
+ this._pll = (Pll*)(void*)this._pllBuffer;
+ }
+
+ public unsafe void Process(float* baseBand, float* interleavedStereo, int length)
+ {
+ if (this._forceMono)
+ {
+ this.ProcessMono(baseBand, interleavedStereo, length);
+ }
+ else
+ {
+ this.ProcessStereo(baseBand, interleavedStereo, length);
+ }
+ }
+
+ private unsafe void ProcessMono(float* baseBand, float* interleavedStereo, int length)
+ {
+ if (this._channelABuffer == null || this._channelABuffer.Length != length)
+ {
+ this._channelABuffer = UnsafeBuffer.Create(length, 4);
+ this._channelAPtr = (float*)(void*)this._channelABuffer;
+ }
+ Utils.Memcpy(this._channelAPtr, baseBand, length * 4);
+ this._channelADecimator.Process(this._channelAPtr, length);
+ length /= this._audioDecimationRatio;
+ this._channelAFilter.Process(this._channelAPtr, length, 1);
+ for (int i = 0; i < length; i++)
+ {
+ this._deemphasisAvgL += this._deemphasisAlpha * (this._channelAPtr[i] - this._deemphasisAvgL);
+ this._channelAPtr[i] = this._deemphasisAvgL;
+ }
+ for (int j = 0; j < length; j++)
+ {
+ interleavedStereo[j * 2 + 1] = (interleavedStereo[j * 2] = this._channelAPtr[j] * 0.2f);
+ }
+ }
+
+ private unsafe void ProcessStereo(float* baseBand, float* interleavedStereo, int length)
+ {
+ if (this._channelABuffer == null || this._channelABuffer.Length != length)
+ {
+ this._channelABuffer = UnsafeBuffer.Create(length, 4);
+ this._channelAPtr = (float*)(void*)this._channelABuffer;
+ }
+ if (this._channelBBuffer == null || this._channelBBuffer.Length != length)
+ {
+ this._channelBBuffer = UnsafeBuffer.Create(length, 4);
+ this._channelBPtr = (float*)(void*)this._channelBBuffer;
+ }
+ int num = length / this._audioDecimationRatio;
+ Utils.Memcpy(this._channelAPtr, baseBand, length * 4);
+ this._channelADecimator.Process(this._channelAPtr, length);
+ this._channelAFilter.Process(this._channelAPtr, num, 1);
+ for (int i = 0; i < length; i++)
+ {
+ float sample = this._pilotFilter->Process(baseBand[i]);
+ this._pll->Process(sample);
+ this._channelBPtr[i] = baseBand[i] * Trig.Sin((float)((double)this._pll->AdjustedPhase * 2.0));
+ }
+ if (!this._pll->IsLocked)
+ {
+ for (int j = 0; j < num; j++)
+ {
+ this._deemphasisAvgL += this._deemphasisAlpha * (this._channelAPtr[j] - this._deemphasisAvgL);
+ this._channelAPtr[j] = this._deemphasisAvgL;
+ }
+ for (int k = 0; k < num; k++)
+ {
+ interleavedStereo[k * 2 + 1] = (interleavedStereo[k * 2] = this._channelAPtr[k] * 0.2f);
+ }
+ }
+ else
+ {
+ this._channelBDecimator.Process(this._channelBPtr, length);
+ this._channelBFilter.Process(this._channelBPtr, num, 1);
+ for (int l = 0; l < num; l++)
+ {
+ float num2 = this._channelAPtr[l];
+ float num3 = 2f * this._channelBPtr[l];
+ interleavedStereo[l * 2] = (num2 + num3) * 0.2f;
+ interleavedStereo[l * 2 + 1] = (num2 - num3) * 0.2f;
+ }
+ for (int m = 0; m < num; m++)
+ {
+ this._deemphasisAvgL += this._deemphasisAlpha * (interleavedStereo[m * 2] - this._deemphasisAvgL);
+ interleavedStereo[m * 2] = this._deemphasisAvgL;
+ this._deemphasisAvgR += this._deemphasisAlpha * (interleavedStereo[m * 2 + 1] - this._deemphasisAvgR);
+ interleavedStereo[m * 2 + 1] = this._deemphasisAvgR;
+ }
+ }
+ }
+
+ public unsafe void Configure(double sampleRate, int decimationStageCount)
+ {
+ this._audioDecimationRatio = 1 << decimationStageCount;
+ if (this._sampleRate != sampleRate)
+ {
+ this._sampleRate = sampleRate;
+ this._pilotFilterBuffer = UnsafeBuffer.Create(sizeof(IirFilter));
+ this._pilotFilter = (IirFilter*)(void*)this._pilotFilterBuffer;
+ this._pilotFilter->Init(IirFilterType.BandPass, 19000.0, this._sampleRate, 500.0);
+ this._pll->SampleRate = (float)this._sampleRate;
+ this._pll->DefaultFrequency = 19000f;
+ this._pll->Range = 20f;
+ this._pll->Bandwidth = 10f;
+ this._pll->Zeta = 0.707f;
+ this._pll->PhaseAdjM = StereoDecoder._pllPhaseAdjM;
+ this._pll->PhaseAdjB = StereoDecoder._pllPhaseAdjB;
+ this._pll->LockTime = 0.5f;
+ this._pll->LockThreshold = 1f;
+ double num = sampleRate / (double)this._audioDecimationRatio;
+ Complex[] kernel = FilterBuilder.MakeComplexKernel(num, 250, Math.Min(0.9 * num, 30000.0), 0.0, WindowType.BlackmanHarris4);
+ this._channelAFilter = new ComplexFilter(kernel);
+ this._channelBFilter = new ComplexFilter(kernel);
+ this._deemphasisAlpha = (float)(1.0 - Math.Exp(-1.0 / (num * (double)StereoDecoder._deemphasisTime)));
+ this._deemphasisAvgL = 0f;
+ this._deemphasisAvgR = 0f;
+ }
+ if (this._channelADecimator != null && this._channelBDecimator != null && this._audioDecimationRatio == this._channelADecimator.DecimationRatio)
+ {
+ return;
+ }
+ this._channelADecimator = new FloatDecimator(this._audioDecimationRatio);
+ this._channelBDecimator = new FloatDecimator(this._audioDecimationRatio);
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/StreamControl.cs b/SDRSharp.Radio/SDRSharp.Radio/StreamControl.cs
new file mode 100644
index 0000000..7a0d8e7
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/StreamControl.cs
@@ -0,0 +1,551 @@
+using SDRSharp.Radio.PortAudio;
+using System;
+using System.Threading;
+
+namespace SDRSharp.Radio
+{
+ public sealed class StreamControl : IDisposable
+ {
+ private enum InputType
+ {
+ SoundCard,
+ Plugin,
+ WaveFile
+ }
+
+ private const int IQBufferingFactor = 6;
+
+ private const int AudioBufferingFactor = 2;
+
+ private const int WaveBufferSize = 65536;
+
+ private const int MaxDecimationStageCount = 20;
+
+ private static readonly int _bufferAlignment = 8 * Environment.ProcessorCount;
+
+ private static readonly int _minOutputSampleRate = Utils.GetIntSetting("minOutputSampleRate", 24000);
+
+ private static readonly int _minReducedNarrowBandwidth = Utils.GetIntSetting("minReducedNarrowBandwidth", 8000);
+
+ private static readonly int _minReducedWideBandwidth = Utils.GetIntSetting("minReducedWideBandwidth", 120000);
+
+ private unsafe float* _dspOutPtr;
+
+ private UnsafeBuffer _dspOutBuffer;
+
+ private unsafe Complex* _dspInPtr;
+
+ private WavePlayer _wavePlayer;
+
+ private WaveRecorder _waveRecorder;
+
+ private WaveDuplex _waveDuplex;
+
+ private WaveFile _waveFile;
+
+ private ComplexCircularBuffer _iqCircularBuffer;
+
+ private FloatCircularBuffer _audioCircularBuffer;
+
+ private Thread _waveReadThread;
+
+ private Thread _dspThread;
+
+ private float _audioGain;
+
+ private float _outputGain;
+
+ private int _inputDevice;
+
+ private double _inputSampleRate;
+
+ private int _inputBufferSize;
+
+ private double _bufferSizeInMs;
+
+ private int _outputDevice;
+
+ private double _outputSampleRate;
+
+ private int _outputBufferSize;
+
+ private int _decimationStageCount;
+
+ private bool _swapIQ;
+
+ private bool _isPlaying;
+
+ private InputType _inputType;
+
+ private IFrontendController _frontend;
+
+ private HookManager _hookManager;
+
+ public static bool ReducedBandwidth
+ {
+ get;
+ set;
+ }
+
+ public float AudioGain
+ {
+ get
+ {
+ return this._audioGain;
+ }
+ set
+ {
+ this._audioGain = value;
+ this._outputGain = (float)Math.Pow(10.0, (double)value / 10.0);
+ }
+ }
+
+ public bool ScaleOutput
+ {
+ get;
+ set;
+ }
+
+ public bool SwapIQ
+ {
+ get
+ {
+ return this._swapIQ;
+ }
+ set
+ {
+ this._swapIQ = value;
+ }
+ }
+
+ public double SampleRate
+ {
+ get
+ {
+ return this._inputSampleRate;
+ }
+ }
+
+ public bool IsPlaying
+ {
+ get
+ {
+ return this._isPlaying;
+ }
+ }
+
+ public int BufferSize
+ {
+ get
+ {
+ return this._inputBufferSize;
+ }
+ }
+
+ public double BufferSizeInMs
+ {
+ get
+ {
+ return this._bufferSizeInMs;
+ }
+ }
+
+ public int DecimationStageCount
+ {
+ get
+ {
+ return this._decimationStageCount;
+ }
+ }
+
+ public double AudioSampleRate
+ {
+ get
+ {
+ return this._outputSampleRate;
+ }
+ }
+
+ public event BufferNeededDelegate BufferNeeded;
+
+ public StreamControl(HookManager hookManager = null)
+ {
+ this._hookManager = hookManager;
+ this.AudioGain = 10f;
+ this.ScaleOutput = true;
+ }
+
+ ~StreamControl()
+ {
+ this.Dispose();
+ }
+
+ public void Dispose()
+ {
+ this.Stop();
+ GC.SuppressFinalize(this);
+ }
+
+ private unsafe void DuplexFiller(float* buffer, int frameCount)
+ {
+ if (!this._isPlaying)
+ {
+ Utils.Memset(buffer, 0, this._outputBufferSize * 4);
+ }
+ else
+ {
+ this._dspInPtr = (Complex*)buffer;
+ if (this._dspOutBuffer == null || this._dspOutBuffer.Length != frameCount * 2)
+ {
+ this._dspOutBuffer = UnsafeBuffer.Create(frameCount * 2, 4);
+ this._dspOutPtr = (float*)(void*)this._dspOutBuffer;
+ }
+ if (this._hookManager != null)
+ {
+ this._hookManager.ProcessRawIQ(this._dspInPtr, frameCount);
+ }
+ this.ProcessIQ();
+ this.ScaleBuffer(this._dspOutPtr, this._dspOutBuffer.Length);
+ Utils.Memcpy(buffer, this._dspOutPtr, this._dspOutBuffer.Length * 4);
+ }
+ }
+
+ private unsafe void PlayerFiller(float* buffer, int frameCount)
+ {
+ if (!this._isPlaying)
+ {
+ Utils.Memset(buffer, 0, this._outputBufferSize * 4);
+ }
+ else
+ {
+ float* ptr = this._audioCircularBuffer.Acquire();
+ if (ptr != null)
+ {
+ Utils.Memcpy(buffer, ptr, this._outputBufferSize * 4);
+ this._audioCircularBuffer.Release();
+ this.ScaleBuffer(buffer, this._outputBufferSize);
+ }
+ }
+ }
+
+ private unsafe void RecorderFiller(float* buffer, int frameCount)
+ {
+ if (this._isPlaying)
+ {
+ Utils.Memcpy(buffer, buffer, frameCount * sizeof(Complex));
+ if (this._hookManager != null)
+ {
+ this._hookManager.ProcessRawIQ((Complex*)buffer, frameCount);
+ }
+ this._iqCircularBuffer.Write((Complex*)buffer, frameCount);
+ }
+ }
+
+ private unsafe void FrontendFiller(IFrontendController sender, Complex* samples, int len)
+ {
+ if (this._isPlaying)
+ {
+ if (this._hookManager != null)
+ {
+ this._hookManager.ProcessRawIQ(samples, len);
+ }
+ this._iqCircularBuffer.Write(samples, len, !(sender is INonBlockingController));
+ }
+ }
+
+ private unsafe void WaveFileFiller()
+ {
+ Complex[] array = new Complex[65536];
+ Complex[] array2 = array;
+ fixed (Complex* ptr = array2)
+ {
+ while (this.IsPlaying)
+ {
+ this._waveFile.Read(ptr, array.Length);
+ if (this._hookManager != null)
+ {
+ this._hookManager.ProcessRawIQ(ptr, array.Length);
+ }
+ this._iqCircularBuffer.Write(ptr, array.Length);
+ }
+ }
+ }
+
+ private unsafe void ScaleBuffer(float* buffer, int length)
+ {
+ if (this.ScaleOutput)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ buffer[i] *= this._outputGain;
+ }
+ }
+ }
+
+ private unsafe void DSPProc()
+ {
+ if (this._dspOutBuffer == null || this._dspOutBuffer.Length != this._outputBufferSize)
+ {
+ this._dspOutBuffer = UnsafeBuffer.Create(this._outputBufferSize, 4);
+ this._dspOutPtr = (float*)(void*)this._dspOutBuffer;
+ }
+ while (this._isPlaying)
+ {
+ this._dspInPtr = this._iqCircularBuffer.Acquire();
+ if (this._dspInPtr == null)
+ {
+ break;
+ }
+ this.ProcessIQ();
+ this._iqCircularBuffer.Release();
+ this._audioCircularBuffer.Write(this._dspOutPtr, this._outputBufferSize);
+ }
+ }
+
+ private unsafe void ProcessIQ()
+ {
+ BufferNeededDelegate bufferNeeded = this.BufferNeeded;
+ if (bufferNeeded != null)
+ {
+ if (this._swapIQ)
+ {
+ this.SwapIQBuffer();
+ }
+ bufferNeeded(this._dspInPtr, this._dspOutPtr, this._inputBufferSize);
+ }
+ }
+
+ private unsafe void SwapIQBuffer()
+ {
+ for (int i = 0; i < this._inputBufferSize; i++)
+ {
+ float real = this._dspInPtr[i].Real;
+ this._dspInPtr[i].Real = this._dspInPtr[i].Imag;
+ this._dspInPtr[i].Imag = real;
+ }
+ }
+
+ public void Stop()
+ {
+ this._isPlaying = false;
+ if (this._inputType == InputType.Plugin && this._frontend is IIQStreamController)
+ {
+ ((IIQStreamController)this._frontend).Stop();
+ this._frontend = null;
+ }
+ if (this._iqCircularBuffer != null)
+ {
+ this._iqCircularBuffer.Close();
+ }
+ if (this._audioCircularBuffer != null)
+ {
+ this._audioCircularBuffer.Close();
+ }
+ if (this._wavePlayer != null)
+ {
+ this._wavePlayer.Dispose();
+ this._wavePlayer = null;
+ }
+ if (this._waveRecorder != null)
+ {
+ this._waveRecorder.Dispose();
+ this._waveRecorder = null;
+ }
+ if (this._waveDuplex != null)
+ {
+ this._waveDuplex.Dispose();
+ this._waveDuplex = null;
+ }
+ this._inputSampleRate = 0.0;
+ if (this._waveReadThread != null)
+ {
+ this._waveReadThread.Join();
+ this._waveReadThread = null;
+ }
+ if (this._dspThread != null)
+ {
+ this._dspThread.Join();
+ this._dspThread = null;
+ }
+ if (this._waveFile != null)
+ {
+ this._waveFile.Dispose();
+ this._waveFile = null;
+ }
+ this._iqCircularBuffer = null;
+ this._audioCircularBuffer = null;
+ this._dspOutBuffer = null;
+ }
+
+ public unsafe void Play()
+ {
+ if (this._wavePlayer == null && this._waveDuplex == null)
+ {
+ this._isPlaying = true;
+ try
+ {
+ switch (this._inputType)
+ {
+ case InputType.SoundCard:
+ if (this._inputDevice == this._outputDevice)
+ {
+ this._waveDuplex = new WaveDuplex(this._inputDevice, this._inputSampleRate, this._inputBufferSize, this.DuplexFiller);
+ }
+ else
+ {
+ this._iqCircularBuffer = new ComplexCircularBuffer(this._inputBufferSize, 6);
+ this._audioCircularBuffer = new FloatCircularBuffer(this._outputBufferSize, 2);
+ this._waveRecorder = new WaveRecorder(this._inputDevice, this._inputSampleRate, this._inputBufferSize, this.RecorderFiller);
+ this._wavePlayer = new WavePlayer(this._outputDevice, this._outputSampleRate, this._outputBufferSize / 2, this.PlayerFiller);
+ this._dspThread = new Thread(this.DSPProc);
+ this._dspThread.Start();
+ }
+ break;
+ case InputType.WaveFile:
+ this._iqCircularBuffer = new ComplexCircularBuffer(this._inputBufferSize, 6);
+ this._audioCircularBuffer = new FloatCircularBuffer(this._outputBufferSize, 2);
+ this._wavePlayer = new WavePlayer(this._outputDevice, this._outputSampleRate, this._outputBufferSize / 2, this.PlayerFiller);
+ this._waveReadThread = new Thread(this.WaveFileFiller);
+ this._waveReadThread.Start();
+ this._dspThread = new Thread(this.DSPProc);
+ this._dspThread.Start();
+ break;
+ case InputType.Plugin:
+ this._iqCircularBuffer = new ComplexCircularBuffer(this._inputBufferSize, 6);
+ this._audioCircularBuffer = new FloatCircularBuffer(this._outputBufferSize, 2);
+ this._wavePlayer = new WavePlayer(this._outputDevice, this._outputSampleRate, this._outputBufferSize / 2, this.PlayerFiller);
+ if (this._frontend is IIQStreamController)
+ {
+ ((IIQStreamController)this._frontend).Start(this.FrontendFiller);
+ }
+ this._dspThread = new Thread(this.DSPProc);
+ this._dspThread.Start();
+ break;
+ }
+ if (this._dspThread != null)
+ {
+ this._dspThread.Name = "DSP Thread";
+ }
+ }
+ catch
+ {
+ this._isPlaying = false;
+ throw;
+ }
+ }
+ }
+
+ public void OpenSoundDevice(int inputDevice, int outputDevice, double inputSampleRate, int bufferSizeInMs)
+ {
+ this.Stop();
+ this._inputType = InputType.SoundCard;
+ this._inputDevice = inputDevice;
+ this._outputDevice = outputDevice;
+ this._inputSampleRate = inputSampleRate;
+ this._inputBufferSize = (int)((double)bufferSizeInMs * this._inputSampleRate / 1000.0);
+ if (this._inputDevice == this._outputDevice)
+ {
+ this._decimationStageCount = 0;
+ this._outputSampleRate = this._inputSampleRate;
+ this._outputBufferSize = this._inputBufferSize * 2;
+ }
+ else
+ {
+ this._decimationStageCount = StreamControl.GetDecimationStageCount(this._inputSampleRate, DetectorType.AM);
+ int num = 1 << this._decimationStageCount;
+ int num2 = num * StreamControl._bufferAlignment;
+ this._inputBufferSize = this._inputBufferSize / num2 * num2;
+ this._outputSampleRate = this._inputSampleRate / (double)num;
+ this._outputBufferSize = this._inputBufferSize / num * 2;
+ }
+ this._bufferSizeInMs = (double)this._inputBufferSize / this._inputSampleRate * 1000.0;
+ }
+
+ public void OpenFile(string filename, int outputDevice, int bufferSizeInMs)
+ {
+ this.Stop();
+ try
+ {
+ this._inputType = InputType.WaveFile;
+ this._waveFile = new WaveFile(filename);
+ this._outputDevice = outputDevice;
+ this._inputSampleRate = (double)this._waveFile.SampleRate;
+ this._inputBufferSize = (int)((double)bufferSizeInMs * this._inputSampleRate / 1000.0);
+ this._decimationStageCount = StreamControl.GetDecimationStageCount(this._inputSampleRate, DetectorType.AM);
+ int num = 1 << this._decimationStageCount;
+ int num2 = num * StreamControl._bufferAlignment;
+ this._inputBufferSize = this._inputBufferSize / num2 * num2;
+ this._outputSampleRate = this._inputSampleRate / (double)num;
+ this._outputBufferSize = this._inputBufferSize / num * 2;
+ this._bufferSizeInMs = (double)this._inputBufferSize / this._inputSampleRate * 1000.0;
+ }
+ catch
+ {
+ this.Stop();
+ throw;
+ }
+ }
+
+ public void OpenPlugin(IFrontendController frontend, int outputDevice, int bufferSizeInMs)
+ {
+ this.Stop();
+ try
+ {
+ this._inputType = InputType.Plugin;
+ this._frontend = frontend;
+ if (frontend is IIQStreamController)
+ {
+ this._inputSampleRate = ((IIQStreamController)this._frontend).Samplerate;
+ this._outputDevice = outputDevice;
+ this._inputBufferSize = (int)((double)bufferSizeInMs * this._inputSampleRate / 1000.0);
+ if (this._inputBufferSize == 0)
+ {
+ throw new ArgumentException("The source '" + this._frontend + "' is not ready");
+ }
+ this._decimationStageCount = StreamControl.GetDecimationStageCount(this._inputSampleRate, DetectorType.AM);
+ int num = 1 << this._decimationStageCount;
+ int num2 = num * StreamControl._bufferAlignment;
+ this._inputBufferSize = this._inputBufferSize / num2 * num2;
+ this._outputSampleRate = this._inputSampleRate / (double)num;
+ this._outputBufferSize = this._inputBufferSize / num * 2;
+ this._bufferSizeInMs = (double)this._inputBufferSize / this._inputSampleRate * 1000.0;
+ goto end_IL_0006;
+ }
+ throw new ArgumentException("The source '" + this._frontend + "' is not ready");
+ end_IL_0006:;
+ }
+ catch
+ {
+ this.Stop();
+ throw;
+ }
+ }
+
+ public static int GetDecimationStageCount(double inputSampleRate, DetectorType detector = DetectorType.AM)
+ {
+ int num = 20;
+ int num2;
+ if (StreamControl.ReducedBandwidth)
+ {
+ if (inputSampleRate <= (double)StreamControl._minReducedNarrowBandwidth)
+ {
+ return 0;
+ }
+ num2 = ((detector == DetectorType.WFM) ? StreamControl._minReducedWideBandwidth : StreamControl._minReducedNarrowBandwidth);
+ }
+ else
+ {
+ if (inputSampleRate <= (double)StreamControl._minOutputSampleRate)
+ {
+ return 0;
+ }
+ num2 = ((detector == DetectorType.WFM) ? 250000 : StreamControl._minOutputSampleRate);
+ }
+ while (inputSampleRate / (double)(1 << num) < (double)num2 && num > 0)
+ {
+ num--;
+ }
+ return num;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/SyndromeDetector.cs b/SDRSharp.Radio/SDRSharp.Radio/SyndromeDetector.cs
new file mode 100644
index 0000000..4d15c82
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/SyndromeDetector.cs
@@ -0,0 +1,189 @@
+namespace SDRSharp.Radio
+{
+ public class SyndromeDetector
+ {
+ protected enum BlockSequence
+ {
+ GotA,
+ GotB,
+ GotC,
+ GotD,
+ WaitBitSync,
+ GotBitSync
+ }
+
+ private const int MaxCorrectableBits = 5;
+
+ private const int CheckwordBitsCount = 10;
+
+ private readonly RdsDumpGroups _dumpGroups;
+
+ private readonly ushort[] _blocks = new ushort[4];
+
+ private bool _useFec = Utils.GetBooleanSetting("RdsUseFec");
+
+ private BlockSequence _sequence = BlockSequence.WaitBitSync;
+
+ private ushort _syndrome;
+
+ private uint _raw;
+
+ private RdsFrame _frame;
+
+ private int _count;
+
+ public bool UseFEC
+ {
+ get
+ {
+ return this._useFec;
+ }
+ set
+ {
+ this._useFec = value;
+ }
+ }
+
+ public event RdsFrameAvailableDelegate FrameAvailable;
+
+ public SyndromeDetector(RdsDumpGroups dumpGroups)
+ {
+ this._dumpGroups = dumpGroups;
+ }
+
+ public void Clock(bool b)
+ {
+ this._raw <<= 1;
+ this._raw |= (uint)(b ? 1 : 0);
+ this._count++;
+ if (this._sequence == BlockSequence.WaitBitSync)
+ {
+ this._syndrome = SyndromeDetector.BuildSyndrome(this._raw);
+ this._syndrome ^= 984;
+ this._sequence = ((this._syndrome != 0) ? BlockSequence.WaitBitSync : BlockSequence.GotA);
+ this._blocks[0] = (ushort)(this._raw >> 10 & 0xFFFF);
+ this._count = 0;
+ }
+ if (this._count == 26)
+ {
+ this.ProcessSyndrome();
+ if (this._sequence == BlockSequence.GotD)
+ {
+ this._frame.GroupA = this._blocks[0];
+ this._frame.GroupB = this._blocks[1];
+ this._frame.GroupC = this._blocks[2];
+ this._frame.GroupD = this._blocks[3];
+ this._frame.Filter = false;
+ RdsFrameAvailableDelegate frameAvailable = this.FrameAvailable;
+ if (frameAvailable != null)
+ {
+ frameAvailable(ref this._frame);
+ }
+ if (!this._frame.Filter)
+ {
+ this._dumpGroups.AnalyseFrames(ref this._frame);
+ }
+ this._sequence = BlockSequence.GotBitSync;
+ }
+ this._count = 0;
+ }
+ }
+
+ private void ProcessSyndrome()
+ {
+ this._syndrome = SyndromeDetector.BuildSyndrome(this._raw);
+ switch (this._sequence)
+ {
+ case BlockSequence.GotBitSync:
+ this._syndrome ^= 984;
+ this._sequence = BlockSequence.GotA;
+ break;
+ case BlockSequence.GotA:
+ this._syndrome ^= 980;
+ this._sequence = BlockSequence.GotB;
+ break;
+ case BlockSequence.GotB:
+ this._syndrome ^= (ushort)(((this._blocks[1] & 0x800) == 0) ? 604 : 972);
+ this._sequence = BlockSequence.GotC;
+ break;
+ case BlockSequence.GotC:
+ this._syndrome ^= 600;
+ this._sequence = BlockSequence.GotD;
+ break;
+ }
+ int sequence = (int)this._sequence;
+ if (this._syndrome != 0)
+ {
+ if (this._useFec)
+ {
+ int num = this.ApplyFEC();
+ if (this._syndrome != 0 || num > 5)
+ {
+ this._sequence = BlockSequence.WaitBitSync;
+ }
+ else
+ {
+ this._blocks[sequence] = (ushort)(this._raw & 0xFFFF);
+ }
+ }
+ else
+ {
+ this._sequence = BlockSequence.WaitBitSync;
+ }
+ }
+ else
+ {
+ this._blocks[sequence] = (ushort)(this._raw >> 10 & 0xFFFF);
+ }
+ }
+
+ private int ApplyFEC()
+ {
+ uint num = 33554432u;
+ int num2 = 0;
+ for (int i = 0; i < 16; i++)
+ {
+ bool flag = (this._syndrome & 0x200) == 512;
+ bool flag2 = (this._syndrome & 0x20) == 0;
+ this._raw ^= ((flag & flag2) ? num : 0);
+ this._syndrome <<= 1;
+ this._syndrome ^= (ushort)((flag && !flag2) ? 1465 : 0);
+ num2 += ((flag & flag2) ? 1 : 0);
+ num >>= 1;
+ }
+ this._syndrome &= 1023;
+ return num2;
+ }
+
+ private static ushort BuildSyndrome(uint raw)
+ {
+ ushort[] array = new ushort[16]
+ {
+ 732,
+ 366,
+ 183,
+ 647,
+ 927,
+ 787,
+ 853,
+ 886,
+ 443,
+ 513,
+ 988,
+ 494,
+ 247,
+ 679,
+ 911,
+ 795
+ };
+ uint num = raw & 0x3FFFFFF;
+ ushort num2 = (ushort)(num >> 16);
+ for (int i = 0; i < 16; i++)
+ {
+ num2 = (ushort)(num2 ^ (((num & 0x8000) == 32768) ? array[i] : 0));
+ num <<= 1;
+ }
+ return num2;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/Trig.cs b/SDRSharp.Radio/SDRSharp.Radio/Trig.cs
new file mode 100644
index 0000000..f33ece4
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/Trig.cs
@@ -0,0 +1,100 @@
+using System;
+
+namespace SDRSharp.Radio
+{
+ public static class Trig
+ {
+ private const int ResolutionInBits = 16;
+
+ private static readonly int _mask;
+
+ private static readonly float _indexScale;
+
+ private static readonly UnsafeBuffer _sinBuffer;
+
+ private static readonly UnsafeBuffer _cosBuffer;
+
+ private unsafe static readonly float* _sinPtr;
+
+ private unsafe static readonly float* _cosPtr;
+
+ unsafe static Trig()
+ {
+ Trig._mask = 65535;
+ int num = Trig._mask + 1;
+ Trig._sinBuffer = UnsafeBuffer.Create(num, 4);
+ Trig._cosBuffer = UnsafeBuffer.Create(num, 4);
+ Trig._sinPtr = (float*)(void*)Trig._sinBuffer;
+ Trig._cosPtr = (float*)(void*)Trig._cosBuffer;
+ Trig._indexScale = (float)num / 6.28318548f;
+ for (int i = 0; i < num; i++)
+ {
+ Trig._sinPtr[i] = (float)Math.Sin((double)(((float)i + 0.5f) / (float)num * 6.28318548f));
+ Trig._cosPtr[i] = (float)Math.Cos((double)(((float)i + 0.5f) / (float)num * 6.28318548f));
+ }
+ for (float num2 = 0f; num2 < 6.28318548f; num2 += 1.57079637f)
+ {
+ Trig._sinPtr[(int)(num2 * Trig._indexScale) & Trig._mask] = (float)Math.Sin((double)num2);
+ Trig._cosPtr[(int)(num2 * Trig._indexScale) & Trig._mask] = (float)Math.Cos((double)num2);
+ }
+ }
+
+ public unsafe static float Sin(float angle)
+ {
+ return Trig._sinPtr[(int)(angle * Trig._indexScale) & Trig._mask];
+ }
+
+ public unsafe static float Cos(float angle)
+ {
+ return Trig._cosPtr[(int)(angle * Trig._indexScale) & Trig._mask];
+ }
+
+ public unsafe static Complex SinCos(float rad)
+ {
+ int num = (int)(rad * Trig._indexScale) & Trig._mask;
+ Complex result = default(Complex);
+ result.Real = Trig._cosPtr[num];
+ result.Imag = Trig._sinPtr[num];
+ return result;
+ }
+
+ public static float Atan2(float y, float x)
+ {
+ if ((double)x == 0.0)
+ {
+ if ((double)y > 0.0)
+ {
+ return 1.57079637f;
+ }
+ if ((double)y == 0.0)
+ {
+ return 0f;
+ }
+ return -1.57079637f;
+ }
+ float num = y / x;
+ float num2;
+ if ((double)Math.Abs(num) < 1.0)
+ {
+ num2 = num / (1f + 0.2854f * num * num);
+ if ((double)x < 0.0)
+ {
+ if ((double)y < 0.0)
+ {
+ return num2 - 3.14159274f;
+ }
+ return num2 + 3.14159274f;
+ }
+ }
+ else
+ {
+ num2 = 1.57079637f - num / (num * num + 0.2854f);
+ if ((double)y < 0.0)
+ {
+ return num2 - 3.14159274f;
+ }
+ }
+ return num2;
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/TuningStyle.cs b/SDRSharp.Radio/SDRSharp.Radio/TuningStyle.cs
new file mode 100644
index 0000000..baa41e2
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/TuningStyle.cs
@@ -0,0 +1,9 @@
+namespace SDRSharp.Radio
+{
+ public enum TuningStyle
+ {
+ Free,
+ Sticky,
+ Center
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/UnsafeBuffer.cs b/SDRSharp.Radio/SDRSharp.Radio/UnsafeBuffer.cs
new file mode 100644
index 0000000..6636977
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/UnsafeBuffer.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SDRSharp.Radio
+{
+ public sealed class UnsafeBuffer : IDisposable
+ {
+ private readonly GCHandle _handle;
+
+ private unsafe void* _ptr;
+
+ private int _length;
+
+ private Array _buffer;
+
+ public unsafe void* Address
+ {
+ get
+ {
+ return this._ptr;
+ }
+ }
+
+ public int Length
+ {
+ get
+ {
+ return this._length;
+ }
+ }
+
+ private unsafe UnsafeBuffer(Array buffer, int realLength, bool aligned)
+ {
+ this._buffer = buffer;
+ this._handle = GCHandle.Alloc(this._buffer, GCHandleType.Pinned);
+ this._ptr = (void*)this._handle.AddrOfPinnedObject();
+ if (aligned)
+ {
+ this._ptr = (void*)((long)this._ptr + 15 & -16);
+ }
+ this._length = realLength;
+ }
+
+ ~UnsafeBuffer()
+ {
+ this.Dispose();
+ }
+
+ public unsafe void Dispose()
+ {
+ GCHandle handle = this._handle;
+ if (handle.IsAllocated)
+ {
+ handle = this._handle;
+ handle.Free();
+ }
+ this._buffer = null;
+ this._ptr = null;
+ this._length = 0;
+ GC.SuppressFinalize(this);
+ }
+
+ public void Clear()
+ {
+ Array.Clear(this._buffer, 0, this._buffer.Length);
+ }
+
+ public unsafe static implicit operator void*(UnsafeBuffer unsafeBuffer)
+ {
+ return unsafeBuffer.Address;
+ }
+
+ public static UnsafeBuffer Create(int size)
+ {
+ return UnsafeBuffer.Create(size, 1, true);
+ }
+
+ public static UnsafeBuffer Create(int length, int sizeOfElement)
+ {
+ return UnsafeBuffer.Create(length, sizeOfElement, true);
+ }
+
+ public static UnsafeBuffer Create(int length, int sizeOfElement, bool aligned)
+ {
+ return new UnsafeBuffer(new byte[length * sizeOfElement + (aligned ? 16 : 0)], length, aligned);
+ }
+
+ public static UnsafeBuffer Create(Array buffer)
+ {
+ return new UnsafeBuffer(buffer, buffer.Length, false);
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/Utils.cs b/SDRSharp.Radio/SDRSharp.Radio/Utils.cs
new file mode 100644
index 0000000..ae57eb1
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/Utils.cs
@@ -0,0 +1,351 @@
+using System;
+using System.Configuration;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Globalization;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace SDRSharp.Radio
+{
+ public static class Utils
+ {
+ private const string Libc = "msvcrt.dll";
+
+ public unsafe static void ManagedMemcpy(void* dest, void* src, int len)
+ {
+ byte* ptr = (byte*)dest;
+ byte* ptr2 = (byte*)src;
+ if (len >= 16)
+ {
+ do
+ {
+ *(int*)ptr = *(int*)ptr2;
+ *(int*)(ptr + 4) = *(int*)(ptr2 + 4);
+ *(int*)(ptr + 2L * 4L) = *(int*)(ptr2 + 2L * 4L);
+ *(int*)(ptr + 3L * 4L) = *(int*)(ptr2 + 3L * 4L);
+ ptr += 16;
+ ptr2 += 16;
+ }
+ while ((len -= 16) >= 16);
+ }
+ if (len > 0)
+ {
+ if ((len & 8) != 0)
+ {
+ *(int*)ptr = *(int*)ptr2;
+ *(int*)(ptr + 4) = *(int*)(ptr2 + 4);
+ ptr += 8;
+ ptr2 += 8;
+ }
+ if ((len & 4) != 0)
+ {
+ *(int*)ptr = *(int*)ptr2;
+ ptr += 4;
+ ptr2 += 4;
+ }
+ if ((len & 2) != 0)
+ {
+ *(short*)ptr = *(short*)ptr2;
+ ptr += 2;
+ ptr2 += 2;
+ }
+ if ((len & 1) != 0)
+ {
+ *ptr = *ptr2;
+ }
+ }
+ }
+
+ [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "memmove")]
+ public unsafe static extern void* Memmove(void* dest, void* src, int len);
+
+ [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "memcpy")]
+ public unsafe static extern void* Memcpy(void* dest, void* src, int len);
+
+ [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "memset")]
+ public unsafe static extern void* Memset(void* dest, int value, int len);
+
+ [DllImport("winmm.dll", EntryPoint = "timeBeginPeriod", SetLastError = true)]
+ public static extern uint TimeBeginPeriod(uint uMilliseconds);
+
+ [DllImport("winmm.dll", EntryPoint = "timeEndPeriod", SetLastError = true)]
+ public static extern uint TimeEndPeriod(uint uMilliseconds);
+
+ public static double GetDoubleSetting(string name, double defaultValue)
+ {
+ double result;
+ if (double.TryParse(ConfigurationManager.AppSettings[name], NumberStyles.Number, (IFormatProvider)CultureInfo.InvariantCulture, out result))
+ {
+ return result;
+ }
+ return defaultValue;
+ }
+
+ public static bool GetBooleanSetting(string name)
+ {
+ return Utils.GetBooleanSetting(name, false);
+ }
+
+ public static bool GetBooleanSetting(string name, bool defaultValue)
+ {
+ string text;
+ try
+ {
+ text = (ConfigurationManager.AppSettings[name] ?? string.Empty);
+ }
+ catch
+ {
+ return defaultValue;
+ }
+ if (string.IsNullOrEmpty(text))
+ {
+ return defaultValue;
+ }
+ return "YyTt".IndexOf(text[0]) >= 0;
+ }
+
+ public static Color GetColorSetting(string name, Color defaultColor)
+ {
+ return Utils.GetColorSetting(name, defaultColor, byte.MaxValue);
+ }
+
+ public static Color GetColorSetting(string name, Color defaultColor, byte alpha)
+ {
+ try
+ {
+ string text = ConfigurationManager.AppSettings[name];
+ int red = int.Parse(text.Substring(0, 2), NumberStyles.HexNumber);
+ int green = int.Parse(text.Substring(2, 2), NumberStyles.HexNumber);
+ int blue = int.Parse(text.Substring(4, 2), NumberStyles.HexNumber);
+ return Color.FromArgb(alpha, red, green, blue);
+ }
+ catch
+ {
+ return defaultColor;
+ }
+ }
+
+ public static ColorBlend GetGradientBlend(int alpha, string settingName)
+ {
+ ColorBlend colorBlend = new ColorBlend();
+ string text;
+ try
+ {
+ text = (ConfigurationManager.AppSettings[settingName] ?? string.Empty);
+ }
+ catch
+ {
+ text = string.Empty;
+ }
+ string[] array = text.Split(',');
+ if (array.Length < 2)
+ {
+ colorBlend.Colors = new Color[6]
+ {
+ Color.White,
+ Color.LightBlue,
+ Color.DodgerBlue,
+ Color.FromArgb(0, 0, 80),
+ Color.Black,
+ Color.Black
+ };
+ for (int i = 0; i < colorBlend.Colors.Length; i++)
+ {
+ colorBlend.Colors[i] = Color.FromArgb(alpha, colorBlend.Colors[i]);
+ }
+ }
+ else
+ {
+ colorBlend.Colors = new Color[array.Length];
+ for (int j = 0; j < array.Length; j++)
+ {
+ string obj2 = array[j];
+ int red = int.Parse(obj2.Substring(0, 2), NumberStyles.HexNumber);
+ int green = int.Parse(obj2.Substring(2, 2), NumberStyles.HexNumber);
+ int blue = int.Parse(obj2.Substring(4, 2), NumberStyles.HexNumber);
+ colorBlend.Colors[j] = Color.FromArgb(red, green, blue);
+ }
+ }
+ float[] array2 = new float[colorBlend.Colors.Length];
+ float num = 1f / (float)(array2.Length - 1);
+ for (int k = 0; k < array2.Length; k++)
+ {
+ byte r = colorBlend.Colors[k].R;
+ byte g = colorBlend.Colors[k].G;
+ byte b = colorBlend.Colors[k].B;
+ colorBlend.Colors[k] = Color.FromArgb(alpha, r, g, b);
+ array2[k] = (float)k * num;
+ }
+ colorBlend.Positions = array2;
+ return colorBlend;
+ }
+
+ public static GraphicsPath RoundedRect(RectangleF bounds, int radius)
+ {
+ int num = radius * 2;
+ SizeF size = new SizeF((float)num, (float)num);
+ RectangleF rect = new RectangleF(bounds.Location, size);
+ GraphicsPath graphicsPath = new GraphicsPath();
+ if (radius == 0)
+ {
+ graphicsPath.AddRectangle(bounds);
+ return graphicsPath;
+ }
+ graphicsPath.AddArc(rect, 180f, 90f);
+ rect.X = bounds.Right - (float)num;
+ graphicsPath.AddArc(rect, 270f, 90f);
+ rect.Y = bounds.Bottom - (float)num;
+ graphicsPath.AddArc(rect, 0f, 90f);
+ rect.X = bounds.Left;
+ graphicsPath.AddArc(rect, 90f, 90f);
+ graphicsPath.CloseFigure();
+ return graphicsPath;
+ }
+
+ public static void DrawRoundedRectangle(this Graphics graphics, Pen pen, RectangleF bounds, int cornerRadius)
+ {
+ if (graphics == null)
+ {
+ throw new ArgumentNullException("graphics");
+ }
+ if (pen == null)
+ {
+ throw new ArgumentNullException("pen");
+ }
+ using (GraphicsPath path = Utils.RoundedRect(bounds, cornerRadius))
+ {
+ graphics.DrawPath(pen, path);
+ }
+ }
+
+ public static void FillRoundedRectangle(this Graphics graphics, Brush brush, RectangleF bounds, int cornerRadius)
+ {
+ if (graphics == null)
+ {
+ throw new ArgumentNullException("graphics");
+ }
+ if (brush == null)
+ {
+ throw new ArgumentNullException("brush");
+ }
+ using (GraphicsPath path = Utils.RoundedRect(bounds, cornerRadius))
+ {
+ graphics.FillPath(brush, path);
+ }
+ }
+
+ public static string GetFrequencyDisplay(long frequency, bool appendHz)
+ {
+ string str = (frequency != 0L) ? ((Math.Abs(frequency) < 1000000000) ? ((Math.Abs(frequency) < 1000000) ? ((Math.Abs(frequency) < 1000) ? string.Format("{0} ", frequency) : string.Format("{0:#,#.###} k", (double)frequency / 1000.0)) : string.Format("{0:#,0.000#} M", (double)frequency / 1000000.0)) : string.Format("{0:#,0.000 000} G", (double)frequency / 1000000000.0)) : "0";
+ return str + ((frequency == 0L || !appendHz) ? string.Empty : "Hz");
+ }
+
+ public static int GetIntSetting(string name, int defaultValue)
+ {
+ int result;
+ if (int.TryParse(ConfigurationManager.AppSettings[name], out result))
+ {
+ return result;
+ }
+ return defaultValue;
+ }
+
+ public static long GetLongSetting(string name, long defaultValue)
+ {
+ long result;
+ if (long.TryParse(ConfigurationManager.AppSettings[name], out result))
+ {
+ return result;
+ }
+ return defaultValue;
+ }
+
+ public static string IntArrayToString(params int[] values)
+ {
+ StringBuilder stringBuilder = new StringBuilder();
+ foreach (int value in values)
+ {
+ stringBuilder.Append(value);
+ stringBuilder.Append(',');
+ }
+ return stringBuilder.ToString().TrimEnd(',');
+ }
+
+ public static string ColorToString(Color color)
+ {
+ return string.Format("{0:X2}{1:X2}{2:X2}{3:X2}", color.A, color.R, color.G, color.B);
+ }
+
+ public static Color StringToColor(string code, byte defaultTransparency = byte.MaxValue)
+ {
+ if (string.IsNullOrEmpty(code))
+ {
+ return Color.Empty;
+ }
+ int argb;
+ if (int.TryParse(code, NumberStyles.HexNumber, (IFormatProvider)null, out argb))
+ {
+ return Color.FromArgb(argb);
+ }
+ Color baseColor = Color.FromName(code);
+ if (!baseColor.IsKnownColor)
+ {
+ return Color.Empty;
+ }
+ return Color.FromArgb(defaultTransparency, baseColor);
+ }
+
+ public static int[] GetIntArraySetting(string name, int[] defaultValue)
+ {
+ try
+ {
+ string text = ConfigurationManager.AppSettings[name];
+ if (string.IsNullOrEmpty(text))
+ {
+ return defaultValue;
+ }
+ string[] array = text.Split(',');
+ if (defaultValue != null && defaultValue.Length != array.Length)
+ {
+ return defaultValue;
+ }
+ int[] array2 = new int[array.Length];
+ for (int i = 0; i < array2.Length; i++)
+ {
+ array2[i] = int.Parse(array[i]);
+ }
+ return array2;
+ }
+ catch
+ {
+ return defaultValue;
+ }
+ }
+
+ public static string GetStringSetting(string name, string defaultValue)
+ {
+ string text = ConfigurationManager.AppSettings[name];
+ if (string.IsNullOrEmpty(text))
+ {
+ return defaultValue;
+ }
+ return text;
+ }
+
+ public static void SaveSetting(string key, object value)
+ {
+ string value2 = Convert.ToString(value, CultureInfo.InvariantCulture);
+ Utils.SaveSetting(key, value2);
+ }
+
+ public static void SaveSetting(string key, string value)
+ {
+ Configuration configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
+ configuration.AppSettings.Settings.Remove(key);
+ configuration.AppSettings.Settings.Add(key, value);
+ configuration.Save(ConfigurationSaveMode.Full);
+ ConfigurationManager.RefreshSection("appSettings");
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/Vfo.cs b/SDRSharp.Radio/SDRSharp.Radio/Vfo.cs
new file mode 100644
index 0000000..af3ea38
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/Vfo.cs
@@ -0,0 +1,871 @@
+using System;
+
+namespace SDRSharp.Radio
+{
+ public sealed class Vfo
+ {
+ private const float NFMDeemphasisTime = 0.000149999993f;
+
+ public const int DefaultCwSideTone = 600;
+
+ public const int DefaultSSBBandwidth = 2400;
+
+ public const int DefaultWFMBandwidth = 250000;
+
+ public const int MinSSBAudioFrequency = 100;
+
+ public const int MinBCAudioFrequency = 20;
+
+ public const int MaxBCAudioFrequency = 15000;
+
+ public const int MaxNFMBandwidth = 15000;
+
+ public const int MinNFMAudioFrequency = 300;
+
+ public const int MaxNFMAudioFrequency = 3800;
+
+ private readonly double _minThreadedSampleRate = Utils.GetDoubleSetting("minThreadedSampleRate", 1000000.0);
+
+ private readonly AutomaticGainControl _agc = new AutomaticGainControl();
+
+ private readonly AmDetector _amDetector = new AmDetector();
+
+ private readonly FmDetector _fmDetector = new FmDetector();
+
+ private readonly SideBandDetector _sideBandDetector = new SideBandDetector();
+
+ private readonly CwDetector _cwDetector = new CwDetector();
+
+ private readonly StereoDecoder _stereoDecoder = new StereoDecoder();
+
+ private readonly RdsDecoder _rdsDecoder = new RdsDecoder();
+
+ private readonly CarrierLocker _carrierLocker = new CarrierLocker();
+
+ private readonly AmAntiFading _amAntiFading = new AmAntiFading();
+
+ private readonly HookManager _hookManager;
+
+ private DownConverter _mainDownConverter;
+
+ private FrequencyTranslator _ifOffsetTranslator;
+
+ private ComplexFilter _iqFilter;
+
+ private ComplexFilter _audioFIR;
+
+ private IirFilter _audioIIR;
+
+ private DetectorType _detectorType;
+
+ private DetectorType _actualDetectorType;
+
+ private WindowType _windowType;
+
+ private double _sampleRate;
+
+ private int _bandwidth;
+
+ private int _frequency;
+
+ private int _ifOffset;
+
+ private int _filterOrder;
+
+ private int _decimationStageCount;
+
+ private int _baseBandDecimationStageCount;
+
+ private int _audioDecimationStageCount;
+
+ private int _cwToneShift;
+
+ private bool _needConfigure;
+
+ private bool _lockCarrier;
+
+ private bool _useAgc;
+
+ private float _agcThreshold;
+
+ private float _agcDecay;
+
+ private float _agcSlope;
+
+ private bool _agcUseHang;
+
+ private bool _useAntiFading;
+
+ private float _deemphasisAlpha;
+
+ private float _deemphasisState;
+
+ private int _squelchThreshold;
+
+ private bool _fmStereo;
+
+ private bool _filterAudio;
+
+ private bool _bypassDemodulation;
+
+ private bool _muted;
+
+ private bool _hooksEnabled;
+
+ private UnsafeBuffer _rawAudioBuffer;
+
+ private unsafe float* _rawAudioPtr;
+
+ public DetectorType DetectorType
+ {
+ get
+ {
+ return this._detectorType;
+ }
+ set
+ {
+ if (value != this._detectorType)
+ {
+ this._detectorType = value;
+ this._needConfigure = true;
+ }
+ }
+ }
+
+ public int Frequency
+ {
+ get
+ {
+ return this._frequency;
+ }
+ set
+ {
+ if (this._frequency != value)
+ {
+ this._frequency = value;
+ this._needConfigure = true;
+ this._carrierLocker.Reset();
+ }
+ }
+ }
+
+ public int IFOffset
+ {
+ get
+ {
+ return this._ifOffset;
+ }
+ set
+ {
+ if (this._ifOffset != value)
+ {
+ this._ifOffset = value;
+ this._needConfigure = true;
+ this._carrierLocker.Reset();
+ }
+ }
+ }
+
+ public int FilterOrder
+ {
+ get
+ {
+ return this._filterOrder;
+ }
+ set
+ {
+ if (this._filterOrder != value)
+ {
+ this._filterOrder = value;
+ this._needConfigure = true;
+ }
+ }
+ }
+
+ public double SampleRate
+ {
+ get
+ {
+ return this._sampleRate;
+ }
+ set
+ {
+ if (this._sampleRate != value)
+ {
+ this._sampleRate = value;
+ this._needConfigure = true;
+ }
+ }
+ }
+
+ public WindowType WindowType
+ {
+ get
+ {
+ return this._windowType;
+ }
+ set
+ {
+ if (this._windowType != value)
+ {
+ this._windowType = value;
+ this._needConfigure = true;
+ }
+ }
+ }
+
+ public int Bandwidth
+ {
+ get
+ {
+ return this._bandwidth;
+ }
+ set
+ {
+ if (this._bandwidth != value)
+ {
+ this._bandwidth = value;
+ this._needConfigure = true;
+ }
+ }
+ }
+
+ public bool UseAGC
+ {
+ get
+ {
+ return this._useAgc;
+ }
+ set
+ {
+ this._useAgc = value;
+ }
+ }
+
+ public float AgcThreshold
+ {
+ get
+ {
+ return this._agcThreshold;
+ }
+ set
+ {
+ if (this._agcThreshold != value)
+ {
+ this._agcThreshold = value;
+ this._needConfigure = true;
+ }
+ }
+ }
+
+ public float AgcDecay
+ {
+ get
+ {
+ return this._agcDecay;
+ }
+ set
+ {
+ if (this._agcDecay != value)
+ {
+ this._agcDecay = value;
+ this._needConfigure = true;
+ }
+ }
+ }
+
+ public float AgcSlope
+ {
+ get
+ {
+ return this._agcSlope;
+ }
+ set
+ {
+ if (this._agcSlope != value)
+ {
+ this._agcSlope = value;
+ this._needConfigure = true;
+ }
+ }
+ }
+
+ public bool AgcHang
+ {
+ get
+ {
+ return this._agcUseHang;
+ }
+ set
+ {
+ if (this._agcUseHang != value)
+ {
+ this._agcUseHang = value;
+ this._needConfigure = true;
+ }
+ }
+ }
+
+ public bool UseAntiFading
+ {
+ get
+ {
+ return this._useAntiFading;
+ }
+ set
+ {
+ this._useAntiFading = value;
+ }
+ }
+
+ public int SquelchThreshold
+ {
+ get
+ {
+ return this._squelchThreshold;
+ }
+ set
+ {
+ if (this._squelchThreshold != value)
+ {
+ this._squelchThreshold = value;
+ this._needConfigure = true;
+ }
+ }
+ }
+
+ public bool IsSquelchOpen
+ {
+ get
+ {
+ if (this._actualDetectorType == DetectorType.NFM && this._fmDetector.IsSquelchOpen)
+ {
+ return true;
+ }
+ if (this._actualDetectorType == DetectorType.AM)
+ {
+ return this._amDetector.IsSquelchOpen;
+ }
+ return false;
+ }
+ }
+
+ public int DecimationStageCount
+ {
+ get
+ {
+ return this._decimationStageCount;
+ }
+ set
+ {
+ if (this._decimationStageCount != value)
+ {
+ this._decimationStageCount = value;
+ this._needConfigure = true;
+ }
+ }
+ }
+
+ public int CWToneShift
+ {
+ get
+ {
+ return this._cwToneShift;
+ }
+ set
+ {
+ if (this._cwToneShift != value)
+ {
+ this._cwToneShift = value;
+ this._needConfigure = true;
+ }
+ }
+ }
+
+ public bool LockCarrier
+ {
+ get
+ {
+ return this._lockCarrier;
+ }
+ set
+ {
+ if (this._lockCarrier != value)
+ {
+ this._lockCarrier = value;
+ this._needConfigure = true;
+ }
+ }
+ }
+
+ public bool FmStereo
+ {
+ get
+ {
+ return this._fmStereo;
+ }
+ set
+ {
+ if (this._fmStereo != value)
+ {
+ this._fmStereo = value;
+ this._needConfigure = true;
+ }
+ }
+ }
+
+ public bool Muted
+ {
+ get
+ {
+ return this._muted;
+ }
+ set
+ {
+ this._muted = value;
+ }
+ }
+
+ public bool HookdEnabled
+ {
+ get
+ {
+ return this._hooksEnabled;
+ }
+ set
+ {
+ this._hooksEnabled = value;
+ }
+ }
+
+ public bool SignalIsStereo
+ {
+ get
+ {
+ if (this._actualDetectorType == DetectorType.WFM && this._fmStereo)
+ {
+ return this._stereoDecoder.IsPllLocked;
+ }
+ return false;
+ }
+ }
+
+ public string RdsStationName
+ {
+ get
+ {
+ return this._rdsDecoder.ProgramService;
+ }
+ }
+
+ public string RdsStationText
+ {
+ get
+ {
+ return this._rdsDecoder.RadioText;
+ }
+ }
+
+ public ushort RdsPICode
+ {
+ get
+ {
+ return this._rdsDecoder.PICode;
+ }
+ }
+
+ public bool RdsUseFEC
+ {
+ get
+ {
+ return this._rdsDecoder.UseFEC;
+ }
+ set
+ {
+ this._rdsDecoder.UseFEC = value;
+ }
+ }
+
+ public bool FilterAudio
+ {
+ get
+ {
+ return this._filterAudio;
+ }
+ set
+ {
+ this._filterAudio = value;
+ }
+ }
+
+ public bool BypassDemodulation
+ {
+ get
+ {
+ return this._bypassDemodulation;
+ }
+ set
+ {
+ this._bypassDemodulation = value;
+ }
+ }
+
+ public double BasebandSampleRate
+ {
+ get
+ {
+ return this._sampleRate / (double)(1 << this._baseBandDecimationStageCount);
+ }
+ }
+
+ public Vfo(HookManager hookManager = null)
+ {
+ this._hookManager = hookManager;
+ this._bandwidth = 2400;
+ this._filterOrder = 500;
+ this._rdsDecoder.RdsFrameAvailable += this.RdsFrameAvailableHandler;
+ this._needConfigure = true;
+ }
+
+ public void RdsReset()
+ {
+ this._rdsDecoder.Reset();
+ }
+
+ public void CarrierLockerReset()
+ {
+ this._carrierLocker.Reset();
+ }
+
+ private void ConfigureHookSampleRates()
+ {
+ if (this._hookManager != null)
+ {
+ this._hookManager.SetProcessorSampleRate(ProcessorType.RawIQ, this._sampleRate);
+ this._hookManager.SetProcessorSampleRate(ProcessorType.DecimatedAndFilteredIQ, this._sampleRate / (double)(1 << this._baseBandDecimationStageCount));
+ this._hookManager.SetProcessorSampleRate(ProcessorType.DemodulatorOutput, this._sampleRate / (double)(1 << this._baseBandDecimationStageCount));
+ this._hookManager.SetProcessorSampleRate(ProcessorType.FMMPX, this._sampleRate / (double)(1 << this._baseBandDecimationStageCount));
+ this._hookManager.SetProcessorSampleRate(ProcessorType.FilteredAudioOutput, this._sampleRate / (double)(1 << this._decimationStageCount));
+ }
+ }
+
+ public void Init()
+ {
+ this.Configure(false);
+ }
+
+ private void Configure(bool refreshOnly = true)
+ {
+ if (this._sampleRate != 0.0)
+ {
+ this._actualDetectorType = this._detectorType;
+ bool refresh = false;
+ this._baseBandDecimationStageCount = StreamControl.GetDecimationStageCount(this._sampleRate, this._actualDetectorType);
+ this._audioDecimationStageCount = this._decimationStageCount - this._baseBandDecimationStageCount;
+ int num = 1 << this._baseBandDecimationStageCount;
+ double num2 = this._sampleRate / (double)num;
+ if (!refreshOnly || this._mainDownConverter == null || this._mainDownConverter.SampleRate != this._sampleRate || this._mainDownConverter.DecimationRatio != num)
+ {
+ this._mainDownConverter = new DownConverter(this._sampleRate, num);
+ refresh = true;
+ this.ConfigureHookSampleRates();
+ }
+ this._mainDownConverter.Frequency = (double)this._frequency;
+ if (!refreshOnly || this._ifOffsetTranslator == null || this._ifOffsetTranslator.SampleRate != num2)
+ {
+ this._ifOffsetTranslator = new FrequencyTranslator(num2);
+ }
+ this._ifOffsetTranslator.Frequency = (double)(-this._ifOffset);
+ this.UpdateFilters(refresh);
+ this._carrierLocker.SampleRate = num2;
+ this._cwDetector.SampleRate = num2;
+ this._fmDetector.SampleRate = num2;
+ this._fmDetector.SquelchThreshold = this._squelchThreshold;
+ this._amDetector.SquelchThreshold = this._squelchThreshold;
+ this._stereoDecoder.Configure(this._fmDetector.SampleRate, this._audioDecimationStageCount);
+ this._rdsDecoder.SampleRate = this._fmDetector.SampleRate;
+ this._stereoDecoder.ForceMono = !this._fmStereo;
+ switch (this._actualDetectorType)
+ {
+ case DetectorType.CW:
+ this._cwDetector.BfoFrequency = this._cwToneShift;
+ break;
+ case DetectorType.NFM:
+ this._fmDetector.Mode = FmMode.Narrow;
+ break;
+ case DetectorType.WFM:
+ this._fmDetector.Mode = FmMode.Wide;
+ break;
+ }
+ this._agc.SampleRate = this._sampleRate / (double)(1 << this._decimationStageCount);
+ this._agc.Decay = this._agcDecay;
+ this._agc.Slope = this._agcSlope;
+ this._agc.Threshold = this._agcThreshold;
+ this._agc.UseHang = this._agcUseHang;
+ this._needConfigure = false;
+ }
+ }
+
+ private void UpdateFilters(bool refresh)
+ {
+ int num = 0;
+ int num2 = 15000;
+ int num3 = 0;
+ switch (this._actualDetectorType)
+ {
+ case DetectorType.WFM:
+ case DetectorType.AM:
+ num = 20;
+ num2 = Math.Min(this._bandwidth / 2, 15000);
+ break;
+ case DetectorType.CW:
+ num = Math.Abs(this._cwToneShift) - this._bandwidth / 2;
+ num2 = Math.Abs(this._cwToneShift) + this._bandwidth / 2;
+ break;
+ case DetectorType.USB:
+ num = (this._lockCarrier ? 20 : 100);
+ num2 = this._bandwidth;
+ num3 = this._bandwidth / 2;
+ break;
+ case DetectorType.LSB:
+ num = (this._lockCarrier ? 20 : 100);
+ num2 = this._bandwidth;
+ num3 = -this._bandwidth / 2;
+ break;
+ case DetectorType.DSB:
+ num = 20;
+ num2 = this._bandwidth / 2;
+ break;
+ case DetectorType.NFM:
+ num = 300;
+ num2 = Math.Min(this._bandwidth / 2, 3800);
+ break;
+ }
+ Complex[] array = FilterBuilder.MakeComplexKernel(this._sampleRate / (double)(1 << this._baseBandDecimationStageCount), this._filterOrder, (double)this._bandwidth, (double)num3, this._windowType);
+ if ((this._iqFilter == null | refresh) || this._iqFilter.KernelSize != array.Length)
+ {
+ this._iqFilter = new ComplexFilter(array);
+ }
+ else
+ {
+ this._iqFilter.SetKernel(array);
+ }
+ double num4 = this._sampleRate / (double)(1 << this._decimationStageCount);
+ if (refresh)
+ {
+ if (this._actualDetectorType == DetectorType.CW)
+ {
+ this._audioIIR.Init(IirFilterType.BandPass, (double)Math.Abs(this._cwToneShift), num4, 3.0);
+ }
+ else if (this._actualDetectorType == DetectorType.WFM)
+ {
+ double sampleRate = this._sampleRate / (double)(1 << this._baseBandDecimationStageCount);
+ this._audioIIR.Init(IirFilterType.HighPass, (double)num, sampleRate, 1.0);
+ }
+ else
+ {
+ this._audioIIR.Init(IirFilterType.HighPass, (double)num, num4, 1.0);
+ }
+ }
+ Complex[] array2 = FilterBuilder.MakeComplexKernel(num4, this._filterOrder, (double)(num2 - num), (double)(num2 + num) * 0.5, this._windowType);
+ if ((this._audioFIR == null | refresh) || this._audioFIR.KernelSize != array2.Length)
+ {
+ this._audioFIR = new ComplexFilter(array2);
+ }
+ else
+ {
+ this._audioFIR.SetKernel(array2);
+ }
+ this._deemphasisAlpha = (float)(1.0 - Math.Exp(-1.0 / (num4 * 0.00014999999257270247)));
+ this._deemphasisState = 0f;
+ }
+
+ public unsafe void ProcessBuffer(Complex* iqBuffer, float* audioBuffer, int length)
+ {
+ if (this._needConfigure)
+ {
+ this.Configure(true);
+ }
+ length = this._mainDownConverter.Process(iqBuffer, length);
+ this._ifOffsetTranslator.Process(iqBuffer, length);
+ if (this._lockCarrier && (this._actualDetectorType == DetectorType.LSB || this._actualDetectorType == DetectorType.USB))
+ {
+ this._carrierLocker.Process(iqBuffer, length);
+ }
+ this._iqFilter.Process(iqBuffer, length);
+ if (this._hookManager != null && this._hooksEnabled)
+ {
+ this._hookManager.ProcessDecimatedAndFilteredIQ(iqBuffer, length);
+ }
+ if (this._lockCarrier && (this._actualDetectorType == DetectorType.DSB || this._actualDetectorType == DetectorType.AM))
+ {
+ this._carrierLocker.Process(iqBuffer, length);
+ }
+ if (this._lockCarrier && this._useAntiFading && this._carrierLocker.IsLocked && (this._actualDetectorType == DetectorType.DSB || this._actualDetectorType == DetectorType.AM))
+ {
+ this._amAntiFading.Process(iqBuffer, length);
+ }
+ if (this._actualDetectorType == DetectorType.RAW)
+ {
+ Utils.Memcpy(audioBuffer, iqBuffer, length * sizeof(Complex));
+ if (this._hookManager != null && this._hooksEnabled)
+ {
+ this._hookManager.ProcessFilteredAudioOutput(audioBuffer, length * 2);
+ }
+ if (this._muted)
+ {
+ Vfo.MuteAudio(audioBuffer, length * 2);
+ }
+ }
+ else
+ {
+ if (this._rawAudioBuffer == null || this._rawAudioBuffer.Length != length)
+ {
+ this._rawAudioBuffer = UnsafeBuffer.Create(length, 4);
+ this._rawAudioPtr = (float*)(void*)this._rawAudioBuffer;
+ }
+ if (this._actualDetectorType != DetectorType.WFM)
+ {
+ Vfo.ScaleIQ(iqBuffer, length);
+ }
+ if (this._bypassDemodulation)
+ {
+ if (this._actualDetectorType == DetectorType.WFM)
+ {
+ length >>= this._audioDecimationStageCount;
+ }
+ length <<= 1;
+ Vfo.MuteAudio(audioBuffer, length);
+ }
+ else
+ {
+ this.Demodulate(iqBuffer, this._rawAudioPtr, length);
+ if (this._hookManager != null && this._hooksEnabled)
+ {
+ this._hookManager.ProcessDemodulatorOutput(this._rawAudioPtr, length);
+ }
+ switch (this._actualDetectorType)
+ {
+ case DetectorType.WFM:
+ if (this._filterAudio)
+ {
+ this._audioIIR.Process(this._rawAudioPtr, length);
+ }
+ if (this._hookManager != null && this._hooksEnabled)
+ {
+ this._hookManager.ProcessFMMPX(this._rawAudioPtr, length);
+ }
+ this._rdsDecoder.Process(this._rawAudioPtr, length);
+ this._stereoDecoder.Process(this._rawAudioPtr, audioBuffer, length);
+ length >>= this._audioDecimationStageCount;
+ break;
+ case DetectorType.NFM:
+ if (this._filterAudio)
+ {
+ this._audioIIR.Process(this._rawAudioPtr, length);
+ this._audioFIR.Process(this._rawAudioPtr, length, 1);
+ this.Deemphasis(this._rawAudioPtr, length);
+ }
+ if (this._useAgc)
+ {
+ this._agc.Process(this._rawAudioPtr, length);
+ }
+ Vfo.MonoToStereo(this._rawAudioPtr, audioBuffer, length);
+ break;
+ default:
+ if (this._useAgc)
+ {
+ this._agc.Process(this._rawAudioPtr, length);
+ }
+ if (this._filterAudio)
+ {
+ this._audioIIR.Process(this._rawAudioPtr, length);
+ this._audioFIR.Process(this._rawAudioPtr, length, 1);
+ }
+ Vfo.MonoToStereo(this._rawAudioPtr, audioBuffer, length);
+ break;
+ }
+ length <<= 1;
+ if (this._hookManager != null && this._hooksEnabled)
+ {
+ this._hookManager.ProcessFilteredAudioOutput(audioBuffer, length);
+ }
+ if (this._muted)
+ {
+ Vfo.MuteAudio(audioBuffer, length);
+ }
+ }
+ }
+ }
+
+ private unsafe static void MuteAudio(float* buffer, int length)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ buffer[i] = 0f;
+ }
+ }
+
+ private unsafe static void ScaleIQ(Complex* buffer, int length)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ buffer[i].Real *= 0.01f;
+ buffer[i].Imag *= 0.01f;
+ }
+ }
+
+ private unsafe static void MonoToStereo(float* input, float* output, int inputLength)
+ {
+ for (int i = 0; i < inputLength; i++)
+ {
+ float* intPtr = output;
+ output = intPtr + 1;
+ *intPtr = *input;
+ float* intPtr2 = output;
+ output = intPtr2 + 1;
+ *intPtr2 = *input;
+ input++;
+ }
+ }
+
+ private unsafe void Deemphasis(float* buffer, int length)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ this._deemphasisState += this._deemphasisAlpha * (buffer[i] - this._deemphasisState);
+ buffer[i] = this._deemphasisState;
+ }
+ }
+
+ private unsafe void Demodulate(Complex* iq, float* audio, int length)
+ {
+ switch (this._actualDetectorType)
+ {
+ case DetectorType.NFM:
+ case DetectorType.WFM:
+ this._fmDetector.Demodulate(iq, audio, length);
+ break;
+ case DetectorType.AM:
+ this._amDetector.Demodulate(iq, audio, length);
+ break;
+ case DetectorType.DSB:
+ case DetectorType.LSB:
+ case DetectorType.USB:
+ this._sideBandDetector.Demodulate(iq, audio, length);
+ break;
+ case DetectorType.CW:
+ this._cwDetector.Demodulate(iq, audio, length);
+ break;
+ }
+ }
+
+ private void RdsFrameAvailableHandler(ref RdsFrame frame)
+ {
+ if (this._hookManager != null)
+ {
+ this._hookManager.ProcessRdsBitStream(ref frame);
+ }
+ }
+ }
+}
diff --git a/SDRSharp.Radio/SDRSharp.Radio/WindowType.cs b/SDRSharp.Radio/SDRSharp.Radio/WindowType.cs
new file mode 100644
index 0000000..27663f3
--- /dev/null
+++ b/SDRSharp.Radio/SDRSharp.Radio/WindowType.cs
@@ -0,0 +1,13 @@
+namespace SDRSharp.Radio
+{
+ public enum WindowType
+ {
+ None,
+ Hamming,
+ Blackman,
+ BlackmanHarris4,
+ BlackmanHarris7,
+ HannPoisson,
+ Youssef
+ }
+}
diff --git a/SDRSharp.Radio/refs to PortAudio.dll & Shark.dll.txt b/SDRSharp.Radio/refs to PortAudio.dll & Shark.dll.txt
new file mode 100644
index 0000000..e69de29
diff --git a/SDRSharp/Properties/AssemblyInfo.cs b/SDRSharp/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..5210412
--- /dev/null
+++ b/SDRSharp/Properties/AssemblyInfo.cs
@@ -0,0 +1,18 @@
+using System.Diagnostics;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.Versioning;
+using System.Security;
+using System.Security.Permissions;
+
+[assembly: CompilationRelaxations(8)]
+[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
+[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
+[assembly: SecurityRules(SecurityRuleSet.Level1)]
+[assembly: AssemblyTitle("SDR Sharp")]
+[assembly: AssemblyDescription("Software Defined Radio")]
+[assembly: AssemblyProduct("SDR#")]
+[assembly: AssemblyCopyright("Copyright © Youssef TOUIL 2012")]
+[assembly: AssemblyFileVersion("1.0.0.1632")]
+[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
+[assembly: AssemblyVersion("1.0.0.1632")]
diff --git a/SDRSharp/Properties/Resources.Designer.cs b/SDRSharp/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..e65dda0
--- /dev/null
+++ b/SDRSharp/Properties/Resources.Designer.cs
@@ -0,0 +1,163 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ public class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ public static System.Drawing.Bitmap audio_muted {
+ get {
+ object obj = ResourceManager.GetObject("audio_muted", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ public static System.Drawing.Bitmap audio_unmuted {
+ get {
+ object obj = ResourceManager.GetObject("audio_unmuted", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ public static System.Drawing.Bitmap center_24 {
+ get {
+ object obj = ResourceManager.GetObject("center_24", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ public static System.Drawing.Bitmap config_gear {
+ get {
+ object obj = ResourceManager.GetObject("config_gear", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ public static System.Drawing.Bitmap free_tuning {
+ get {
+ object obj = ResourceManager.GetObject("free_tuning", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
+ ///
+ public static System.Drawing.Icon mainicon {
+ get {
+ object obj = ResourceManager.GetObject("mainicon", resourceCulture);
+ return ((System.Drawing.Icon)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ public static System.Drawing.Bitmap sdr_start {
+ get {
+ object obj = ResourceManager.GetObject("sdr_start", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ public static System.Drawing.Bitmap sdr_stop {
+ get {
+ object obj = ResourceManager.GetObject("sdr_stop", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ public static System.Drawing.Bitmap sticky {
+ get {
+ object obj = ResourceManager.GetObject("sticky", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ public static System.Drawing.Bitmap toggle_menu {
+ get {
+ object obj = ResourceManager.GetObject("toggle_menu", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+ }
+}
diff --git a/SDRSharp/Properties/Resources.resx b/SDRSharp/Properties/Resources.resx
new file mode 100644
index 0000000..66466c6
--- /dev/null
+++ b/SDRSharp/Properties/Resources.resx
@@ -0,0 +1,638 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAABE
+ IAAARCABv5pDRgAAAbdJREFUSEu1lbFKA0EURddkJRGxEoOtWIig/oGgYmMhRFArCxv9ApEg+AWiFkFb
+ EdHWQlSMzeajUoUQzw07Mu68rImYgUPy3rt7387s7kyUJMlANJvNMUZs1fIwk1k6nc5UFEWXcGvV8zCT
+ Pq1WaxzjC/gUloaZlYVVCxI+lUqliOmZM+/XgHEDz7CtpfxR84MsjGP4Nhd9dHvwpjozqfnPKhA7GDvQ
+ 0EU+llYUi8V56nfS0ODE5QNhLxlFqxCYC6fRUhDvw6LLxXE8TXwvHU02e178qaqjg+IpvEhk4cy63e4M
+ sW5CS7Pl8oVCYYH4A57wK/PbC0wzC2ckGBugm3mHZZfHuEYsveqhSR5+g9RsjbxmUndvELNbSfXnEJrk
+ kW2QPos6NPSglWu32xPEmtkD2Eb9yDYQzOIwra8rTps+wiuEJnlkzcVIG/hLBHPK/esSDfKQR/uaEvzp
+ Q0M7S/z7h+YKPoxftwoujuEAllzO3CpcMQuCKsKgiaUVQ212DsaRLvCxdBjuUhtuuxZDHDh6TYc/cETm
+ yGxYGu64XCqVJq1akLBID/1ruLLqeZhJC03dX9vBSKIvPrZM3Y89BAAAAAAASUVORK5CYII=
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
+ wwAADsMBx2+oZAAAAWFJREFUSEu1kjFOw1AMQBMBGysR3IARJBZ2BmZOADtHYOEE3dlg5BTpCgMCiRWY
+ y4ZUgTo0PH99R07q3yQoWHpqYjt++U6zsiz/FTc5Jm4yRZ7np3Dp1VLUFzy4DyfT6TS3DRbq51mWTbya
+ Qs8RPcd6XxeIJ1jScJWSdAmIM/iBL3q3Q84U36GCING8ZZ2A0OEyY1FV1U7ImwYVBLyTpASEDF/Ic5Fu
+ AaysyxMQ9s2VXgKhsa62gPCGC70FAT2JFRDttViGCSCchN8LmEDqzZXBAmEJd3AP3zGX4k+CGVzDDTzH
+ XIrBAhl+qN+Ah/f4fYk1j0GCTwYfSI/9yFzvcv1q+iy9BTMdHoc2/qZR4p2klyCsRetCWyAk1tUpqNdi
+ 8QQx316XK3iLxcZaLClBrNl1uYJb+IDGWizrBEJc1wM80rspubpYFMXGfD7f0nuPLoEgg+2clYaxcZNj
+ 4ibHo8x+AU63uPHXUkSFAAAAAElFTkSuQmCC
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAA3
+ XAAAN1wBy8ekuQAAALZJREFUSEvFlcENwjAQBM8PSkGiOGrgDxRBK3ZR/hL2JPYTLSS27pSV5jNRvLHj
+ OFZrTUXKSKSMRErSez+Z2bm1VtT1PUhJkBt4g/tsiZQEeYHly2OmREqyKnCeoyVSEmRd4AzNREqCqIKh
+ EikJ8qvA2bVcUhLkX4GzORMpCbJVsJRSrupeIiVBDi9IXaLUl5y6TVM/tNSjYujJiZQE8ePaB885rv2H
+ g31+mR3ckTISKSORMo5qH5MYfWaww5UBAAAAAElFTkSuQmCC
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
+ wwAADsMBx2+oZAAAAIxJREFUSEvt1UEKAjEMBdAURip4//vkFE4Zi5cYuxP1ZyOz+DCbn1mIgbdpoIWk
+ TW2Mcaq1XjLE3oaY4ZHkCvZO9AKaUDnmgDs8k3Rz91R0UYkuKkV0YA1SuAFNqPzIO1iAzRGFOX+asqul
+ ZKWUiZ2uEHujTNY2NVP7/we7vu9gTdIsOg3nJBO9uzpuH18Eg6dHrMZ7AAAAAElFTkSuQmCC
+
+
+
+
+ AAABAAQAEBAAAAEAIABoBAAARgAAABgYAAABACAAiAkAAK4EAAAgIAAAAQAgAKgQAAA2DgAAQEAAAAEA
+ IAAoQgAA3h4AACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAABRAwDYVAMA7FED
+ AOdUAwDsUQMA7VMDAOhRAwDnUQMA51EDAOdRAwDnUQMA51EDAOdRAwDnUQMA51QDAOxRAwDYWwcA6l8K
+ AP9jDAD/XggA6V0KAOBiDAD/XgsA/1oKAPpdCgD7XQoA+10KAPtdCgD7XQoA+10KAPteCgD/WwcA6mYQ
+ AOVpFAD/Yw4AeWMSAClpEgApYgwAU2cTAPBmEwD5ZhAA8mMQAPNjEADzYxAA82MQAPNjEADzZRIA+WYT
+ AONsHQD2bR0Ae3QhAC5sGgDdbB0A63MfAEluHQA8bR0A+24cAPNuGwDxbhsA8W4bAPFuGwDxbhsA8W4a
+ APdrHADheCMAtHskACN2JQDoeCUA/HglAPh4JQD8fyUAIngkAJR6JgD/diUA8XglAPZ5JQDwdyUA73cl
+ AO92JQD1eSMA34IuADGBLgCUiDAA/4IsAOuALgDsgC4A/oEuAKuCKwApgS8A/oIuAO1/LQDKgC4A8oAv
+ APSALgDsgi4A838rAN6NOAASizQAWIk4ANmMOQD1izYA6ow4AOyMNQD3jDkAOoo3AIaLNwBYkjwAXIo3
+ AFOMNwDhijYA84k4APGKOADdlUIAipdCALiUQQA+lEAA7ZRAAO2TQADpk0AA/JZDAJy2bQAHl0IAwJhE
+ AP+XQwCUlUAAS5ZAAPSWQADxk0AA3Z1KANGmTgD/o04Acp5KAGeiTQD/oEwA56JNAP+fSgB+fz8ACJ1M
+ AOShTADtoEwA/6FMAGifSwCDqlEA/6FMAKSqUwDZqVMA7qZWAO2oVgA+qFMAma5YAP+nVAGjqFYAR6dT
+ AFKrVQBxp1YA/KpTAOioVQDmrFYARKlVAMWsVgBNtF8A1LNfAOm0XwDstGAA1LZgAEq0XgJcuWADQrJd
+ ANK0XgDQtFUAGLVgANa0XwDssVwA7bRfANO2YgBGqlUABrxqANO+aQDovmoA4bxnAOq+aQDkv2sAub9n
+ AOC8aQDrvmoA9b9qAGW6aAA4wGsA871rAPq/ZwDsumMAKb5rAHfKdADQyXIA5cpzAN/KcwDfx3IA4cZx
+ AOfHdADiyXMA38lyAOPJdADoznkAP8JvADfGcACDwnIAJsp2AGHHdADi1H8A0NR/AOTUfwDe1H8A3tR/
+ AN7UfwDe1H8A3tR/AN7UgADd038A5NJ8AOXYgQB+2YQAUdWBAI7TfgDw03wA09+IANPghwDn4IoA4eCK
+ AOHgigDh4IoA4eCKAOHgigDh4IoA4eCKAOHfigDj34gA8d+JAPPghwDv4IoA6N+IANPplQC/65YA0euW
+ AMzrlgDM65YAzOuWAMzrlgDM65YAzOuWAMzrlgDM65YAzOuWAMzrlgDM65YAzOuWANHplQC/AAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgA
+ AAAYAAAAMAAAAAEAIAAAAAAAYAkAAAAAAAAAAAAAAAAAAAAAAABPAQDXUgEA61IBAOZSAQDmUgEA5lIB
+ AOZSAQDmUgEA5lIBAOZSAQDmUgEA5lIBAOZSAQDmUgEA5lIBAOZSAQDmUgEA5lIBAOZSAQDmUgEA5lIB
+ AOZSAQDmUgEA61EBANZXBgDsWggA/1kIAPxZCAD8VggA/VoIAP9bCAD/WQgA/1kIAPxZCAD8WQgA/FkI
+ APxZCAD8WQgA/FkIAPxZCAD8WQgA/FkIAPxZCAD8WQgA/FkIAPxZCAD8WggA/1YGAOtfCwDkXAsA+l8K
+ APNdDAD9YQwA/18MAONfCgDbXQwA+GIMAP9fCwD0XwsA9F8LAPRfCwD0XwsA9F8LAPRfCwD0XwsA9F8L
+ APRfCwD0XwsA9F8LAPRfCwD0XAsA+l8LAONhDQDjYg8A+WURAP9iEADcYg0ATgAAAAAAAAAAZA0AJmQQ
+ AKpnEQD/Yw0A9GENAPNhDQDzYQ0A82ENAPNhDQDzYQ0A82ENAPNhDQDzYQ0A82ENAPNhDQDzYxAA+WMN
+ AOJqGgDhbhsA/2sZANNpDwARaxoAE2wZAIttGgCcbh4AMwAAAABsGQCLbhsA/2sZAPJrGQDyaxkA8msZ
+ APJrGQDyaxkA8msZAPJrGQDyaxkA8msZAPJrGQDyaRcA+GoaAOFsGgDmbh0A9HIjAB1zHgAqcBwA6nMd
+ AP9yHQD/bx0A/nAeAFsAAAAAbhwAsnAdAP9uGgDwbhoA8G4aAPBuGgDwbhoA8G4aAPBuGgDwbhoA8G4a
+ APBuGgDwbR0A9nAbAN90JAD1dyQAbwAAAAB3IQDcdiQA/nQlAO52JQDvdCUA93YlAPh4IQAmeyMAHXUi
+ APN3IgDydiUA73YlAO92JQDvdiUA73YlAO92JQDvdiUA73YlAO92JQDvdCUA9XYiAN5+KQDEAAAAAH0q
+ AIqAKgD/eyYA7X0mAO59JgDueyYA7X4pAP59KgC7AAAAAH0pAJt/KgD/fSYA7nspAPF+KQD2eyYA830m
+ AO59JgDufSYA7n0mAO59JgDufSYA9HwpAN2ELwBLiTEAGoQwAP+DMADzgS4A7IMvAO2DLwDtgy8A7YMw
+ AO2EMAD/gzAAOoMzACOBLQD2hS4A+4EtAPCCMADPgDAA5IQwAP2EMADtgS4A7IMvAO2DLwDtgy8A84Qv
+ ANwAAAAAeC0AEYk0AnqLNwD5iDYA74g3AOuINwDriDcA64g3AOuLNAD7izcAsAAAAACLNwDCizcAwoUz
+ ACiQOwAeiC0AHIo2AIyLNwD9iDQA7Yg3AOuINwDriDcA8Yo1ANuTQwATkz0Au5E/AByPPQJgkD0A/pI6
+ AOuSPQDqkToA65E6AOuPPADsjzoA85I/BiiOPQAykTwAFZM/AIWUPgHhkj8ArqJFAAuQPAJ6kj4A/5I9
+ AOqSPQDqkDsA8JI9ANyVQgBjn0gA/5VCANd/fwAClkMAkJdCAP2VRQDol0IA6ZdCAOmVRQDol0IA/ZhE
+ AJUAAAAAlkQAjZtGAP+WRADvl0QA/5hCAMQAAAAAmEUAsJVCAPmVRQDol0UA75ZFANmeRwDBn0gA8pxH
+ APyfSgCeAAAAAJ9LANKfSADynEcA55xLAOecRwDnnUsA/p9KAHsAAAAAnUoAnZ9KAPqcRwDnnEcA559K
+ AP+gSwCElkUAFp5KAO2cSQDsnEgA+Z5JAJyjUQDcplIA7KRPAOejUQD7qFIARKVQADOmTwD6pE8A6qNR
+ AOWkUgD4plEBwKJFAAujVQAno04AKqNPAPClTgDpplIA5qNRAOijTwD2o1EAMqZQAF+nUgD/plEA9KVQ
+ ADyrWADWq1UA66pVAOWrVwDtqFYA3bBiAA2qVwBmq1gA/KxZAP+rWAHZr08PEKpWAGqpVQDFAAAAAKtX
+ AKSrWAD2q1gA5KpVAOWoVwDvqlcA06JcAAuqVwGmrFcA0v9/AAKyWwDUsl0A6bFdAOOyXADisVoA8rJc
+ AMS2YQAVrVgDQq9aAnaqVQ4StF8AS7JaAO+xXQD2sF0ANK5eACOyWwDvsV0A5rFdAOOyXADir10A9LJe
+ ALC1XQAmsF4APgAAAAC6ZQDUumIA6bplAOO6ZQDjumQA4rdiAPC3ZQDWvmcAZ79qAEi8ZAGRumIA7rdk
+ AOi4ZQDvumMAtgAAAAC6ZACCuWIA+rplAOK6ZQDjt2IA4bxmAP+5ZQCXAAAAALllA0nBbQDSwGwA579q
+ AOG/agDhv2oA4cBsAODAbADnvGsA87xrAPO/awDwwGkA479qAOG/agDhwGkA9MBsAF4AAAAAwGwBs75t
+ APrAagDowWoA9L9sAN27aQAiv2gALL1pAN7IcQDRxnMA5shwAODIcADgyHAA4MhwAODIcADgyHAA4Mhw
+ AODIcADgyHAA4MhwAODJcgDfyHAA5chwAOXKcwA1AAAAAMRyAI/IcwDSx3EAr8FsACG/fwAIx3EAyshw
+ ANzPegDQz3oA5cx6AN/MegDfzHoA38x6AN/MegDfzHoA38x6AN/MegDfzHoA38x6AN/MegDfzHcA3s16
+ AOjOegDd0HoATQAAAAAAAAAAAAAAANR/ACTPeQC7y3kA9M16AM7VfwDQ1H8A5NR/AN7UfwDe1H8A3tR/
+ AN7UfwDe1H8A3tR/AN7UfwDe1H8A3tR/AN7UfwDe1H8A3tWBAN3UfwDk1H8A6td/ALTXggCN1oAAo9OA
+ AN/UfwDq1oEA49Z/AM7dhADO3ocA4t6GANzehgDc3oYA3N6GANzehgDc3oYA3N6GANzehgDc3oYA3N6G
+ ANzehgDc3oYA3N6GANzdhgDc3YgA3duHAObdhwDs3oUA6duIAN/eiADb3ocA4t2HAM3lkADS5I8A5+OP
+ AODjjwDg448A4OOPAODjjwDg448A4OOPAODjjwDg448A4OOPAODjjwDg448A4OOPAODjjwDg448A4OOP
+ AODjjwDg448A4OOPAODjjwDg5I8A5+WNANHslQC/65cA0eyXAMzslwDM7JcAzOyXAMzslwDM7JcAzOyX
+ AMzslwDM7JcAzOyXAMzslwDM7JcAzOyXAMzslwDM7JcAzOyXAMzslwDM7JcAzOyXAMzslwDM65cA0eyX
+ AL4AAAAAAAAAAAAAAAAGAAAAAIAAAABAAAAgAAAAQCAAAAAAAACAEAAAAAAAAAAIIAAICAAAAAAAAAAE
+ AAAAAAEAAAICAAABAAAAAIAAAABwAAAAAAAAAAAAAAAAAAAAAAAoAAAAIAAAAEAAAAABACAAAAAAAIAQ
+ AAAAAAAAAAAAAAAAAAAAAAAAUQEA2E4BAO1PAQDnTwEA508BAOdPAQDnTwEA508BAOdPAQDnTwEA508B
+ AOdPAQDnTwEA508BAOdPAQDnTwEA508BAOdPAQDnTwEA508BAOdPAQDnTwEA508BAOdPAQDnTwEA508B
+ AOdPAQDnTwEA508BAOdPAQDnTgEA7VEBANhWBQDsVwUA/1MFAP1TBQD9UwUA/VMFAP1TBQD9UwUA/VMF
+ AP1TBQD9UwUA/VMFAP1TBQD9UwUA/VMFAP1TBQD9UwUA/VMFAP1TBQD9UwUA/VMFAP1TBQD9UwUA/VMF
+ AP1TBQD9UwUA/VMFAP1TBQD9UwUA/VMFAP1XBQD/VgUA7FsKAOVbCgD7WQoA9VkKAPVZCgD1WQoA9VkK
+ APdZCgD9XAoA/1kKAPpZCgD1WQoA9VkKAPVZCgD1WQoA9VkKAPVZCgD1WQoA9VkKAPVZCgD1WQoA9VkK
+ APVZCgD1WQoA9VkKAPVZCgD1WQoA9VkKAPVZCgD1WQoA9VsKAPtbCgDlYAsA5F0LAPpgCwD0XwoA810L
+ APVjDAD/YQwA/F4LAN1gDQDUYAoA7GMMAP9fCwD7XwoA82ALAPRgCwD0YAsA9GALAPRgCwD0YAsA9GAL
+ APRgCwD0YAsA9GALAPRgCwD0YAsA9GALAPRgCwD0YAsA9GALAPRgCwD0XQsA+mALAORfDQDjYQ4A+V8M
+ APNiDgD3ZRAA/2MQAK5jEQA7AAAAAQAAAABmFAAZYw8AcWMPAOdkDwD/XwwA8l8MAPNfDADzXwwA818M
+ APNfDADzXwwA818MAPNfDADzXwwA818MAPNfDADzXwwA818MAPNfDADzXwwA818MAPNhDgD5Xw0A42YV
+ AOJnFQD4ZhQA9moXAP9oEwBzAAAAAAAAAABtGAAqaRoAOn8AAAIAAAAAbRUAI2kVAM5rGAD/ZhcA8WkX
+ APJpFwDyaRcA8mkXAPJpFwDyaRcA8mkXAPJpFwDyaRcA8mkXAPJpFwDyaRcA8mkXAPJpFwDyaRcA8mcV
+ APhmFQDiahsA4WoZAPlvHAD/bBwAdQAAAABsFwAhbhoAt2oYAPlsGwD+ahgA2m0ZAE8AAAAAah8AGG0c
+ ANlqGwD9ahsA8WobAPFqGwDxahsA8WobAPFqGwDxahsA8WobAPFqGwDxahsA8WobAPFqGwDxahsA8Wob
+ APFqGwDxbRkA92obAOFwGwDfdB4A/3AeAKoAAAAAdB0AI28cAOByHQD/bRwA8m8bAPFwHAD8cB0A/nAg
+ AFYAAAAAbxsAN28aAPdvHQDzbhoA8G4aAPBuGgDwbhoA8G4aAPBuGgDwbhoA8G4aAPBuGgDwbhoA8G4a
+ APBuGgDwbhoA8G4aAPBtHQD2bxwA4HUjAOZ3IwDtdCoAGD8AAAR0IgDHdyUA/3MlAO52JQDvdiUA73Ul
+ AO92JQD4diEA8XIfACgAAAAAdSQAlHglAP91JQDvdiUA73YlAO92JQDvdiUA73YlAO92JQDvdiUA73Yl
+ AO92JQDvdiUA73YlAO92JQDvdiUA73QlAPV0IwDfeSQA83onAHsAAAAAfCUAeX4oAP97JQDueyUA7nsl
+ AO57JQDueyUA7nslAO58JwD/eycAtgAAAAB7KQAfeScA73gkAPN7JQDueyUA7nslAO57JQDueyUA7nsl
+ AO57JQDueyUA7nslAO57JQDueyUA7nslAO57JQDueyQA9HwnAN5/KwDOfyoADHovABt8LADrfykA9H8p
+ AO5/KQDufykA7n8pAO5/KQDufykA7n8pAO6ALQD/fy0ATgAAAACALACbgSwA/4AsAO1/KQDufyoA8H8q
+ APaAKQD1gCkA738pAO5/KQDufykA7n8pAO5/KQDufykA7n8pAO5/KQD0fyoA3oMyAGUAAAAAhjAAlIwz
+ AP+ELQDrgi4A7IIuAOyCLgDsgi4A7IIuAOyCLgDsgi4A7IItAPqFMAC/AAAAAIIwAC+FLgD1hDAA74Iu
+ APyCLgDwhDAA0oIvANeCMQD4gi4A9oQuAOuCLgDsgi4A7IIuAOyCLgDsgi4A7IIwAPKDMADdAAAAAAAA
+ AACHMAAviDYBu4g3AP6HNwDrhzcA64g3AOuINwDriDcA64g3AOuINwDrijcA7Ic3APqLNQA+AAAAAIo2
+ ALiROQD/ijYApIYxByQAAAAAAAAAAIYzAEaHNADRiDQA/Io2AOqINwDriDcA64g3AOuINwDriDcA8Ys2
+ ANwAAAAAjDoAV408AEgAAAAAjjoBp407AP6MNwDqjzkA6485AOuPOQDrjzkA6485AOuNNwDqjDcA/I06
+ AKYAAAAAkDsAY445AJMAAAAAkTwEP5A8AaGPPAGQjD8AFH8zGQqNOgHBjzgA+4w3AOqPOQDrjzkA6485
+ AOuMOQDxjjcA3H8/AASTPgDQl0IA/5ZCAEkAAAAAkj8AwZJAAPqSPwDplEAA6pRAAOqUQADqlEAA6pRA
+ AOqUPwDtlD0B7JE6ByOqVQADAAAAAJI/AHGUQAD/lD8A/ZVBAP+UPwDlk0QALYk6AA2UQADalD4A9JRA
+ AOqUQADqlEAA6pI+APCSPwDdlkQAR5dCAPiXQwDxl0UA7JZDACKcRAAal0UA5ZVFAPCVRQDolUUA6JVF
+ AOiVRQDolUUA6JVFAOiXQwD9l0UAhQAAAACbRgNFlEIA/JVCAO2VRQDolUUA6JdFAPaVRADcnDoADZZC
+ AD2WRQD3l0MA6pVFAOiVRQDolEUA8JVDANeeSQCrm0kA955KAOabSQD3nkcAwQAAAACdSgBWnkoA+55I
+ AOieSADonkgA6J5IAOieSADom0cA555HAP2dSABwAAAAAJ9IA0ObRwD2nkgA6J5IAOieSADom0cA555J
+ APudSQCnAAAAAJ1JAJKbRwD8m0cA55xHAOeeSQD6nUkAkqNMANyiTQDto04A56RMAOajTAD9pE0CcwAA
+ AACjTwCqoUwA+aRMAOajTgDno04A56RMAOahTAD2oU0AvwAAAAD/AP8BAAAAAKNOALqkTwD0o04A56NO
+ AOejTgDnpE8A56FOAPuhTQNSmWYABaFOANqkTwDxoU8A6KFMAPCjTAUyplEA2KdUAOyoVADmp1QA5qhS
+ AOuoUgDrqlUAJKVMABSoUwDfplQA8qhTAOWoUwDlp1QA76hSAeunUAYmplgAF6ZTAZMAAAAAp1MDUqVR
+ APmnVADmqFQA5qhUAOanVADmp1IA7qRTAOKqVQASp1MAQKZUAPimVQD1p1MBuAAAAACrWADWq1UA66tV
+ AOWrVQDlrFgA5KtYAPOsWADCAAAAAa5VADasWADnrFgA+alYAPiqVwHvqlgDSwAAAACsVwCqqVcA+6pV
+ ACR/fwACrFcAxqxYAPCrVQDlq1UA5atVAOWsWADkqVgA9axYALMAAAAArFgCdrdeAP+tWQJhAAAAAK1c
+ ANWwWwDqrlkA5K5ZAOSxXADjsFwA47BcAPaxXACoAAAAAK9XByCvXAGgsVsDp61cBS8AAAAAsF0AiLBc
+ APawXAD2sV0AkAAAAACyXANQsV0A9q5aAOSxXADjrlkA5LFcAOOwXADjrVsA+LBcAI8AAAAAsFwCcape
+ ABsAAAAAuGEA1LRhAOm3XwDjt18A47dfAOO3XwDjt18A47dfAPW5YQC6uWQFMAAAAAAAAAAAuWQHIbhj
+ AKK3XwD1t2IA5LhgAOa3YQDnuGMAJAAAAAC4YgCwtWIA9LViAOK3XwDjt18A47dfAOO3YgDkt18A9bhj
+ AJ8AAAAAAAAAAKJcAAu8ZQDTvWcA6L1lAOK9ZQDivWUA4r1lAOK9ZQDivGUA4rlnAO+8ZADpu2gAwbtm
+ AL26ZwHjumcA8r1lAOK9ZQDivGUA4rplAPO7ZgCiAAAAALpnACW6ZwDjumgA67xlAOK9ZQDivWUA4r1o
+ AOG6ZQD2u2cAubZtAAfBaQgdvGgBv8FtANLAbADnv2oA4b9qAOG/agDhv2oA4b9qAOG/agDhv2oA4cBq
+ AOO+agDqwGwA68BsAOS/agDhv2oA4b9qAOG/agDhvm0A4r5rAPLBawBTAAAAAMBtA02+bQDuwW0A7L9q
+ AOG+bQDiwWsA9MBtANDBcAAZAAAAAMFsAKLAagDoyHEA0cZyAObIbwDgyG8A4MhvAODIbwDgyG8A4Mhv
+ AODIbwDgyG8A4MhvAODIbwDgyG8A4MhvAODIbwDgyG8A4MhvAODIcgDfxW8A58VwAN7FcwAsAAAAAMZx
+ AEjHbwDOx3MA78VyAOzHcQCyx28AIAAAAADFcgBix28A98huANLLdADQyXcA5ct0AN/LdADfy3QA38t0
+ AN/LdADfy3QA38t0AN/LdADfy3QA38t0AN/LdADfy3QA38t0AN/LdADfy3QA38t0AN/MdwDeyHcA68l0
+ ANLNeQAuAAAAAL9/AATJdwA+yncAMQAAAAAAAAAAzHcAWsp3AOnKdwDpy3QA0NB9ANDSfQDk0n0A3tJ9
+ AN7SfQDe0n0A3tJ9AN7SfQDe0n0A3tJ9AN7SfQDe0n0A3tJ9AN7SfQDe0n0A3tJ9AN7SfQDe0n0A3tJ9
+ AN7PewDd0noA6c99ANzQfQBu0H8AFgAAAAAAAAAAzXwAKdJ8AI/RegDr0noA49F9AOTQfQDQ1n8A0NaC
+ AOPVggDd1YIA3dWCAN3VggDd1YIA3dWCAN3VggDd1YIA3dWCAN3VggDd1YIA3dWCAN3VggDd1YIA3dWC
+ AN3VggDd1YIA3dWCAN3VgQDd1oEA49Z/AO7WfwDW14EAu9eCAMDUgQDf1n8A7tSCAN/VggDd1oIA49Z/
+ ANDdhADO3YcA4t2GANzdhgDc3YYA3N2GANzdhgDc3YYA3N2GANzdhgDc3YYA3N2GANzdhgDc3YYA3N2G
+ ANzdhgDc3YYA3N2GANzdhgDc3YYA3N2GANzdhgDc3YYA3NuHAODbhgDm3IQA5duEAN/dhgDc3YYA3N2G
+ ANzdhwDi3YQAzuKMAM3hjADh44oA2+OKANvjigDb44oA2+OKANvjigDb44oA2+OKANvjigDb44oA2+OK
+ ANvjigDb44oA2+OKANvjigDb44oA2+OKANvjigDb44oA2+OKANvjigDb44oA2+OKANvjigDb44oA2+OK
+ ANvjigDb44oA2+GMAOHijADN5ZAA0uWSAObokADg6JAA4OiQAODokADg6JAA4OiQAODokADg6JAA4OiQ
+ AODokADg6JAA4OiQAODokADg6JAA4OiQAODokADg6JAA4OiQAODokADg6JAA4OiQAODokADg6JAA4OiQ
+ AODokADg6JAA4OiQAODokADg5ZIA5uWQANLtlwC+65cA0e2YAMztmADM7ZgAzO2YAMztmADM7ZgAzO2Y
+ AMztmADM7ZgAzO2YAMztmADM7ZgAzO2YAMztmADM7ZgAzO2YAMztmADM7ZgAzO2YAMztmADM7ZgAzO2Y
+ AMztmADM7ZgAzO2YAMztmADM7ZgAzO2YAMzrlwDR7ZcAvgAAAAAAAAAAAAAAAAAAAAAAgAAABiAAAAgQ
+ AAAQCAAAAAQAACAEAAAAAgAAQAIAAMABDACQASAACABAAAAAgAAEAIBAAgFAAAAAQAEAAgARAIQgCQAw
+ EAYAABAAAAAIBAAABAgAAAIwAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAEAAAACAAAAAAQAgAAAA
+ AAAAQgAAAAAAAAAAAAAAAAAAAAAAAFEAAdlPAAHtUQAB6FEAAehRAAHoUQAB6FEAAehRAAHoUQAB6FEA
+ AehRAAHoUQAB6FEAAehRAAHoUQAB6FEAAehRAAHoUQAB6FEAAehRAAHoUQAB6FEAAehRAAHoUQAB6FEA
+ AehRAAHoUQAB6FEAAehRAAHoUQAB6FEAAehRAAHoUQAB6FEAAehRAAHoUQAB6FEAAehRAAHoUQAB6FEA
+ AehRAAHoUQAB6FEAAehRAAHoUQAB6FEAAehRAAHoUQAB6FEAAehRAAHoUQAB6FEAAehRAAHoUQAB6FEA
+ AehRAAHoUQAB6FEAAehRAAHoUQAB6FEAAehRAAHoTwAB7VEAAdlOAgDtUgIA/04CAP1OAgD9TgIA/U4C
+ AP1OAgD9TgIA/U4CAP1OAgD9TgIA/U4CAP1OAgD9TgIA/U4CAP1OAgD9TgIA/U4CAP1OAgD9TgIA/U4C
+ AP1OAgD9TgIA/U4CAP1OAgD9TgIA/U4CAP1OAgD9TgIA/U4CAP1OAgD9TgIA/U4CAP1OAgD9TgIA/U4C
+ AP1OAgD9TgIA/U4CAP1OAgD9TgIA/U4CAP1OAgD9TgIA/U4CAP1OAgD9TgIA/U4CAP1OAgD9TgIA/U4C
+ AP1OAgD9TgIA/U4CAP1OAgD9TgIA/U4CAP1OAgD9TgIA/U4CAP1OAgD9TgIA/VICAP9OAgDtUgQA51IE
+ AP1SBAD3UgQA91IEAPdSBAD3UgQA91IEAPdSBAD3UgQA91IEAPdSBAD3UgQA91IEAPdSBAD3UgQA91IE
+ APdSBAD3UgQA91IEAPdSBAD3UgQA91IEAPdSBAD3UgQA91IEAPdSBAD3UgQA91IEAPdSBAD3UgQA91IE
+ APdSBAD3UgQA91IEAPdSBAD3UgQA91IEAPdSBAD3UgQA91IEAPdSBAD3UgQA91IEAPdSBAD3UgQA91IE
+ APdSBAD3UgQA91IEAPdSBAD3UgQA91IEAPdSBAD3UgQA91IEAPdSBAD3UgQA91IEAPdSBAD3UgQA91IE
+ APdSBAD9UgQA51YFAOZYBgD8WAYA9lgGAPZYBgD2WAYA9lgGAPZYBgD2WAYA9lgGAPZYBgD2WAYA9lgG
+ APZYBgD2WAYA9lgGAPZYBgD2WAYA9lgGAPZYBgD2WAYA9lgGAPZYBgD2WAYA9lgGAPZYBgD2WAYA9lgG
+ APZYBgD2WAYA9lgGAPZYBgD2WAYA9lgGAPZYBgD2WAYA9lgGAPZYBgD2WAYA9lgGAPZYBgD2WAYA9lgG
+ APZYBgD2WAYA9lgGAPZYBgD2WAYA9lgGAPZYBgD2WAYA9lgGAPZYBgD2WAYA9lgGAPZYBgD2WAYA9lgG
+ APZYBgD2WAYA9lgGAPZYBgD2WAYA/FYFAOZYBwDmWwoA/FsKAPZbCgD2WwoA9lsKAPZbCgD2WwoA9lsK
+ APZbCgD2WwoA9lsKAPZbCgD2WwoA9lsKAPZbCgD2WwoA9lsKAPZbCgD2WwoA9lsKAPZbCgD2WwoA9lsK
+ APZbCgD2WwoA9lsKAPZbCgD2WwoA9lsKAPZbCgD2WwoA9lsKAPZbCgD2WwoA9lsKAPZbCgD2WwoA9lsK
+ APZbCgD2WwoA9lsKAPZbCgD2WwoA9lsKAPZbCgD2WwoA9lsKAPZbCgD2WwoA9lsKAPZbCgD2WwoA9lsK
+ APZbCgD2WwoA9lsKAPZbCgD2WwoA9lsKAPZbCgD2WwoA9lsKAPxYBwDmWwoA5VsKAPtZCgD1WQoA9VkK
+ APVZCgD1WQoA9VkKAPVZCgD1WQoA9VkKAPVZCgD1WQoA9VkKAPVZCgD1WQoA9VkKAPVZCgD1WQoA9VkK
+ APVZCgD1WQoA9VkKAPVZCgD1WQoA9VkKAPVZCgD1WQoA9VkKAPVZCgD1WQoA9VkKAPVZCgD1WQoA9VkK
+ APVZCgD1WQoA9VkKAPVZCgD1WQoA9VkKAPVZCgD1WQoA9VkKAPVZCgD1WQoA9VkKAPVZCgD1WQoA9VkK
+ APVZCgD1WQoA9VkKAPVZCgD1WQoA9VkKAPVZCgD1WQoA9VkKAPVZCgD1WQoA9VkKAPVbCgD7WwoA5V8L
+ AORcCwD6XwsA9F8LAPRfCwD0XwsA9F8LAPRfCwD0XwsA9F8LAPRfCwD0XwsA9F8LAPRfCwD2XwsA+18M
+ AP9hDAD/YAwA/18LAPtfCwD2XwsA9F8LAPRfCwD0XwsA9F8LAPRfCwD0XwsA9F8LAPRfCwD0XwsA9F8L
+ APRfCwD0XwsA9F8LAPRfCwD0XwsA9F8LAPRfCwD0XwsA9F8LAPRfCwD0XwsA9F8LAPRfCwD0XwsA9F8L
+ APRfCwD0XwsA9F8LAPRfCwD0XwsA9F8LAPRfCwD0XwsA9F8LAPRfCwD0XwsA9F8LAPRfCwD0XwsA9F8L
+ APRfCwD0XAsA+l8LAORiCwDkXwsA+mILAPRiCwD0YgsA9GILAPRiCwD0YgsA9GILAPRiCwD0XwoA82IM
+ APllDQD/Xg0A/WINAOJhDADMYg0Au2EOAMdiDQDiYg0A/2UNAP9iCwD5XwoA82ILAPRiCwD0YgsA9GIL
+ APRiCwD0YgsA9GILAPRiCwD0YgsA9GILAPRiCwD0YgsA9GILAPRiCwD0YgsA9GILAPRiCwD0YgsA9GIL
+ APRiCwD0YgsA9GILAPRiCwD0YgsA9GILAPRiCwD0YgsA9GILAPRiCwD0YgsA9GILAPRiCwD0YgsA9GIL
+ APRiCwD0YgsA9GILAPRiCwD0YgsA9F8LAPpiCwDkXw0A42ANAPlfCwDzXwsA818LAPNfCwDzXwsA818L
+ APNfCwDzYQsA9GQOAP9iDADvYg4Am2QRAEdrGgATfyoABgAAAABVAAADaAsAFmERAEliDgCeYA0A8GQO
+ AP9gCwD0XwsA818LAPNfCwDzXwsA818LAPNfCwDzXwsA818LAPNfCwDzXwsA818LAPNfCwDzXwsA818L
+ APNfCwDzXwsA818LAPNfCwDzXwsA818LAPNfCwDzXwsA818LAPNfCwDzXwsA818LAPNfCwDzXwsA818L
+ APNfCwDzXwsA818LAPNfCwDzXwsA818LAPNfCwDzXwsA818LAPNgDQD5Xw0A42ANAONiDwD5YA0A82AN
+ APNgDQDzYA0A82ANAPNjDQDyYRAA+GUSAP9jEQCxZA8AMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAGQVADBjEQCxZxIA/2MPAPdjDQDyYA0A82ANAPNgDQDzYA0A82ANAPNgDQDzYA0A82AN
+ APNgDQDzYA0A82ANAPNgDQDzYA0A82ANAPNgDQDzYA0A82ANAPNgDQDzYA0A82ANAPNgDQDzYA0A82AN
+ APNgDQDzYA0A82ANAPNgDQDzYA0A82ANAPNgDQDzYA0A82ANAPNgDQDzYA0A82ANAPNgDQDzYg8A+WAN
+ AONnFQDjZxUA+WQSAPNkEgDzZBIA82QSAPNnFQDyZxQA+mYUAP1mFQB3AAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGgUAHxlFAD9ZxUA+WcVAPJkEgDzZBIA82QS
+ APNkEgDzZBIA82QSAPNkEgDzZBIA82QSAPNkEgDzZBIA82QSAPNkEgDzZBIA82QSAPNkEgDzZBIA82QS
+ APNkEgDzZBIA82QSAPNkEgDzZBIA82QSAPNkEgDzZBIA82QSAPNkEgDzZBIA82QSAPNkEgDzZBIA82QS
+ APNkEgDzZBIA82cVAPlnFQDjaxYA4mkWAPhrGADyaxgA8msYAPJpGADxaRYA+GgVAPZqFQBdAAAAAAAA
+ AAAAAAAAAAAAAAAAAABmFgA5aRgAamgYAHxpFwBhbxoAJwAAAAAAAAAAAAAAAAAAAAAAAAAAbBUAYGoV
+ APlpFgD4aRgA8WsYAPJrGADyaxgA8msYAPJrGADyaxgA8msYAPJrGADyaxgA8msYAPJrGADyaxgA8msY
+ APJrGADyaxgA8msYAPJrGADyaxgA8msYAPJrGADyaxgA8msYAPJrGADyaxgA8msYAPJrGADyaxgA8msY
+ APJrGADyaxgA8msYAPJrGADyaxgA8msYAPJpFgD4axYA4mscAOJpGgD4axwA8mscAPJrHADyaRkA9mwb
+ AP9qGgBiAAAAAAAAAAAAAAAAAAAAAG4aAENsGQC/ahgA+WwbAP9tHAD/bBsA/2gaAPFqGwCcaRoAHQAA
+ AAAAAAAAAAAAAAAAAABqGgBibBsA/mkZAPZrHADyaxwA8mscAPJrHADyaxwA8mscAPJrHADyaxwA8msc
+ APJrHADyaxwA8mscAPJrHADyaxwA8mscAPJrHADyaxwA8mscAPJrHADyaxwA8mscAPJrHADyaxwA8msc
+ APJrHADyaxwA8mscAPJrHADyaxwA8mscAPJrHADyaxwA8mscAPJrHADyaRoA+GscAOJqGwDhbRkA92ob
+ APFqGwDxaxsA8m8cAP9sGQCBAAAAAAAAAAAAAAAAAAAAAG0bAIJsGgD7bhsA/2sbAPRqGwDxahsA8Wob
+ APFqGAD2cRwA/24aAN5tGwBBAAAAAAAAAAAAAAAAAAAAAG0cAH5wHAD/axsA8mobAPFqGwDxahsA8Wob
+ APFqGwDxahsA8WobAPFqGwDxahsA8WobAPFqGwDxahsA8WobAPFqGwDxahsA8WobAPFqGwDxahsA8Wob
+ APFqGwDxahsA8WobAPFqGwDxahsA8WobAPFqGwDxahsA8WobAPFqGwDxahsA8WobAPFqGwDxahsA8W0Z
+ APdqGwDhbhoA4GwYAPZuGQDwbBgA73AbAP9uHgCyAAAAAAAAAAAAAAAAfwAAAm8cAJlxHAD/bhsA9WwY
+ AO9uGQDwbhkA8G4ZAPBuGQDwbhkA8GwYAO9uGgD+bhkA8G4dAEUAAAAAAAAAAAAAAAAAAAAAcBsAsXAb
+ AP9sGADvbhkA8G4ZAPBuGQDwbhkA8G4ZAPBuGQDwbhkA8G4ZAPBuGQDwbhkA8G4ZAPBuGQDwbhkA8G4Z
+ APBuGQDwbhkA8G4ZAPBuGQDwbhkA8G4ZAPBuGQDwbhkA8G4ZAPBuGQDwbhkA8G4ZAPBuGQDwbhkA8G4Z
+ APBuGQDwbhkA8G4ZAPBsGAD2bhoA4G8cAOBuHwD2bhwA8HEcAPhyHQDocSMAJAAAAAAAAAAAAAAAAHIg
+ AIZ0IQD/bh0A8m4cAPBuHADwbhwA8G4cAPBuHADwbhwA8G4cAPBuHADwcBwA73AeAPxzHwDrdB8AMAAA
+ AAAAAAAAAAAAAHcfACBvIADnbxwA924cAPBuHADwbhwA8G4cAPBuHADwbhwA8G4cAPBuHADwbhwA8G4c
+ APBuHADwbhwA8G4cAPBuHADwbhwA8G4cAPBuHADwbhwA8G4cAPBuHADwbhwA8G4cAPBuHADwbhwA8G4c
+ APBuHADwbhwA8G4cAPBuHADwbhwA8G4cAPBuHADwbh8A9m8cAOBzIwDfcyQA9XMkAPB2IwD/cyQAYwAA
+ AAAAAAAAAAAAAHUgAF13IwD/dCEA83UkAO91JADvdSQA73UkAO91JADvdSQA73UkAO91JADvdSQA73Uk
+ AO91JADvdiMA/3YhAM1tEgAOAAAAAAAAAAAAAAAAdCIAdnYkAP91JADvdSQA73UkAO91JADvdSQA73Uk
+ AO91JADvdSQA73UkAO91JADvdSQA73UkAO91JADvdSQA73UkAO91JADvdSQA73UkAO91JADvdSQA73Uk
+ AO91JADvdSQA73UkAO91JADvdSQA73UkAO91JADvdSQA73UkAO91JADvdSQA73MkAPVzIwDfeCQA33gj
+ APR4JgD+dyQAvP8AAAEAAAAAAAAAAHMeACF2JADndyUA+HUlAO53JgDvdyYA73cmAO93JgDvdyYA73cm
+ AO93JgDvdyYA73cmAO93JgDvdyYA73clAO94JgD/diUAkAAAAAAAAAAAAAAAAHciAA94IwDYdSMA+Xcl
+ AO93JgDvdyYA73cmAO93JgDvdyYA73cmAO93JgDvdyYA73cmAO93JgDvdyYA73cmAO93JgDvdyYA73cm
+ AO93JgDvdyYA73cmAO93JgDvdyYA73cmAO93JgDvdyYA73cmAO93JgDvdyYA73cmAO93JgDvdyYA73cm
+ AO91JgD1eCQA33koAN94JwD4dyQA+X0nAEEAAAAAAAAAAAAAAAB5JwCqeScA/3UlAO53JwDvdycA73cn
+ AO93JwDvdycA73cnAO93JwDvdycA73cnAO93JwDvdycA73cnAO93JwDveCQA8nYkAPl5JAA/AAAAAAAA
+ AAAAAAAAeiYAcHooAP93JwDvdycA73cnAO93JwDvdycA73cnAO93JwDvdycA73cnAO93JwDvdycA73cn
+ AO93JwDvdycA73cnAO93JwDvdycA73cnAO93JwDvdycA73cnAO93JwDvdycA73cnAO93JwDvdycA73cn
+ AO93JwDvdycA73cnAO93JwDvdiYA9XkoAN9+JwDegSgA/3wmAKYAAAAAAAAAAAAAAAB9KQBPeicA/X4m
+ APB+JQDufiUA7n4lAO5+JQDufiUA7n4lAO5+JQDufiUA7n4lAO5+JQDufiUA7n4lAO5+JQDufiUA7nwk
+ AO19JgD8fCcAw5EkAAcAAAAAAAAAAH8qABJ7JgDbfiYA9n4lAO5+JQDufiUA7n4lAO5+JQDufiUA7n4l
+ AO5+JQDufiUA7n4lAO5+JQDufiUA7n4lAO5+JQDufiUA7n4lAO5+JQDufiUA7n4lAO5+JQDufiUA7n4l
+ AO5+JQDufiUA7n4lAO5+JQDufiUA7n4lAO5+JQDufiUA7n4kAPR+JwDefycA4n8qAPiCKQAxAAAAAAAA
+ AAB/MwAKfywAynwoAPt/JwDufycA7n8nAO5/JwDufycA7n8nAO5/JwDufycA7n8nAO5/JwDufycA7n8n
+ AO5/JwDufycA7n8nAO5/JwDufycA7oAqAP9+KwBjAAAAAAAAAAAAAAAAgCwAfYErAP9/JwDufycA7n8n
+ AO5/JwDufycA7n8nAO5/JwDufycA7n8nAO5/JwDufycA7n8nAO5/JwDufycA7n8nAO5/JwDufycA7n8n
+ AO5/JwDufycA7n8nAO5/JwDufycA7n8nAO5/JwDufycA7n8nAO5/JwDufycA7n8nAO5/JwD0fykA3oAs
+ AO+BLgCxAAAAAAAAAAAAAAAAgC8AZ4EvAP9/LwDufy8A7n8vAO5/LwDufy8A7n8vAO5/LwDufy8A7n8v
+ AO5/LwDufy8A7n8vAO5/LwDufy8A7n8vAO5/LwDufy8A7n8vAO6ALwD5fy4A0H8qAAwAAAAAAAAAAIMr
+ AB1/LQDmfy8A9H8vAO5/LwDufy8A7n8vAO5/LwDufy8A7n8vAO5/LwDufy8A7n8vAO5/LwDufy8A7n8v
+ AO5/LwDufy8A7n8vAO5/LwDufy8A7n8vAO5/LwDufy8A7n8vAO5/LwDufy8A7n8vAO5/LwDufy8A7n8v
+ AO5/LwDufy8A9H8vAN6ALQDqgzEASAAAAAAAAAAAiy4AC4IvAM1/LwD4gC4A7IAuAOyALgDsgC4A7IAu
+ AOyALgDsgC4A7IAuAOyALgDsgC4A7IAuAOyALgDsgC4A7IAuAOyALgDsgC4A7IAuAOyALgDsgC4A7IEv
+ AP6ELwBgAAAAAAAAAAAAAAAAgy4Ak4MwAP9/LgDsgC4A7IAuAOyALgDsgC4A7IAuAOyCLADvgC0A9oAv
+ APiCLwD3gC8A8oIvAO2ALgDsgC4A7IAuAOyALgDsgC4A7IAuAOyALgDsgC4A7IAuAOyALgDsgC4A7IAu
+ AOyALgDsgC4A7IAuAOyALgDsgC4A7IAvAPKCLwDdhjMAn38/AAQAAAAAAAAAAIc0AGuONQD/hzAA74Ut
+ AOuHLgDshy4A7IcuAOyHLgDshy4A7IcuAOyHLgDshy4A7IcuAOyHLgDshy4A7IcuAOyHLgDshy4A7Icu
+ AOyHLgDshy4A7IcuAOyFMAD4hjIAxX8qAAYAAAAAAAAAAIcyADOGLwDxhzAA74cuAOyHLgDshS0A64Uw
+ APKHMQD9hjEA7YQxAM6GMQC+hTIAxYYxAN6GLgD7hDAA+IcuAOyHLgDshy4A7IcuAOyHLgDshy4A7Icu
+ AOyHLgDshy4A7IcuAOyHLgDshy4A7IcuAOyHLgDshy4A7IcuAOyELwDyhC8A3ZE2AA4AAAAAAAAAAAAA
+ AACINAA6hzIAooYyAPKKMwD3hzYA64k2AOyJNgDsiTYA7Ik2AOyJNgDsiTYA7Ik2AOyJNgDsiTYA7Ik2
+ AOyJNgDsiTYA7Ik2AOyJNgDsiTYA7Ik2AOyJNgDshzMA7YgzAPuHNgBPAAAAAAAAAAAAAAAAhzYAs4c2
+ APqHNgDriTYA7IczAPyKNQDhiDYBg4k1CTSZMwAKAAAAAKpVAAOEOAAbijYAXog1AMSJNQD9ijYA8Ic2
+ AOuJNgDsiTYA7Ik2AOyJNgDsiTYA7Ik2AOyJNgDsiTYA7Ik2AOyJNgDsiTYA7Ik2AOyJNgDshjMA8ok2
+ AN0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACKOABEiTYD14o1APuKNgDqhzgA64c4AOuHOADrhzgA64c4
+ AOuHOADrhzgA64c4AOuHOADrhzgA64c4AOuHOADrhzgA64c4AOuHOADrhzgA64c4AOuIOAD6jDYAsgAA
+ AAAAAAAAAAAAAIs3AFiINwD6hzkA7Yc4AP6KNwCqijoAIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAB/PwAIijcAe4c3APSKNgDzijYA6oc4AOuHOADrhzgA64c4AOuHOADrhzgA64c4AOuHOADrhzgA64c4
+ AOuHOADrhzgA64g4APGHNgDcAAAAAAAAAAAAAAAAjjgARIUwABUAAAAAAAAAAI48HiKNOQDMjzcA+4w3
+ AOqOOQDrjjkA6445AOuOOQDrjjkA6445AOuOOQDrjjkA6445AOuOOQDrjjkA6445AOuOOQDrjjkA6445
+ AOuOOQDrjjYA7os4APGJNQA0AAAAAAAAAACIMwAPizkA1pI7AP+NOACQf38AAgAAAAAAAAAAfzMzCow7
+ C0WPOgBpkDoCXIs5IB8AAAAAAAAAAAAAAACLNw9SjDkB7I45APSMNwDqjjkA6445AOuOOQDrjjkA6445
+ AOuOOQDrjjkA6445AOuOOQDrjjkA6445AOuLOQDxjjcA3AAAAAAAAAAAkD4AJY88APKOOQDVjz4AOQAA
+ AAAAAAAAizkIH487AtKPOgD5jjcA6pA5AOuQOQDrkDkA65A5AOuQOQDrkDkA65A5AOuQOQDrkDkA65A5
+ AOuQOQDrkDkA65A5AOuQOQDrkDkA6444AOqQOgD9jzsAkgAAAAAAAAAAAAAAAJE6AJWQPACqAAAAAAAA
+ AAAAAAAAjzsAUo48AsyQOwH2jTsA/o45APyQOwLkjzwAfpkzAAUAAAAAAAAAAJE8B0iQOwLwjToA8Y44
+ AOqQOQDrkDkA65A5AOuQOQDrkDkA65A5AOuQOQDrkDkA65A5AOuQOQDrjToA8Y43ANwAAAAAAAAAAJI+
+ AH+SPwD9kj8A+ZA8AOePPQA+AAAAAAAAAACWPwssjz8A5ZBAAPOQPwDpkz8A6pM/AOqTPwDqkz8A6pM/
+ AOqTPwDqkz8A6pM/AOqTPwDqkz8A6pM/AOqTPwDqkz8A6pM/AOqTPwDqkj8A8ZM+At6UPx8YAAAAAAAA
+ AACRPxIckUgADgAAAAAAAAAAkj4AepI+APmQPAD3kkAA65M/AOqTPwDqkz8A8ZM/AP+RPwCxk0MAEwAA
+ AAAAAAAAkj8CYJE+APmSPwDtkz8A6pM/AOqTPwDqkz8A6pM/AOqTPwDqkz8A6pM/AOqTPwDqkz8A6pA9
+ APCRPADbAAAAAJlEAA+WPgDUlkIA85dAAOmUQgD3lD8A4ZRBACsAAAAAAAAAAJNBAE6UQAD1k0IA7pZC
+ AOqWQgDqlkIA6pZCAOqWQgDqlkIA6pZCAOqWQgDqlkIA6pZCAOqWQgDqlkIA6pZCAOqWQgDqlkIA6pZC
+ AOqUPwH8l0AEagAAAAAAAAAAAAAAAAAAAAAAAAAAlUIAb5NBAP6WQgDvlkIA6pZCAOqWQgDqlkIA6pZC
+ AOqUQgDrlkIA/5ZBALeZTAAKAAAAAAAAAACUQQCQlkAA/ZZCAOqWQgDqlkIA6pZCAOqWQgDqlkIA6pZC
+ AOqWQgDqlkIA6pZCAOqUQADwlUIA3AAAAACWQQBdlEEA+ZNBAOmTQQDplUQA6JRBAPmVQwDImUQADwAA
+ AAAAAAAAl0MAgJRBAPyTQQDpk0EA6ZNBAOmTQQDpk0EA6ZNBAOmTQQDpk0EA6ZNBAOmTQQDpk0EA6ZNB
+ AOmTQQDpk0EA6ZNBAOmTQQDpk0EA9ZVEAL+ZZgAFAAAAAAAAAAAAAAAAmkEDQpZBAPGVQwDvlUQA6JNB
+ AOmTQQDpk0EA6ZNBAOmTQQDpk0EA6ZNBAOmURAD+lEIAmQAAAAAAAAAAiToADZZDAMiWQwD2lUQA6JNB
+ AOmTQQDpk0EA6ZNBAOmTQQDpk0EA6ZNBAOmTQQDplUMA75RDAN9/AAACl0YAuZZHAPSVRwDolUcA6JVH
+ AOiYRQDnlkQA/JlEAJkAAAAAAAAAAJlmAAWXRQC0mEMA+JhFAOeVRwDolUcA6JVHAOiVRwDolUcA6JVH
+ AOiVRwDolUcA6JVHAOiVRwDolUcA6JVHAOiVRwDolUcA6JhHAOmXRgD0m0MHSAAAAAAAAAAAnz8ACJlG
+ BcSWRwD3mEUA55VHAOiVRwDolUcA6JVHAOiVRwDolUcA6JVHAOiVRwDol0QA6plGAPuYRABoAAAAAAAA
+ AACcRQg+mUYB8JVGAOuVRgDolUcA6JVHAOiVRwDolUcA6JVHAOiVRwDolUYA6JZHAPSXRgDAnEoAPp1F
+ APKeRQDqnUgA6J1IAOidSADonUgA6J5JAOqaRQD5m0YAWgAAAAAAAAAAm0oAKZ1HAOWbRwDvnUgA6J1I
+ AOidSADonUgA6J1IAOidSADonUgA6J1IAOidSADonUgA6J1IAOidSADonUgA6J1IAOiaSADsnEUA7ptF
+ ADsAAAAAAAAAAKpVAAObRwGhnUUA+J1IAOidSADonUgA6J1IAOidSADonUgA6J1IAOidSADonUgA6J1I
+ AOiaRwDvnUcA6JxFACwAAAAAAAAAAJpIAIyaRQD8m0YA551IAOidSADonUgA6J1IAOidSADonUgA6J5I
+ AOidSAD6nEgAap5LAJ+fSgD4oEsA56BLAOegSwDnoEsA56BLAOecSwDnn0sA8J5JAN6fTwAgAAAAAAAA
+ AACeSgBqn0oA+p9IAOigSwDnoEsA56BLAOegSwDnoEsA56BLAOegSwDnoEsA56BLAOegSwDnoEsA56BL
+ AOecSwDnn0oA+59KAJAAAAAAAAAAAAAAAAAAAAAAoEoAQZ5JAPOdSgDpoEsA56BLAOegSwDnoEsA56BL
+ AOegSwDnoEsA56BLAOegSwDnnksA5pxKAPegSwC0fz8ABAAAAACZTAAUnkgA059IAPKcSwDnoEsA56BL
+ AOegSwDnoEsA56BLAOedSADun0kA26ZNABehTQDaok0A7aJOAOeiTgDnok4A56JOAOeiTgDnok4A56BM
+ AOafSwD5oU0EoQAAAAAAAAAAvz8ABKJNALigTAD2oEwA5qJOAOeiTgDnok4A56JOAOeiTgDnok4A56JO
+ AOeiTgDnok4A56JOAOeiTgDnoUwA8J5NANmmTgAaAAAAAAAAAAAAAAAAAAAAAL8/AASgTQC8n00A9KJO
+ AOeiTgDnok4A56JOAOeiTgDnok4A56JOAOeiTgDnok4A56JOAOeiTgDnn0sA+aBNAmkAAAAAAAAAAKFN
+ AFKfSwD2o0sA6aJOAOeiTgDnok4A56JOAOeiTgDnn0sA+Z9MAJAAAAAAok8A2qVNAOykTwDnpE8A56RP
+ AOekTwDnpE8A56RPAOekTwDnpE0A6aVQAfWmTgNRAAAAAAAAAACmUwAxpU0B6aVNAOykTwDnpE8A56RP
+ AOekTwDnpE8A56RPAOekTwDnpE8A56RPAOekTwDnpE0A6aNQAPiiTgBYAAAAAAAAAAChUCgTv19fCAAA
+ AAAAAAAApE8AYKFNAPmkTwDnpE8A56RPAOekTwDnpE8A56RPAOekTwDnpE8A56RPAOekTwDnpE8A56FQ
+ AO6gTQLjpVIGJQAAAAAAAAAApFAAm6VOAPqlTQDmpE8A56RPAOekTwDnpU0A6aFPAO6iTgk3AAAAAKdT
+ ANenUwDsp1MA5qdTAOanUwDmp1MA5qdTAOanUwDmp1MA5qdTAOanUQDxp1IA0p1IABUAAAAAAAAAAKZR
+ BHGnUAD6pVAA56dTAOanUwDmp1MA5qdTAOanUwDmp1MA5qdTAOanUwDmqFMA5adQAPqnUgObAAAAAAAA
+ AAAAAAAApVIBrqVSA0oAAAAAAAAAAKpVAA+lUgHQp1MA76dTAOanUwDmp1MA5qdTAOanUwDmp1MA5qdT
+ AOanUwDmp1MA5qdTAOaoUwDlpVAA9qdTAK9/PwAEAAAAAKZYABelUgDTqFEA8ahTAOWnUwDmp1MA5qRP
+ APOnTwG3/38AAgAAAACoVQDXp1UA7KhVAOaoVQDmqFUA5qhVAOaoVQDmqFUA5qhVAOaoVQDmp1UA5qZV
+ APioUwCTAAAAAAAAAACqVQADp1YApqlTAPqoVQDmqFUA5qhVAOaoVQDmqFUA5qhVAOaoVQDmqFMA5aZW
+ APWnUgDJrlAaEwAAAAAAAAAAqFQAW61YAP+oUwCZAAAAAAAAAAAAAAAAqVUCdKVWAPmoVQDmqFUA5qhV
+ AOaoVQDmqFUA5qhVAOaoVQDmqFUA5qhVAOaoVQDmqFUA5qhWAOaoUgD6qVUAdAAAAAAAAAAAplUARalT
+ APGoUwDrqFUA5qhWAOalVQD4qFQFZAAAAAAAAAAAqFYA16dXAOyoVgDmqFYA5qhWAOaoVgDmqFYA5qhW
+ AOaoVgDmqFYA5qpVAOWnVwDnqlQA9KtYAFkAAAAAAAAAAK5dABOrVgDDqFYA+KlUAOWqVQDlqFYA5qhW
+ AOaqVQDlqVQA5adWAPOrVgLeqlUcLQAAAAAAAAAArFIGJatWAd6oVwDyqFYA4K9XACAAAAAAAAAAALFY
+ ABeqUwDZplcA7apVAOWoVgDmqFYA5qhWAOaoVgDmqFYA5qhWAOaoVgDmqFYA5qhWAOaqVQDlp1cA6qlX
+ AOyoVgA+AAAAAAAAAACsVgB2qlQA+qdXAOenVwDsqlMB2apVChgAAAAAAAAAAK1YANWuWQDqrlkA5K5Z
+ AOSuWQDkrlkA5K5ZAOSuWQDkrlkA5K5ZAOSuWQDkrlcA46xWAOurWQDisFgJNAAAAAAAAAAArVsAHK1Y
+ AMGuWgD5rlkA565XAOOuVwDjrFkA5a9ZAParWALZrVYAOAAAAAAAAAAAqlUADK1ZALmuWQDzrlcA461W
+ APesWAB8AAAAAAAAAAAAAAAArFgAea5WAPetWQDkrlkA5K5ZAOSuWQDkrlkA5K5ZAOSuWQDkrlkA5K5Z
+ AOSuWQDkrlkA5K5XAOOrVwDvrFcA0q1RABwAAAAAAAAAAKxZBpeuWQH5rlkA9q5ZAY8AAAAAAAAAAAAA
+ AACtWADVr1oA6q5ZAOSuWQDkrlkA5K5ZAOSuWQDkrlkA5K5ZAOSuWQDkrlkA5K5ZAOSvVwDjr1oA8LBc
+ ANKzYAAlAAAAAAAAAACqYw4SrloGmK5bAO6vWAD3r1gA97BbAfOvWwGzrl0fKQAAAAAAAAAAqlUAA65a
+ AJ6vWAD3rlkA5K5ZAOSvWADsrlsA1LJmABQAAAAAAAAAALJZDBSwWQHTr1oA7a5ZAOSuWQDkrlkA5K5Z
+ AOSuWQDkrlkA5K5ZAOSuWQDkrlkA5K5ZAOSuWQDkr1gA46xZAPWwWwC2qlUADAAAAAC2bW0HsFsAorBa
+ AP2vYAA9AAAAAAAAAAD/fwACsl8A1LFfAOmxXwDjsV8A47FfAOOxXwDjsV8A47FfAOOxXwDjsV8A47Ff
+ AOOxXwDjsV8A47FeAOOxXADys14Aza9gAC0AAAAAAAAAAAAAAAC1XwA7sl4KfLNeBYGzYBFK////AQAA
+ AAAAAAAAtkgAB7NfAJivXAD1r18A5LFfAOOxXwDjsV8A47JfAPa0XgB0AAAAAAAAAAAAAAAAtF4AZ7Je
+ APaxXwDjsV8A47FfAOOxXwDjsV8A47FfAOOxXwDjsV8A47FfAOOxXwDjsV8A47FfAOOxXwDjsV8A9rNe
+ AKW2bQAHAAAAAL9/AASxXQZSuVwACwAAAAAAAAAAf38AArRfANSzXwDpsl8A47JfAOOyXwDjsl8A47Jf
+ AOOyXwDjsl8A47JfAOOyXwDjsl8A47JfAOOyXwDjtF0A4rNdAPC2XQDatmEAUQAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAtV0AJrZhAK+1XQD1tV8A5LJfAOOyXwDjsl8A47JfAOO1XwDrtGAA1LFj
+ ABcAAAAAAAAAALZtAAe2YAC+s10A8LRdAOKyXwDjsl8A47JfAOOyXwDjsl8A47JfAOOyXwDjsl8A47Jf
+ AOOyXwDjsl8A47JfAOO2XQD1tF8Aq7xeABsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC5YQDUumIA6bph
+ AOO6YQDjumEA47phAOO6YQDjumEA47phAOO6YQDjumEA47phAOO6YQDjumEA47phAOO6ZADit2QA7Ldk
+ AO+4ZQOcuWYNN8xmAAUAAAAAAAAAAAAAAAC6ZxslumQEerdjANq2YgDzuWEA47lhAOO6YQDjumEA47ph
+ AOO6YQDjuWEA47hjAPa5YwCAAAAAAAAAAAAAAAAAuWQAQrdkAO+5ZADnuWEA47phAOO6YQDjumEA47ph
+ AOO6YQDjumEA47phAOO6YQDjumEA47phAOO5YQDjumEA47dhAPO5YgDXtmIAMQAAAAAAAAAAAAAAAH9/
+ AAK5ZAAhumYA1LtjAOm6ZgDjumYA47pmAOO6ZgDjumYA47pmAOO6ZgDjumYA47pmAOO6ZgDjumYA47pm
+ AOO6ZgDjumYA47tlAOK7ZgDlumYA8rtjAea7ZgC9u2cAnrtkAJu8ZgCzvGUB3bliAPO8YwDpu2UA4rpm
+ AOO6ZgDjumYA47pmAOO6ZgDjumYA47xlAOK4ZgDot2IA4b1iACcAAAAAAAAAAAAAAAC5ZwCHuGcA97tl
+ AOK6ZgDjumYA47pmAOO6ZgDjumYA47pmAOO6ZgDjumYA47pmAOO6ZgDjumYA47tlAOK8ZwDwuWcAxsJh
+ ABUAAAAAAAAAAJlmZgW6ZwOoumUA1LxpANO9aADovWYA4r1mAOK9ZgDivWYA4r1mAOK9ZgDivWYA4r1m
+ AOK9ZgDivWYA4r1mAOK9ZgDivWYA4r1mAOK9ZgDivWYA4r1mAOK9ZgDlvWYA7LxmAPG8ZwDxu2YA7rxp
+ AOa9ZgDivWYA4r1mAOK9ZgDivWYA4r1mAOK9ZgDivWYA4r1mAOK9ZgDivmkA4btoAPK7aQCiAAAAAAAA
+ AAAAAAAAzGYACr1nALu7aADyvWkA4b1mAOK9ZgDivWYA4r1mAOK9ZgDivWYA4r1mAOK9ZgDivWYA4r1m
+ AOK9aADoumYA58FsADYAAAAAAAAAAAAAAAC9agdiumkB/r1oANm8awDTvmkA6L1sAOK9bADivWwA4r1s
+ AOK9bADivWwA4r1sAOK9bADivWwA4r1sAOK9bADivWwA4r1sAOK9bADivWwA4r1sAOK9bADivWwA4r1s
+ AOK9bADivWwA4r1sAOK9bADivWwA4r1sAOK9bADivWwA4r1sAOK9bADivWwA4r1sAOK9bADivWwA4r1s
+ AOK8agDjv2kA8b9sAFkAAAAAAAAAAAAAAADEaA0nv2wD2b1sAO6/agDhvWwA4r1sAOK9bADivWwA4r1s
+ AOK9bADivWwA4r1sAOK+bADlv2kA9L5sAGcAAAAAAAAAAAAAAAC8ZwAbvWsA1sBqAO+8awDTv28A0b5r
+ AObBbgDgwW4A4MFuAODBbgDgwW4A4MFuAODBbgDgwW4A4MFuAODBbgDgwW4A4MFuAODBbgDgwW4A4MFu
+ AODBbgDgwW4A4MFuAODBbgDgwW4A4MFuAODBbgDgwW4A4MFuAODBbgDgwW4A4MFuAODBbgDgwW4A4MFu
+ AODBbgDgwW4A4MFuAODBbgDgwW4A4MJrAOi/bQHZw2kHIgAAAAAAAAAAAAAAAMJsBDvAbADdwGsA7cJr
+ AN/BbgDgwW4A4MFuAODBbgDgwW4A4MFuAODBbADkv20A9cFtA4UAAAAAAAAAAAAAAAAAAAAAw24An8Bv
+ APG/bgDlv28A0chwANHGcQDmx28A4MdvAODHbwDgx28A4MdvAODHbwDgx28A4MdvAODHbwDgx28A4Mdv
+ AODHbwDgx28A4MdvAODHbwDgx28A4MdvAODHbwDgx28A4MdvAODHbwDgx28A4MdvAODHbwDgx28A4Mdv
+ AODHbwDgx28A4MdvAODHbwDgx28A4MdvAODHbwDgx28A4MdvAODEcADfxnAA78ZwALO/fwAIAAAAAAAA
+ AAAAAAAAx28AN8ZvAM7HcADzx20A5MdvAODHbwDgx28A4MdwAOHFcADsxXAA7sRxAH7/AP8BAAAAAAAA
+ AAAAAAAAxnAAZMNuAPDHcADhxm0A5shwANHJcQDRx3QA5sl0AODJdADgyXQA4Ml0AODJdADgyXQA4Ml0
+ AODJdADgyXQA4Ml0AODJdADgyXQA4Ml0AODJdADgyXQA4Ml0AODJdADgyXQA4Ml0AODJdADgyXQA4Ml0
+ AODJdADgyXQA4Ml0AODJdADgyXQA4Ml0AODJdADgyXQA4Ml0AODJdADgyXQA4Ml0AODJdADgyXQA4Ml0
+ AODIdADzx3QBl/8AAAEAAAAAAAAAAAAAAADNdQAayHMAlsVxAOPIcgDyyXMA8clyAPLIcgDuyHMBwMZz
+ AE0AAAAAAAAAAAAAAAAAAAAAynQARMZzAObJcgDlyXQA4Md0AObJcQDRy3QA0MlyAOXLcwDfy3MA38tz
+ AN/LcwDfy3MA38tzAN/LcwDfy3MA38tzAN/LcwDfy3MA38tzAN/LcwDfy3MA38tzAN/LcwDfy3MA38tz
+ AN/LcwDfy3MA38tzAN/LcwDfy3MA38tzAN/LcwDfy3MA38tzAN/LcwDfy3MA38tzAN/LcwDfy3MA38tz
+ AN/LcwDfy3MA38tzAN/LcwDfyXQA4Mt1APHKdgCMAAAAAAAAAAAAAAAAAAAAAAAAAADKeQAsyXcAacp1
+ AILKdwB1xnQDSNptJAcAAAAAAAAAAAAAAAAAAAAAx3IAPMpyANrLdADoy3UA3stzAN/JcgDly3QA0Mt5
+ ANDJeQDly3kA38t5AN/LeQDfy3kA38t5AN/LeQDfy3kA38t5AN/LeQDfy3kA38t5AN/LeQDfy3kA38t5
+ AN/LeQDfy3kA38t5AN/LeQDfy3kA38t5AN/LeQDfy3kA38t5AN/LeQDfy3kA38t5AN/LeQDfy3kA38t5
+ AN/LeQDfy3kA38t5AN/LeQDfy3kA38t5AN/LeQDfy3kA38t5AN/JeQDhzHkA8Mp6AZbEdQANAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzXcATct3AN7KeQDpzHcA3st5
+ AN/LeQDfyXkA5ct5ANDQegDQ0HoA5dB7AN/QewDf0HsA39B7AN/QewDf0HsA39B7AN/QewDf0HsA39B7
+ AN/QewDf0HsA39B7AN/QewDf0HsA39B7AN/QewDf0HsA39B7AN/QewDf0HsA39B7AN/QewDf0HsA39B7
+ AN/QewDf0HsA39B7AN/QewDf0HsA39B7AN/QewDf0HsA39B7AN/QewDf0HsA39B7AN/QewDf0XwA3tB4
+ AODQewDw0HsAutB7ADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADJeAATz3sAgs17
+ AOjPegDn0XwA3tB7AN/QewDf0HsA39B6AOXQegDQ1H4A0NR+AOTUfgDe1H4A3tR+AN7UfgDe1H4A3tR+
+ AN7UfgDe1H4A3tR+AN7UfgDe1H4A3tR+AN7UfgDe1H4A3tR+AN7UfgDe1H4A3tR+AN7UfgDe1H4A3tR+
+ AN7UfgDe1H4A3tR+AN7UfgDe1H4A3tR+AN7UfgDe1H4A3tR+AN7UfgDe1H4A3tR+AN7UfgDe1H4A3tR+
+ AN7UfgDe1H4A3tR+AN7UfgDe034A3tJ8AOvTfADj038AmNJ9AEXUfwASAAAAAAAAAAAAAAAAzGYABdeC
+ ACfSfQBu0n0AydN7AO/RfADh1HwA3dR+AN7UfgDe1H4A3tR+AN7UfgDk1H4A0NR/ANDUfwDk1H8A3tR/
+ AN7UfwDe1H8A3tR/AN7UfwDe1H8A3tR/AN7UfwDe1H8A3tR/AN7UfwDe1H8A3tR/AN7UfwDe1H8A3tR/
+ AN7UfwDe1H8A3tR/AN7UfwDe1H8A3tR/AN7UfwDe1H8A3tR/AN7UfwDe1H8A3tR/AN7UfwDe1H8A3tR/
+ AN7UfwDe1H8A3tR/AN7UfwDe1H8A3tR/AN7UfwDe1H8A3tR/AN7UgADd1YAA4dN/AO7UfwDo0n8AyNaA
+ AKnVgACf1IAAp9R/ALrUgADd1H8A7tF8AOfUfwDe1H8A3tR/AN7UfwDe1H8A3tR/AN7UfwDe1H8A5NR/
+ ANDXggDP1oMA49aDAN3WgwDd1oMA3daDAN3WgwDd1oMA3daDAN3WgwDd1oMA3daDAN3WgwDd1oMA3daD
+ AN3WgwDd1oMA3daDAN3WgwDd1oMA3daDAN3WgwDd1oMA3daDAN3WgwDd1oMA3daDAN3WgwDd1oMA3daD
+ AN3WgwDd1oMA3daDAN3WgwDd1oMA3daDAN3WgwDd1oMA3daDAN3WgwDd1oMA3daDAN3WgwDd1oMA3daD
+ AN3WgwDd2IMA39mDAOXWgQDq2YAA7NmDAOvYgQDo2IMA4daDAN3WgwDd1oMA3daDAN3WgwDd1oMA3daD
+ AN3WgwDd1oMA3daDAOPXggDP3IQAzt2GAOLdhgDc3YYA3N2GANzdhgDc3YYA3N2GANzdhgDc3YYA3N2G
+ ANzdhgDc3YYA3N2GANzdhgDc3YYA3N2GANzdhgDc3YYA3N2GANzdhgDc3YYA3N2GANzdhgDc3YYA3N2G
+ ANzdhgDc3YYA3N2GANzdhgDc3YYA3N2GANzdhgDc3YYA3N2GANzdhgDc3YYA3N2GANzdhgDc3YYA3N2G
+ ANzdhgDc3YYA3N2GANzdhgDc3YYA3N2GANzchgDc3IYA3NyGANzchgDc3IYA3N2GANzdhgDc3YYA3N2G
+ ANzdhgDc3YYA3N2GANzdhgDc3YYA3N2GANzdhgDi3IQAzt2EAM7ehwDi3oYA3N6GANzehgDc3oYA3N6G
+ ANzehgDc3oYA3N6GANzehgDc3oYA3N6GANzehgDc3oYA3N6GANzehgDc3oYA3N6GANzehgDc3oYA3N6G
+ ANzehgDc3oYA3N6GANzehgDc3oYA3N6GANzehgDc3oYA3N6GANzehgDc3oYA3N6GANzehgDc3oYA3N6G
+ ANzehgDc3oYA3N6GANzehgDc3oYA3N6GANzehgDc3oYA3N6GANzehgDc3oYA3N6GANzehgDc3oYA3N6G
+ ANzehgDc3oYA3N6GANzehgDc3oYA3N6GANzehgDc3oYA3N6GANzehgDc3ocA4t2EAM7diQDO34gA4t6L
+ ANzeiwDc3osA3N6LANzeiwDc3osA3N6LANzeiwDc3osA3N6LANzeiwDc3osA3N6LANzeiwDc3osA3N6L
+ ANzeiwDc3osA3N6LANzeiwDc3osA3N6LANzeiwDc3osA3N6LANzeiwDc3osA3N6LANzeiwDc3osA3N6L
+ ANzeiwDc3osA3N6LANzeiwDc3osA3N6LANzeiwDc3osA3N6LANzeiwDc3osA3N6LANzeiwDc3osA3N6L
+ ANzeiwDc3osA3N6LANzeiwDc3osA3N6LANzeiwDc3osA3N6LANzeiwDc3osA3N6LANzeiwDc3osA3N+I
+ AOLdiQDO4owAzeGMAOHkjgDb5I4A2+SOANvkjgDb5I4A2+SOANvkjgDb5I4A2+SOANvkjgDb5I4A2+SO
+ ANvkjgDb5I4A2+SOANvkjgDb5I4A2+SOANvkjgDb5I4A2+SOANvkjgDb5I4A2+SOANvkjgDb5I4A2+SO
+ ANvkjgDb5I4A2+SOANvkjgDb5I4A2+SOANvkjgDb5I4A2+SOANvkjgDb5I4A2+SOANvkjgDb5I4A2+SO
+ ANvkjgDb5I4A2+SOANvkjgDb5I4A2+SOANvkjgDb5I4A2+SOANvkjgDb5I4A2+SOANvkjgDb5I4A2+SO
+ ANvkjgDb5I4A2+SOANvhjADh4owAzeaRAM3mkQDh5pEA2+aRANvmkQDb5pEA2+aRANvmkQDb5pEA2+aR
+ ANvmkQDb5pEA2+aRANvmkQDb5pEA2+aRANvmkQDb5pEA2+aRANvmkQDb5pEA2+aRANvmkQDb5pEA2+aR
+ ANvmkQDb5pEA2+aRANvmkQDb5pEA2+aRANvmkQDb5pEA2+aRANvmkQDb5pEA2+aRANvmkQDb5pEA2+aR
+ ANvmkQDb5pEA2+aRANvmkQDb5pEA2+aRANvmkQDb5pEA2+aRANvmkQDb5pEA2+aRANvmkQDb5pEA2+aR
+ ANvmkQDb5pEA2+aRANvmkQDb5pEA2+aRANvmkQDb5pEA4eaRAM3okwDM6JAA4OaTANrmkwDa5pMA2uaT
+ ANrmkwDa5pMA2uaTANrmkwDa5pMA2uaTANrmkwDa5pMA2uaTANrmkwDa5pMA2uaTANrmkwDa5pMA2uaT
+ ANrmkwDa5pMA2uaTANrmkwDa5pMA2uaTANrmkwDa5pMA2uaTANrmkwDa5pMA2uaTANrmkwDa5pMA2uaT
+ ANrmkwDa5pMA2uaTANrmkwDa5pMA2uaTANrmkwDa5pMA2uaTANrmkwDa5pMA2uaTANrmkwDa5pMA2uaT
+ ANrmkwDa5pMA2uaTANrmkwDa5pMA2uaTANrmkwDa5pMA2uaTANrmkwDa5pMA2uiQAODokwDM65cA0eyT
+ AObqlgDg6pYA4OqWAODqlgDg6pYA4OqWAODqlgDg6pYA4OqWAODqlgDg6pYA4OqWAODqlgDg6pYA4OqW
+ AODqlgDg6pYA4OqWAODqlgDg6pYA4OqWAODqlgDg6pYA4OqWAODqlgDg6pYA4OqWAODqlgDg6pYA4OqW
+ AODqlgDg6pYA4OqWAODqlgDg6pYA4OqWAODqlgDg6pYA4OqWAODqlgDg6pYA4OqWAODqlgDg6pYA4OqW
+ AODqlgDg6pYA4OqWAODqlgDg6pYA4OqWAODqlgDg6pYA4OqWAODqlgDg6pYA4OqWAODqlgDg6pYA4OqW
+ AODskwDm65cA0fCaAL7rmADR7ZoAy+2aAMvtmgDL7ZoAy+2aAMvtmgDL7ZoAy+2aAMvtmgDL7ZoAy+2a
+ AMvtmgDL7ZoAy+2aAMvtmgDL7ZoAy+2aAMvtmgDL7ZoAy+2aAMvtmgDL7ZoAy+2aAMvtmgDL7ZoAy+2a
+ AMvtmgDL7ZoAy+2aAMvtmgDL7ZoAy+2aAMvtmgDL7ZoAy+2aAMvtmgDL7ZoAy+2aAMvtmgDL7ZoAy+2a
+ AMvtmgDL7ZoAy+2aAMvtmgDL7ZoAy+2aAMvtmgDL7ZoAy+2aAMvtmgDL7ZoAy+2aAMvtmgDL7ZoAy+2a
+ AMvtmgDL7ZoAy+2aAMvtmgDL65gA0fCaAL4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAD/gAAAAAAAA//gAAAAAAAHwfAAAA
+ AAAA8AeAAAAAAAHgA8AAAAAAA4AB4AAAAAADgADgAAAAAAcAAHAAAAAABgAAcAAAAAAOAAA4AAAAABwA
+ ABgAAAAAGAAAHAAAAAA4AAAMAAAAADAAAA4AAAAAMAAABgAAAABwAAAHAEAAAPwAAAcD+AAA5gAAAwYO
+ AADDAAADnAMAAMGAAAGYAYAAgMAAAfAAwACAYAAA4ADAAABgAADAAGAAADAAAMAAMAAAGAAB4AAQAAAY
+ AAHgABgBAAwAAzAADAEABgAHMAAEAQAGAAY4AAYDAAMADBgAAwMAAYAYHAABhwAAwDAMAACGAABwYA4A
+ AEYAAD/ABgAAPwAABwAHAAAcAAAAAAOAABgAAAAAA4AAOAAAAAABwABwAAAAAADgAPAAAAAAAHAA4AAA
+ AAAAOAPAAAAAAAA+B4AAAAAAAA//AAAAAAAAB/wAAAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
+ wwAADsMBx2+oZAAAAPhJREFUSEu1kNERwjAMQzMOn+0OjNVlmIgNgG/YgdQyMcSJyjUh5O7dNbIqtQ4x
+ xr9CxZFQcSRUHAkVDTlH4cBmADN42MygIpCzCE/hzOYAs+RZ2Bxw8ROOy5V5AGbJs1lSC69wPBh7Coyq
+ xF/8lxstBdWf5GYWDloKgCsxY7mWnNYCQ0twtr7c6C3QPxG+hoPeAoDscM8Exi8FyA5zemAG0FuAzNmM
+ k/AQShPoKUDWpJ7MjBL2J60FyNBw9RQvsHW1FOhanCe/qFCva2/Bey3OUwoq+nVdmAdgljxuLc7DRCAH
+ 67oJJzYHmCWPW0sOFUdCxZFQcRwxrOuq/3kR1YvqAAAAAElFTkSuQmCC
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAA3
+ XAAAN1wBy8ekuQAAAcBJREFUSEu1lr9KA0EQxjdHIoiIiI2VjYVY2IlIGrEQEcR3EJsUwc4TCQELS0FE
+ RNJIChGOICKCiC8gwQe6Luc3cfacndtc/pwu/Mjt3Ow3t5PZuTNhGA4ljuMpY8wxOGVqsAU+X43XqMFY
+ A1+SUqm06PPVZAxJksxDYFPaMA6ssGBD+iDgVqVSmZY2wpmweMQCTVqAVJRxfcY2ST9N+J0Dl2xrBUEw
+ IzW1+CM7WjrgVdkkT0Dfb8mdpAGwxX3lWIQ0fWmA/sSYG+E0KeeOppz0er0FOLyrBePQGfgfEFzvRQJE
+ +nyQaBn5X6Yyg8OJWiDpgjZ44GufD1EDVbDEVZYravkE1fSpfs5FXhDLITBNYRjErdw2B7HnJY86+P8A
+ 1Lx8NyWUot/aNmYXjJKiI1s5q2AH+FqChYLcgXuQJ05PvQ1WqICcbdMRx40PdpyETqZM5YT70ZtaNA6R
+ 7qhOAIwr4TwpDUdTiO8Jp6KsZwKod4HlGeS1jhegU9qW/SgNQHCzs0EuyJGrrME2SZ2qhB/smm3USmal
+ phOAoAXUl6QNY+grE4PK3BHv27XBB8bfvfR98PmgpkipIig9I3y2hOYbflY6gOGbmaUAAAAASUVORK5C
+ YII=
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAABE
+ IAAARCABv5pDRgAAAY9JREFUSEu1lj9KxFAQh19gRQgKXsJO7QQVLBQRERHWI1hYiScQ0Uqw8CA2oqCi
+ NsmhUob4TXiJ85JJ3OyuAx+Z92fmtzObl8QlSfKv1E6aplGe58t6sY84jhclxlrT1E4URQfOuXcY6w0W
+ 2CY8wbm1rvl1nFuFF/iCM71Jg23DG3zDnbVHEw6cW4dKpFUJtgNSpSQfLlBOdIhgu6CTTycg8H+sEVy3
+ C/bgA3Ty6QUETFfyCc3kwwX45UsEXcEF/oiriLyClVwYLLBVBePfZlm2gN8nMkwAk1tQJ7gRkaIoNvCl
+ XXpNmFmgrETa5UWalcwuIPS0az4CHqtd9zreIhz0C1jtMk+8JhgQvCJJCHrwPPokgUijXeVh1Hk05qQG
+ G/skuppmuzoraU1YYKc+SVCJtAtfn/j2A7I50QV2DMEjA4FLWeMaPLuCOD34CxIdkUCL1LcpZlYSJJgE
+ 7BAqkeAcYLUIP+aknNMbJgXbB3mrtV6Zvl3PXK/Lvc0Nk0ICuaVH1pp8PMhHgfitxfmSuB+m6Lf0bhB1
+ xAAAAABJRU5ErkJggg==
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAA3
+ XAAAN1wBy8ekuQAAAGhJREFUSEvtlbEKwDAIRM89X5ipn+qftQYyKBUsrdLFwEM89C5TAmYuxRUzccVM
+ TENEA8CUerxh7w7taQLWgHB+ZBpP3exbeEuPWR7aswNudEBIB4R0QMi/AXLKn+vaD6cCV8zEFfNgXD54
+ TuKdeifcAAAAAElFTkSuQmCC
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAA
+ TgAAAE4Bsc0fMwAAAXxJREFUSEullEFKw0AUhichImStSG8gCCIewIV4gd7BhV30EopLPYAbF9IzdBsE
+ u+lK11IPoGA3AXET/xfexJfJm8mkffCVmTfz/3+YSWqKotiIPM93jTHzqqqOtXWL2owhSZJLBFTgMxSi
+ NmNAzTggGNJpxOIEeENakyGgboW5N6QligU1Br9sGgzpiPtAhcwtTYhq4gMVY26pQ1QjDdQQc8u8ZYLE
+ EZrXYAFWYAnuwAT0mdM6XTy9XTP6TuqP0ZqjcYGFNdDEfZD5WD6spRng6fex6U2IYvGaE63JBiFBc6LT
+ GBDSa06oTdzHHsSvwkxjomld1CZBBsJM417TuehNY87ADxv5WGpal04Dx3MI8Reb0DlPgXZcK1er0Zrw
+ Bb+zwTc4pz7fiXvxC6n10QzKstyB6JnFH2maHsmNytt1I9d9/A+MeWDhS5ZlB3KTRYSsMR5pe1zqHxzB
+ FZs/0v+Hu0nCIfXRxUDmJxDQeU+1DdtC9YSnOtUWt6cwf9hd9nVpKvmOAAAAAElFTkSuQmCC
+
+
+
\ No newline at end of file
diff --git a/SDRSharp/SDRSharp.FrontEnds.Airspy/AirspyDevice.cs b/SDRSharp/SDRSharp.FrontEnds.Airspy/AirspyDevice.cs
new file mode 100644
index 0000000..a7fd471
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.Airspy/AirspyDevice.cs
@@ -0,0 +1,1060 @@
+using SDRSharp.Common;
+using SDRSharp.Radio;
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+
+namespace SDRSharp.FrontEnds.Airspy
+{
+ public class AirspyDevice : IDisposable
+ {
+ private enum CalibrationState
+ {
+ UnCalibrated,
+ PrologSent,
+ EpilogueSent
+ }
+
+ private class AnalogFilterConfig
+ {
+ public byte LPF
+ {
+ get;
+ set;
+ }
+
+ public byte HPF
+ {
+ get;
+ set;
+ }
+
+ public int Shift
+ {
+ get;
+ set;
+ }
+ }
+
+ private class AnalogFilterSet
+ {
+ public uint SampleRate
+ {
+ get;
+ set;
+ }
+
+ public AnalogFilterConfig[] Filters
+ {
+ get;
+ set;
+ }
+ }
+
+ public const float TimeConst = 0.05f;
+
+ public const uint DefaultFrequency = 103000000u;
+
+ public const uint DefaultSpyVerterLO = 120000000u;
+
+ public const uint DefaultSpyVerterThreshold = 35000000u;
+
+ public const uint CalibrationDelay = 500u;
+
+ public const uint CalibrationDuration = 100u;
+
+ public const string DeviceName = "AIRSPY";
+
+ private static readonly AnalogFilterSet[] _analogDecimationFilters = new AnalogFilterSet[4]
+ {
+ new AnalogFilterSet
+ {
+ SampleRate = 10000000u,
+ Filters = new AnalogFilterConfig[6]
+ {
+ new AnalogFilterConfig
+ {
+ LPF = 60,
+ HPF = 2,
+ Shift = 0
+ },
+ new AnalogFilterConfig
+ {
+ LPF = 38,
+ HPF = 7,
+ Shift = 1250000
+ },
+ new AnalogFilterConfig
+ {
+ LPF = 28,
+ HPF = 4,
+ Shift = 2750000
+ },
+ new AnalogFilterConfig
+ {
+ LPF = 15,
+ HPF = 5,
+ Shift = 3080000
+ },
+ new AnalogFilterConfig
+ {
+ LPF = 6,
+ HPF = 5,
+ Shift = 3200000
+ },
+ new AnalogFilterConfig
+ {
+ LPF = 2,
+ HPF = 6,
+ Shift = 3250000
+ }
+ }
+ },
+ new AnalogFilterSet
+ {
+ SampleRate = 2500000u,
+ Filters = new AnalogFilterConfig[4]
+ {
+ new AnalogFilterConfig
+ {
+ LPF = 4,
+ HPF = 0,
+ Shift = 0
+ },
+ new AnalogFilterConfig
+ {
+ LPF = 8,
+ HPF = 3,
+ Shift = -280000
+ },
+ new AnalogFilterConfig
+ {
+ LPF = 5,
+ HPF = 5,
+ Shift = -500000
+ },
+ new AnalogFilterConfig
+ {
+ LPF = 3,
+ HPF = 6,
+ Shift = -550000
+ }
+ }
+ },
+ new AnalogFilterSet
+ {
+ SampleRate = 6000000u,
+ Filters = new AnalogFilterConfig[5]
+ {
+ new AnalogFilterConfig
+ {
+ LPF = 33,
+ HPF = 2,
+ Shift = 0
+ },
+ new AnalogFilterConfig
+ {
+ LPF = 26,
+ HPF = 2,
+ Shift = 1000000
+ },
+ new AnalogFilterConfig
+ {
+ LPF = 17,
+ HPF = 4,
+ Shift = 1100000
+ },
+ new AnalogFilterConfig
+ {
+ LPF = 7,
+ HPF = 5,
+ Shift = 1200000
+ },
+ new AnalogFilterConfig
+ {
+ LPF = 2,
+ HPF = 6,
+ Shift = 1250000
+ }
+ }
+ },
+ new AnalogFilterSet
+ {
+ SampleRate = 3000000u,
+ Filters = new AnalogFilterConfig[3]
+ {
+ new AnalogFilterConfig
+ {
+ LPF = 8,
+ HPF = 0,
+ Shift = 0
+ },
+ new AnalogFilterConfig
+ {
+ LPF = 7,
+ HPF = 4,
+ Shift = -250000
+ },
+ new AnalogFilterConfig
+ {
+ LPF = 2,
+ HPF = 4,
+ Shift = -300000
+ }
+ }
+ }
+ };
+
+ private IntPtr _dev;
+
+ private uint _sampleRate;
+
+ private uint _centerFrequency;
+
+ private uint _frequencySet;
+
+ private int _decimationStages;
+
+ private byte _vgaGain;
+
+ private byte _mixerGain;
+
+ private byte _lnaGain;
+
+ private byte _linearityGain;
+
+ private byte _sensitivityGain;
+
+ private bool _isStreaming;
+
+ private bool _lnaGainAuto;
+
+ private bool _mixerGainAuto;
+
+ private bool _biasTeeEnabled;
+
+ private bool _biasTeeState;
+
+ private bool _spyVerterEnabled;
+
+ private bool _usePacking;
+
+ private bool _bypassTrackingFilter;
+
+ private bool _bypassTrackingFilterState;
+
+ private uint[] _supportedSampleRates;
+
+ private float _spyVerterPPM;
+
+ private float _iavg;
+
+ private float _qavg;
+
+ private float _alpha;
+
+ private byte _old_0x0f_value;
+
+ private byte _old_0x0b_value;
+
+ private CalibrationState _calibrationState;
+
+ private Timer _calibrationTimer;
+
+ private AnalogFilterConfig _analogFilterConfig;
+
+ private DownConverter _ddc;
+
+ private AirspyGainMode _gainMode;
+
+ private GCHandle _gcHandle;
+
+ private bool _useDynamicRangeEnhancements = Utils.GetBooleanSetting("airspy.useDynamicRangeEnhancements", true);
+
+ private unsafe static readonly airspy_sample_block_cb_fn _airspyCallback = AirspyDevice.AirSpySamplesAvailable;
+
+ public uint[] SupportedSampleRates
+ {
+ get
+ {
+ return this._supportedSampleRates;
+ }
+ }
+
+ public uint SampleRate
+ {
+ get
+ {
+ return this._sampleRate;
+ }
+ set
+ {
+ if (value != this._sampleRate)
+ {
+ if (NativeMethods.airspy_set_samplerate(this._dev, value) != 0)
+ {
+ throw new ApplicationException("Sample rate is not supported");
+ }
+ this._sampleRate = value;
+ this.OnSampleRateChanged();
+ }
+ }
+ }
+
+ public bool UseDynamicRangeEnhancements
+ {
+ get
+ {
+ return this._useDynamicRangeEnhancements;
+ }
+ set
+ {
+ this._useDynamicRangeEnhancements = value;
+ }
+ }
+
+ public uint DecimatedSampleRate
+ {
+ get
+ {
+ return this._sampleRate >> this._decimationStages;
+ }
+ }
+
+ public bool UsePacking
+ {
+ get
+ {
+ return this._usePacking;
+ }
+ set
+ {
+ this._usePacking = value;
+ NativeMethods.airspy_set_packing(this._dev, this._usePacking);
+ }
+ }
+
+ public uint Frequency
+ {
+ get
+ {
+ return this._centerFrequency;
+ }
+ set
+ {
+ this._centerFrequency = value;
+ this.UpdateFrequency();
+ }
+ }
+
+ public byte VgaGain
+ {
+ get
+ {
+ return this._vgaGain;
+ }
+ set
+ {
+ this._vgaGain = value;
+ this.UpdateGains();
+ }
+ }
+
+ public byte MixerGain
+ {
+ get
+ {
+ return this._mixerGain;
+ }
+ set
+ {
+ this._mixerGain = value;
+ this.UpdateGains();
+ }
+ }
+
+ public byte LnaGain
+ {
+ get
+ {
+ return this._lnaGain;
+ }
+ set
+ {
+ this._lnaGain = value;
+ this.UpdateGains();
+ }
+ }
+
+ public bool MixerGainAuto
+ {
+ get
+ {
+ return this._mixerGainAuto;
+ }
+ set
+ {
+ this._mixerGainAuto = value;
+ NativeMethods.airspy_set_mixer_agc(this._dev, value);
+ }
+ }
+
+ public bool LnaGainAuto
+ {
+ get
+ {
+ return this._lnaGainAuto;
+ }
+ set
+ {
+ this._lnaGainAuto = value;
+ NativeMethods.airspy_set_lna_agc(this._dev, value);
+ }
+ }
+
+ public byte LinearityGain
+ {
+ get
+ {
+ return this._linearityGain;
+ }
+ set
+ {
+ this._linearityGain = value;
+ this.UpdateGains();
+ }
+ }
+
+ public byte SensitivityGain
+ {
+ get
+ {
+ return this._sensitivityGain;
+ }
+ set
+ {
+ this._sensitivityGain = value;
+ this.UpdateGains();
+ }
+ }
+
+ public AirspyGainMode GainMode
+ {
+ get
+ {
+ return this._gainMode;
+ }
+ set
+ {
+ this._gainMode = value;
+ this.UpdateGains();
+ }
+ }
+
+ public int DecimationStages
+ {
+ get
+ {
+ return this._decimationStages;
+ }
+ set
+ {
+ if (this._decimationStages != value)
+ {
+ this._decimationStages = value;
+ this._alpha = (float)(1.0 - Math.Exp(-1.0 / (double)((float)(double)this.DecimatedSampleRate * 0.05f)));
+ this.OnSampleRateChanged();
+ }
+ }
+ }
+
+ public bool BiasTeeEnabled
+ {
+ get
+ {
+ return this._biasTeeEnabled;
+ }
+ set
+ {
+ this._biasTeeEnabled = value;
+ this.UpdateFrequency();
+ }
+ }
+
+ public bool BypassTrackingFilter
+ {
+ get
+ {
+ return this._bypassTrackingFilter;
+ }
+ set
+ {
+ this._bypassTrackingFilter = value;
+ this.UpdateTrackingFilter();
+ }
+ }
+
+ public bool SpyVerterEnabled
+ {
+ get
+ {
+ return this._spyVerterEnabled;
+ }
+ set
+ {
+ this._spyVerterEnabled = value;
+ this.UpdateFrequency();
+ }
+ }
+
+ public float SpyVerterPPM
+ {
+ get
+ {
+ return this._spyVerterPPM;
+ }
+ set
+ {
+ this._spyVerterPPM = value;
+ this.UpdateFrequency();
+ }
+ }
+
+ public bool IsStreaming
+ {
+ get
+ {
+ return this._isStreaming;
+ }
+ }
+
+ public bool IsHung
+ {
+ get
+ {
+ if (this._isStreaming)
+ {
+ return NativeMethods.airspy_is_streaming(this._dev) != airspy_error.AIRSPY_TRUE;
+ }
+ return false;
+ }
+ }
+
+ public event SamplesAvailableDelegate ComplexSamplesAvailable;
+
+ public event SamplesAvailableDelegate RealSamplesAvailable;
+
+ public event EventHandler SampleRateChanged;
+
+ public unsafe AirspyDevice(bool useRealSamples = false)
+ {
+ if (NativeMethods.airspy_open(out this._dev) != 0)
+ {
+ throw new ApplicationException("Cannot open AirSpy device");
+ }
+ if (useRealSamples)
+ {
+ NativeMethods.airspy_set_sample_type(this._dev, airspy_sample_type.AIRSPY_SAMPLE_FLOAT32_REAL);
+ }
+ else
+ {
+ NativeMethods.airspy_set_sample_type(this._dev, airspy_sample_type.AIRSPY_SAMPLE_FLOAT32_IQ);
+ }
+ uint num = default(uint);
+ NativeMethods.airspy_get_samplerates(this._dev, &num, 0u);
+ this._supportedSampleRates = new uint[num];
+ uint[] supportedSampleRates = this._supportedSampleRates;
+ fixed (uint* buffer = supportedSampleRates)
+ {
+ NativeMethods.airspy_get_samplerates(this._dev, buffer, num);
+ }
+ this._sampleRate = this._supportedSampleRates[0];
+ this._alpha = (float)(1.0 - Math.Exp(-1.0 / (double)((float)(double)this.DecimatedSampleRate * 0.05f)));
+ NativeMethods.airspy_set_samplerate(this._dev, this._sampleRate);
+ NativeMethods.airspy_set_rf_bias(this._dev, false);
+ NativeMethods.airspy_set_packing(this._dev, false);
+ this.UpdateGains();
+ this._calibrationTimer = new Timer(this.CalibrationHandler, null, 500L, -1L);
+ this._gcHandle = GCHandle.Alloc(this);
+ }
+
+ ~AirspyDevice()
+ {
+ this.Dispose();
+ }
+
+ public void Dispose()
+ {
+ if (this._dev != IntPtr.Zero)
+ {
+ try
+ {
+ this.Stop();
+ NativeMethods.airspy_close(this._dev);
+ }
+ catch (AccessViolationException)
+ {
+ }
+ if (this._gcHandle.IsAllocated)
+ {
+ this._gcHandle.Free();
+ }
+ this._dev = IntPtr.Zero;
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ private void SendCalibrationProlog()
+ {
+ if (this._calibrationState == CalibrationState.UnCalibrated)
+ {
+ airspy_error airspy_error = NativeMethods.airspy_r820t_read(this._dev, (byte)15, out this._old_0x0f_value);
+ if (airspy_error >= airspy_error.AIRSPY_SUCCESS)
+ {
+ airspy_error = NativeMethods.airspy_r820t_read(this._dev, (byte)11, out this._old_0x0b_value);
+ if (airspy_error >= airspy_error.AIRSPY_SUCCESS)
+ {
+ NativeMethods.airspy_r820t_write(this._dev, 15, (byte)((this._old_0x0f_value & -5) | 4));
+ NativeMethods.airspy_r820t_write(this._dev, 11, (byte)((this._old_0x0b_value & -17) | 0x10));
+ this._calibrationState = CalibrationState.PrologSent;
+ }
+ }
+ }
+ }
+
+ private void SendCalibrationEpilogue()
+ {
+ if (this._calibrationState == CalibrationState.PrologSent)
+ {
+ NativeMethods.airspy_r820t_write(this._dev, 11, this._old_0x0b_value);
+ NativeMethods.airspy_r820t_write(this._dev, 15, this._old_0x0f_value);
+ this._old_0x0b_value = 0;
+ this._old_0x0f_value = 0;
+ this._calibrationState = CalibrationState.EpilogueSent;
+ }
+ }
+
+ private void AbortCalibration()
+ {
+ if (this._calibrationState == CalibrationState.PrologSent)
+ {
+ this.SendCalibrationEpilogue();
+ }
+ this._calibrationState = CalibrationState.UnCalibrated;
+ }
+
+ public void CalibrateIF()
+ {
+ lock (this)
+ {
+ this.AbortCalibration();
+ this._calibrationTimer.Change(500L, -1L);
+ }
+ }
+
+ private void CalibrationHandler(object state)
+ {
+ if (this._isStreaming)
+ {
+ lock (this)
+ {
+ switch (this._calibrationState)
+ {
+ case CalibrationState.UnCalibrated:
+ this.SendCalibrationProlog();
+ this._calibrationTimer.Change(100L, -1L);
+ break;
+ case CalibrationState.PrologSent:
+ this.SendCalibrationEpilogue();
+ break;
+ }
+ }
+ }
+ }
+
+ public unsafe void Start()
+ {
+ if (!this._isStreaming)
+ {
+ this._iavg = 0f;
+ this._qavg = 0f;
+ int num = (!this._useDynamicRangeEnhancements) ? Math.Min(this._decimationStages, ConversionFilters.FirKernels100dB.Length - 1) : 0;
+ float[] array = ConversionFilters.FirKernels100dB[num];
+ float[] array2 = array;
+ fixed (float* kernel = array2)
+ {
+ NativeMethods.airspy_set_conversion_filter_float32(this._dev, kernel, array.Length);
+ }
+ if (NativeMethods.airspy_start_rx(this._dev, AirspyDevice._airspyCallback, (IntPtr)this._gcHandle) != 0)
+ {
+ throw new ApplicationException("airspy_start_rx() error");
+ }
+ this.UpdateTrackingFilter();
+ this.UpdateAnalogIFFilters();
+ this._isStreaming = true;
+ }
+ }
+
+ public void Stop()
+ {
+ if (this._isStreaming)
+ {
+ NativeMethods.airspy_stop_rx(this._dev);
+ this._isStreaming = false;
+ }
+ }
+
+ private void UpdateGains()
+ {
+ switch (this._gainMode)
+ {
+ case AirspyGainMode.Custom:
+ NativeMethods.airspy_set_lna_gain(this._dev, this._lnaGain);
+ NativeMethods.airspy_set_mixer_gain(this._dev, this._mixerGain);
+ NativeMethods.airspy_set_vga_gain(this._dev, this._vgaGain);
+ NativeMethods.airspy_set_lna_agc(this._dev, this._lnaGainAuto);
+ NativeMethods.airspy_set_mixer_agc(this._dev, this._mixerGainAuto);
+ break;
+ case AirspyGainMode.Linearity:
+ NativeMethods.airspy_set_linearity_gain(this._dev, this._linearityGain);
+ break;
+ case AirspyGainMode.Sensitivity:
+ NativeMethods.airspy_set_sensitivity_gain(this._dev, this._sensitivityGain);
+ break;
+ }
+ }
+
+ private void UpdateFrequency()
+ {
+ uint num = this._centerFrequency;
+ if (this._spyVerterEnabled && num < 35000000)
+ {
+ num += (uint)(120000000.0 * (1.0 + (double)this._spyVerterPPM * 1E-06));
+ if (!this._biasTeeState)
+ {
+ NativeMethods.airspy_set_rf_bias(this._dev, true);
+ this._biasTeeState = true;
+ }
+ }
+ else if (this._biasTeeState != this._biasTeeEnabled)
+ {
+ NativeMethods.airspy_set_rf_bias(this._dev, this._biasTeeEnabled);
+ this._biasTeeState = this._biasTeeEnabled;
+ }
+ if (this._analogFilterConfig != null)
+ {
+ num = (uint)((int)num - this._analogFilterConfig.Shift);
+ }
+ if (this._frequencySet != num)
+ {
+ this._frequencySet = num;
+ this.SetDeviceCenterFrequency();
+ this._bypassTrackingFilterState = false;
+ this.UpdateTrackingFilter();
+ }
+ }
+
+ private void UpdateTrackingFilter()
+ {
+ if (this._bypassTrackingFilter)
+ {
+ if (!this._bypassTrackingFilterState)
+ {
+ byte b;
+ NativeMethods.airspy_r820t_read(this._dev, (byte)26, out b);
+ b = (byte)((b & 0x3F) | 0x40);
+ NativeMethods.airspy_r820t_write(this._dev, 26, b);
+ this._bypassTrackingFilterState = true;
+ }
+ }
+ else if (this._bypassTrackingFilterState)
+ {
+ this.SetDeviceCenterFrequency();
+ this._bypassTrackingFilterState = false;
+ }
+ }
+
+ private void SetDeviceCenterFrequency()
+ {
+ lock (this)
+ {
+ this.AbortCalibration();
+ NativeMethods.airspy_set_freq(this._dev, this._frequencySet);
+ this.CalibrateIF();
+ }
+ }
+
+ private void UpdateAnalogIFFilters()
+ {
+ this._analogFilterConfig = null;
+ if (this._useDynamicRangeEnhancements)
+ {
+ AnalogFilterSet analogFilterSet = Array.Find(AirspyDevice._analogDecimationFilters, (AnalogFilterSet item) => item.SampleRate == this._sampleRate);
+ if (analogFilterSet != null)
+ {
+ int num = Math.Min(this._decimationStages, analogFilterSet.Filters.Length - 1);
+ this._analogFilterConfig = analogFilterSet.Filters[num];
+ this.SetAnalogIFFilters(this._analogFilterConfig.LPF, this._analogFilterConfig.HPF);
+ }
+ }
+ }
+
+ private void UpdateDDC()
+ {
+ int num = 1 << this._decimationStages;
+ if (this._ddc != null && this._ddc.DecimationRatio == num && this._ddc.SampleRate == (double)this._sampleRate)
+ {
+ return;
+ }
+ this._ddc = new DownConverter((double)this._sampleRate, num);
+ AnalogFilterConfig analogFilterConfig = this._analogFilterConfig;
+ if (analogFilterConfig != null)
+ {
+ this._ddc.Frequency = (double)analogFilterConfig.Shift;
+ }
+ }
+
+ public virtual void OnSampleRateChanged()
+ {
+ this._alpha = (float)(1.0 - Math.Exp(-1.0 / (double)((float)(double)this.DecimatedSampleRate * 0.05f)));
+ this.UpdateAnalogIFFilters();
+ EventHandler sampleRateChanged = this.SampleRateChanged;
+ if (sampleRateChanged != null)
+ {
+ sampleRateChanged(this, EventArgs.Empty);
+ }
+ }
+
+ protected unsafe virtual void OnComplexSamplesAvailable(Complex* buffer, int length, ulong droppedSamples)
+ {
+ SamplesAvailableDelegate complexSamplesAvailable = this.ComplexSamplesAvailable;
+ if (complexSamplesAvailable != null)
+ {
+ ComplexSamplesEventArgs complexSamplesEventArgs = new ComplexSamplesEventArgs();
+ complexSamplesEventArgs.Buffer = buffer;
+ complexSamplesEventArgs.Length = length;
+ complexSamplesEventArgs.DroppedSamples = droppedSamples;
+ complexSamplesAvailable(this, complexSamplesEventArgs);
+ }
+ }
+
+ protected unsafe virtual void OnRealSamplesAvailable(float* buffer, int length, ulong droppedSamples)
+ {
+ SamplesAvailableDelegate realSamplesAvailable = this.RealSamplesAvailable;
+ if (realSamplesAvailable != null)
+ {
+ RealSamplesEventArgs realSamplesEventArgs = new RealSamplesEventArgs();
+ realSamplesEventArgs.Buffer = buffer;
+ realSamplesEventArgs.Length = length;
+ realSamplesEventArgs.DroppedSamples = droppedSamples;
+ realSamplesAvailable(this, realSamplesEventArgs);
+ }
+ }
+
+ private unsafe static int AirSpySamplesAvailable(airspy_transfer* data)
+ {
+ int num = data->sample_count;
+ ulong dropped_samples = data->dropped_samples;
+ IntPtr ctx = data->ctx;
+ GCHandle gCHandle = GCHandle.FromIntPtr(ctx);
+ if (!gCHandle.IsAllocated)
+ {
+ return -1;
+ }
+ AirspyDevice airspyDevice = (AirspyDevice)gCHandle.Target;
+ if (data->sample_type == airspy_sample_type.AIRSPY_SAMPLE_FLOAT32_REAL)
+ {
+ float* samples = (float*)data->samples;
+ airspyDevice.OnRealSamplesAvailable(samples, num, dropped_samples);
+ }
+ else
+ {
+ Complex* samples2 = (Complex*)data->samples;
+ bool flag = airspyDevice._analogFilterConfig != null && airspyDevice._analogFilterConfig.Shift != 0;
+ if (airspyDevice._decimationStages > 0)
+ {
+ airspyDevice.UpdateDDC();
+ num = airspyDevice._ddc.Process(samples2, num);
+ }
+ if (!flag)
+ {
+ float num2 = airspyDevice._iavg;
+ float num3 = airspyDevice._qavg;
+ float alpha = airspyDevice._alpha;
+ for (int i = 0; i < num; i++)
+ {
+ num2 += alpha * (samples2[i].Real - num2);
+ num3 += alpha * (samples2[i].Imag - num3);
+ samples2[i].Real -= num2;
+ samples2[i].Imag -= num3;
+ }
+ airspyDevice._iavg = num2;
+ airspyDevice._qavg = num3;
+ }
+ airspyDevice.OnComplexSamplesAvailable(samples2, num, dropped_samples);
+ }
+ return 0;
+ }
+
+ public void Dump(string baseFileName = "")
+ {
+ string str = "airspy_dump_" + DateTime.Now.ToString("yyyy_MM_dd__hh_mm_ss__");
+ this.DumpFile(268435456, 131072, Path.Combine(baseFileName, str + "local_sram0_128K.bin"));
+ this.DumpFile(268959744, 73728, Path.Combine(baseFileName, str + "local_sram1_72K.bin"));
+ this.DumpFile(402653184, 18432, Path.Combine(baseFileName, str + "m0sub_sram_18K.bin"));
+ this.DumpFile(536870912, 32768, Path.Combine(baseFileName, str + "ahb1_sram_32K.bin"));
+ this.DumpFile(671088640, 32768, Path.Combine(baseFileName, str + "ahb2_sram_32K.bin"));
+ this.DumpFile(1074724884, 812, Path.Combine(baseFileName, str + "periph_adchs.bin"));
+ this.DumpFile(1074728716, 4, Path.Combine(baseFileName, str + "periph_adchs_status0.bin"));
+ this.DumpFile(1074728748, 4, Path.Combine(baseFileName, str + "periph_adchs_status1.bin"));
+ this.DumpFile(1074069524, 184, Path.Combine(baseFileName, str + "periph_cgu.bin"));
+ this.DumpTunerRegs(Path.Combine(baseFileName, str + "r820t.txt"));
+ }
+
+ private void DumpTunerRegs(string filename)
+ {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.AppendLine("Address\tValue");
+ for (int i = 0; i < 32; i++)
+ {
+ byte b;
+ NativeMethods.airspy_r820t_read(this._dev, (byte)i, out b);
+ stringBuilder.AppendLine("0x" + i.ToString("x2") + "\t0x" + b.ToString("x2"));
+ }
+ File.WriteAllText(filename, stringBuilder.ToString());
+ }
+
+ private unsafe void DumpFile(int address, int length, string fileName)
+ {
+ FileStream fileStream = new FileStream(fileName, FileMode.CreateNew);
+ try
+ {
+ byte[] array = new byte[256];
+ try
+ {
+ byte[] array2 = array;
+ fixed (byte* data = array2)
+ {
+ while (length > 0)
+ {
+ ushort num = (ushort)Math.Min(array.Length, length);
+ if (NativeMethods.airspy_spiflash_read(this._dev, (uint)address, num, data) != 0)
+ {
+ break;
+ }
+ fileStream.Write(array, 0, num);
+ length -= num;
+ address += num;
+ }
+ }
+ }
+ finally
+ {
+ }
+ }
+ finally
+ {
+ fileStream.Close();
+ }
+ }
+
+ public byte GetR820TRegister(byte reg)
+ {
+ byte result;
+ NativeMethods.airspy_r820t_read(this._dev, reg, out result);
+ return result;
+ }
+
+ public void SetR820TRegister(byte reg, byte value)
+ {
+ NativeMethods.airspy_r820t_write(this._dev, reg, value);
+ }
+
+ public byte GetSi5351CRegister(byte reg)
+ {
+ byte result;
+ NativeMethods.airspy_si5351c_read(this._dev, reg, out result);
+ return result;
+ }
+
+ public void SetSi5351CRegister(byte reg, byte value)
+ {
+ NativeMethods.airspy_si5351c_write(this._dev, reg, value);
+ }
+
+ public void SetGPIO(airspy_gpio_port_t port, airspy_gpio_pin_t pin, bool value)
+ {
+ NativeMethods.airspy_gpio_write(this._dev, port, pin, value);
+ }
+
+ public bool GetGPIO(airspy_gpio_port_t port, airspy_gpio_pin_t pin)
+ {
+ bool result;
+ NativeMethods.airspy_gpio_read(this._dev, port, pin, out result);
+ return result;
+ }
+
+ public unsafe uint GetMemory(uint address)
+ {
+ uint result = 0u;
+ NativeMethods.airspy_spiflash_read(this._dev, address, 4, (byte*)(&result));
+ return result;
+ }
+
+ public void SetIFBandwidth(byte bw)
+ {
+ this.SetAnalogIFFilters(bw, 0);
+ }
+
+ public void SetAnalogIFFilters(byte lpf, byte hpf)
+ {
+ byte[] array = new byte[4]
+ {
+ 224,
+ 128,
+ 96,
+ 0
+ };
+ byte[] array2 = new byte[16]
+ {
+ 15,
+ 14,
+ 13,
+ 12,
+ 11,
+ 10,
+ 9,
+ 8,
+ 7,
+ 6,
+ 5,
+ 4,
+ 3,
+ 2,
+ 1,
+ 0
+ };
+ int num = 0xF0 | array2[lpf & 0xF];
+ int num2 = array[lpf >> 4] | array2[hpf & 0xF];
+ NativeMethods.airspy_r820t_write(this._dev, 10, (byte)num);
+ NativeMethods.airspy_r820t_write(this._dev, 11, (byte)num2);
+ }
+
+ public unsafe void WriteFlash(uint address, ushort length, byte* data)
+ {
+ NativeMethods.airspy_spiflash_write(this._dev, address, length, data);
+ }
+
+ public unsafe void ReadFlash(uint address, ushort length, byte* data)
+ {
+ NativeMethods.airspy_spiflash_read(this._dev, address, length, data);
+ }
+
+ public void EraseFlashSector(ushort sector_num)
+ {
+ NativeMethods.airspy_spiflash_erase_sector(this._dev, sector_num);
+ }
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.Airspy/AirspyGainMode.cs b/SDRSharp/SDRSharp.FrontEnds.Airspy/AirspyGainMode.cs
new file mode 100644
index 0000000..afbbc75
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.Airspy/AirspyGainMode.cs
@@ -0,0 +1,9 @@
+namespace SDRSharp.FrontEnds.Airspy
+{
+ public enum AirspyGainMode
+ {
+ Linearity,
+ Sensitivity,
+ Custom
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.Airspy/AirspyIO.cs b/SDRSharp/SDRSharp.FrontEnds.Airspy/AirspyIO.cs
new file mode 100644
index 0000000..1526512
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.Airspy/AirspyIO.cs
@@ -0,0 +1,224 @@
+using SDRSharp.Common;
+using SDRSharp.Radio;
+using System;
+using System.Windows.Forms;
+
+namespace SDRSharp.FrontEnds.Airspy
+{
+ public class AirspyIO : IFrontendController, IIQStreamController, ITunableSource, ISampleRateChangeSource, IControlAwareObject, ISpectrumProvider, IConfigurationPanelProvider, IFrontendOffset, IDisposable
+ {
+ private const int DefaultIFOffset = -1582;
+
+ private const double DefaultSampleRate = 10000000.0;
+
+ private bool _disposed;
+
+ private ControllerPanel _gui;
+
+ private AirspyDevice _airspyDevice;
+
+ private ISharpControl _control;
+
+ private long _frequency = 102998418L;
+
+ private SamplesAvailableDelegate _callback;
+
+ private static float _aliasFreeRatio = (float)Math.Min(Math.Max(Utils.GetDoubleSetting("airspy.aliasFreeRatio", 0.8), 0.1), 1.0);
+
+ public bool CanTune
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ public long MinimumTunableFrequency
+ {
+ get
+ {
+ return 0L;
+ }
+ }
+
+ public long MaximumTunableFrequency
+ {
+ get
+ {
+ return 2000000000L;
+ }
+ }
+
+ public ISharpControl SharpControl
+ {
+ get
+ {
+ return this._control;
+ }
+ }
+
+ public double Samplerate
+ {
+ get
+ {
+ if (this._airspyDevice != null)
+ {
+ return (double)this._airspyDevice.DecimatedSampleRate;
+ }
+ return 10000000.0;
+ }
+ }
+
+ public long Frequency
+ {
+ get
+ {
+ return this._frequency;
+ }
+ set
+ {
+ this._frequency = value;
+ this.SetDeviceFrequency();
+ }
+ }
+
+ public float UsableSpectrumRatio
+ {
+ get
+ {
+ return AirspyIO._aliasFreeRatio;
+ }
+ }
+
+ public UserControl Gui
+ {
+ get
+ {
+ return this._gui;
+ }
+ }
+
+ public bool IsDeviceHung
+ {
+ get
+ {
+ if (this._airspyDevice != null && this._airspyDevice.IsHung)
+ {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public int Offset
+ {
+ get
+ {
+ return -1582;
+ }
+ }
+
+ public event EventHandler SampleRateChanged;
+
+ public AirspyIO()
+ {
+ this._gui = new ControllerPanel(this);
+ }
+
+ ~AirspyIO()
+ {
+ this.Dispose();
+ }
+
+ public void Dispose()
+ {
+ if (!this._disposed)
+ {
+ this._disposed = true;
+ this.Close();
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ public void Open()
+ {
+ this._airspyDevice = new AirspyDevice(false);
+ this._airspyDevice.Frequency = (uint)this._frequency;
+ this._gui.Device = this._airspyDevice;
+ this._airspyDevice.ComplexSamplesAvailable += this.AirSpyDevice_SamplesAvailable;
+ this._airspyDevice.SampleRateChanged += this.AirSpyDevice_SampleRateChanged;
+ this._gui.RefreshTimerEnabled = true;
+ }
+
+ public void Close()
+ {
+ if (this._airspyDevice != null)
+ {
+ this._gui.RefreshTimerEnabled = false;
+ this._gui.SaveSettings();
+ this._gui.Device = null;
+ this._airspyDevice.ComplexSamplesAvailable -= this.AirSpyDevice_SamplesAvailable;
+ this._airspyDevice.SampleRateChanged -= this.AirSpyDevice_SampleRateChanged;
+ this._airspyDevice.Dispose();
+ this._airspyDevice = null;
+ }
+ }
+
+ public unsafe void Start(SamplesAvailableDelegate callback)
+ {
+ this._callback = callback;
+ try
+ {
+ if (this._airspyDevice == null)
+ {
+ this.Open();
+ }
+ this._airspyDevice.Start();
+ this.SetDeviceFrequency();
+ }
+ catch
+ {
+ this.Close();
+ throw;
+ }
+ }
+
+ public void Stop()
+ {
+ if (this._airspyDevice != null)
+ {
+ this._airspyDevice.Stop();
+ }
+ }
+
+ public void SetControl(object control)
+ {
+ this._control = (ISharpControl)control;
+ }
+
+ private void SetDeviceFrequency()
+ {
+ if (this._airspyDevice != null)
+ {
+ this._airspyDevice.Frequency = (uint)this._frequency;
+ }
+ }
+
+ private unsafe void AirSpyDevice_SamplesAvailable(object sender, ComplexSamplesEventArgs e)
+ {
+ this._callback(this, e.Buffer, e.Length);
+ }
+
+ private void AirSpyDevice_SampleRateChanged(object sender, EventArgs e)
+ {
+ EventHandler evt = this.SampleRateChanged;
+ this._gui.BeginInvoke((Action)delegate
+ {
+ if (evt != null)
+ {
+ evt(this, EventArgs.Empty);
+ }
+ });
+ }
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.Airspy/ControllerPanel.cs b/SDRSharp/SDRSharp.FrontEnds.Airspy/ControllerPanel.cs
new file mode 100644
index 0000000..411a734
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.Airspy/ControllerPanel.cs
@@ -0,0 +1,1520 @@
+using SDRSharp.Common;
+using SDRSharp.Radio;
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.Globalization;
+using System.Threading;
+using System.Windows.Forms;
+
+namespace SDRSharp.FrontEnds.Airspy
+{
+ public class ControllerPanel : UserControl
+ {
+ private AirspyDevice _device;
+
+ private AirspyGainMode _gainMode;
+
+ private AirspyIO _owner;
+
+ private uint _sampleRate;
+
+ private int _baseHeight;
+
+ private bool _debug;
+
+ private IContainer components;
+
+ private TrackBar lnaTrackBar;
+
+ private Label label2;
+
+ private CheckBox lnaAgcCheckBox;
+
+ private Label lnaGainLabel;
+
+ private Label vgaGainLabel;
+
+ private Label label3;
+
+ private TrackBar vgaTrackBar;
+
+ private Label mixerGainLabel;
+
+ private CheckBox mixerAgcCheckBox;
+
+ private Label label6;
+
+ private TrackBar mixerTrackBar;
+
+ private Label label8;
+
+ private ComboBox decimationComboBox;
+
+ private ComboBox sampleRateComboBox;
+
+ private Label label9;
+
+ private Label label10;
+
+ private Label displayBandwidthRateLabel;
+
+ private TableLayoutPanel mainTableLayoutPanel;
+
+ private Panel debugPanel;
+
+ private Label label4;
+
+ private Button writeClockButton;
+
+ private Label label1;
+
+ private Button readClockButton;
+
+ private TextBox tunerRegTextBox;
+
+ private TextBox clockValTextBox;
+
+ private Label label5;
+
+ private TextBox clockRegTextBox;
+
+ private TextBox tunerValTextBox;
+
+ private Label label7;
+
+ private Button readTunerButton;
+
+ private Button writeTunerButton;
+
+ private Label label11;
+
+ private NumericUpDown lpfNumericUpDown;
+
+ private Button writeGPIOButton;
+
+ private Button readGPIOButton;
+
+ private TextBox gpioValueTextBox;
+
+ private TextBox gpioAddressTextBox;
+
+ private Label label12;
+
+ private Button dumpButton;
+
+ private Button readMemoryButton;
+
+ private TextBox memoryValueTextBox;
+
+ private TextBox memoryAddressTextBox;
+
+ private Label label13;
+
+ private Label label14;
+
+ private CheckBox biasTeeCheckBox;
+
+ private CheckBox usePackingCheckBox;
+
+ private CheckBox spyVerterCheckBox;
+
+ private Label label15;
+
+ private Label label16;
+
+ private NumericUpDown spyverterPPMnumericUpDown;
+
+ private System.Windows.Forms.Timer refreshTimer;
+
+ private TableLayoutPanel advancedTableLayoutPanel;
+
+ private RadioButton freeRadioButton;
+
+ private RadioButton linearityRadioButton;
+
+ private RadioButton sensitivityRadioButton;
+
+ private TableLayoutPanel simplifiedTableLayoutPanel;
+
+ private TrackBar simplifiedTrackBar;
+
+ private Label simplifiedGainLabel;
+
+ private Label label18;
+
+ private TableLayoutPanel tableLayoutPanel2;
+
+ private CheckBox trackingFilterCheckBox;
+
+ private Button calibrateButton;
+
+ private NumericUpDown hpfNumericUpDown;
+
+ private CheckBox dynamicRangeCheckBox;
+
+ public AirspyDevice Device
+ {
+ get
+ {
+ return this._device;
+ }
+ set
+ {
+ this._device = value;
+ if (this._device != null)
+ {
+ this.InitDevice();
+ }
+ else
+ {
+ this.sampleRateComboBox.Items.Clear();
+ this.UpdateActualSampleRate();
+ }
+ }
+ }
+
+ public bool RefreshTimerEnabled
+ {
+ get
+ {
+ return this.refreshTimer.Enabled;
+ }
+ set
+ {
+ this.refreshTimer.Enabled = value;
+ }
+ }
+
+ public ControllerPanel(AirspyIO owner)
+ {
+ this._owner = owner;
+ this.InitializeComponent();
+ this.vgaTrackBar.Value = Utils.GetIntSetting("airspy.vga", 2);
+ this.mixerAgcCheckBox.Checked = Utils.GetBooleanSetting("airspy.mixerAgc");
+ this.mixerTrackBar.Value = Utils.GetIntSetting("airspy.mixer", 0);
+ this.lnaAgcCheckBox.Checked = Utils.GetBooleanSetting("airspy.lnaAgc");
+ this.lnaTrackBar.Value = Utils.GetIntSetting("airspy.lna", 0);
+ this.simplifiedTrackBar.Value = Utils.GetIntSetting("airspy.simplifiedGain", 0);
+ this.decimationComboBox.SelectedIndex = Utils.GetIntSetting("airspy.decimation", 0);
+ this.trackingFilterCheckBox.Checked = Utils.GetBooleanSetting("airspy.trackingFilterEnabled");
+ this.biasTeeCheckBox.Checked = Utils.GetBooleanSetting("airspy.biasTeeEnabled");
+ this.spyVerterCheckBox.Checked = Utils.GetBooleanSetting("airspy.spyVerterEnabled");
+ this.spyverterPPMnumericUpDown.Value = (decimal)Utils.GetDoubleSetting("airspy.spyVerterPPM", 0.0);
+ this._sampleRate = (uint)Utils.GetIntSetting("airspy.sampleRate", 0);
+ this.usePackingCheckBox.Checked = Utils.GetBooleanSetting("airspy.usePacking");
+ this.dynamicRangeCheckBox.Checked = Utils.GetBooleanSetting("airspy.useDynamicRangeEnhancements", true);
+ this._debug = (Utils.GetIntSetting("airspy.debug", 0) != 0);
+ base.Height -= this.simplifiedTableLayoutPanel.Height;
+ base.Height -= this.advancedTableLayoutPanel.Height;
+ if (this._debug)
+ {
+ this.sampleRateComboBox.DropDownStyle = ComboBoxStyle.DropDown;
+ this.sampleRateComboBox.SelectedValueChanged += this.SampleRate_Changed;
+ this.sampleRateComboBox.KeyDown += this.SampleRateComboBox_KeyDown;
+ }
+ else
+ {
+ this.debugPanel.Visible = false;
+ base.Height -= this.debugPanel.Height;
+ this.sampleRateComboBox.SelectedIndexChanged += this.SampleRate_Changed;
+ }
+ this._baseHeight = base.Height;
+ this._gainMode = (AirspyGainMode)Utils.GetIntSetting("airspy.gainMode", 0);
+ switch (this._gainMode)
+ {
+ case AirspyGainMode.Custom:
+ this.freeRadioButton.Checked = true;
+ break;
+ case AirspyGainMode.Linearity:
+ this.linearityRadioButton.Checked = true;
+ break;
+ case AirspyGainMode.Sensitivity:
+ this.sensitivityRadioButton.Checked = true;
+ break;
+ }
+ this.gainModeRadioButton_CheckedChanged(null, null);
+ }
+
+ private void InitDevice()
+ {
+ this.sampleRateComboBox.Items.Clear();
+ uint[] supportedSampleRates = this._device.SupportedSampleRates;
+ foreach (uint sampleRate in supportedSampleRates)
+ {
+ this.sampleRateComboBox.Items.Add(ControllerPanel.SampleRateToString(sampleRate) + "SPS");
+ }
+ if (this._sampleRate == 0)
+ {
+ this._sampleRate = this._device.SupportedSampleRates[0];
+ }
+ string text = ControllerPanel.SampleRateToString(this._sampleRate) + "SPS";
+ if (this._debug)
+ {
+ this.sampleRateComboBox.Text = text;
+ }
+ else
+ {
+ int num = this.sampleRateComboBox.Items.IndexOf(text);
+ if (num < 0)
+ {
+ num = 0;
+ }
+ this.sampleRateComboBox.SelectedIndex = num;
+ }
+ this.SampleRate_Changed(null, null);
+ this.trackingFilterCheckBox_CheckedChanged(null, null);
+ this.biasTeeCheckBox_CheckedChanged(null, null);
+ this.spyVerterCheckBox_CheckedChanged(null, null);
+ this.spyverterPPMnumericUpDown_ValueChanged(null, null);
+ this.usePackingCheckBox_CheckedChanged(null, null);
+ this.dynamicRangeCheckBox_CheckedChanged(null, null);
+ this.InitGains();
+ }
+
+ private void InitGains()
+ {
+ if (this._device != null)
+ {
+ this._device.GainMode = this._gainMode;
+ }
+ switch (this._gainMode)
+ {
+ case AirspyGainMode.Custom:
+ this.vgaTrackBar_Scroll(null, null);
+ this.mixerAgcCheckBox_CheckedChanged(null, null);
+ this.mixerTrackBar_Scroll(null, null);
+ this.lnaAgcCheckBox_CheckedChanged(null, null);
+ this.lnaTrackBar_Scroll(null, null);
+ break;
+ case AirspyGainMode.Linearity:
+ case AirspyGainMode.Sensitivity:
+ this.simplifiedTrackBar_Scroll(null, null);
+ break;
+ }
+ }
+
+ public void SaveSettings()
+ {
+ Utils.SaveSetting("airspy.vga", this.vgaTrackBar.Value);
+ Utils.SaveSetting("airspy.mixerAgc", this.mixerAgcCheckBox.Checked);
+ Utils.SaveSetting("airspy.mixer", this.mixerTrackBar.Value);
+ Utils.SaveSetting("airspy.lnaAgc", this.lnaAgcCheckBox.Checked);
+ Utils.SaveSetting("airspy.lna", this.lnaTrackBar.Value);
+ Utils.SaveSetting("airspy.simplifiedGain", this.simplifiedTrackBar.Value);
+ Utils.SaveSetting("airspy.sampleRate", this._sampleRate);
+ Utils.SaveSetting("airspy.decimation", this.decimationComboBox.SelectedIndex);
+ Utils.SaveSetting("airspy.trackingFilterEnabled", this.trackingFilterCheckBox.Checked);
+ Utils.SaveSetting("airspy.biasTeeEnabled", this.biasTeeCheckBox.Checked);
+ Utils.SaveSetting("airspy.spyVerterEnabled", this.spyVerterCheckBox.Checked);
+ Utils.SaveSetting("airspy.spyVerterPPM", this.spyverterPPMnumericUpDown.Value);
+ Utils.SaveSetting("airspy.usePacking", this.usePackingCheckBox.Checked);
+ Utils.SaveSetting("airspy.gainMode", (int)this._gainMode);
+ Utils.SaveSetting("airspy.useDynamicRangeEnhancements", this.dynamicRangeCheckBox.Checked);
+ }
+
+ private void vgaTrackBar_Scroll(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ this._device.VgaGain = (byte)this.vgaTrackBar.Value;
+ this.vgaGainLabel.Text = this.vgaTrackBar.Value.ToString();
+ }
+ }
+
+ private void mixerAgcCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ this._device.MixerGainAuto = this.mixerAgcCheckBox.Checked;
+ this.mixerTrackBar.Enabled = !this.mixerAgcCheckBox.Checked;
+ this.mixerGainLabel.Visible = !this.mixerAgcCheckBox.Checked;
+ }
+ }
+
+ private void mixerTrackBar_Scroll(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ this._device.MixerGain = (byte)this.mixerTrackBar.Value;
+ this.mixerGainLabel.Text = this.mixerTrackBar.Value.ToString();
+ }
+ }
+
+ private void lnaAgcCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ this._device.LnaGainAuto = this.lnaAgcCheckBox.Checked;
+ this.lnaTrackBar.Enabled = !this.lnaAgcCheckBox.Checked;
+ this.lnaGainLabel.Visible = !this.lnaAgcCheckBox.Checked;
+ }
+ }
+
+ private void lnaTrackBar_Scroll(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ this._device.LnaGain = (byte)this.lnaTrackBar.Value;
+ this.lnaGainLabel.Text = this.lnaTrackBar.Value.ToString();
+ }
+ }
+
+ private void SampleRate_Changed(object sender, EventArgs e)
+ {
+ try
+ {
+ this.UpdateActualSampleRate();
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
+ }
+ }
+
+ private void SampleRateComboBox_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.KeyCode == Keys.Return)
+ {
+ this.SampleRate_Changed(sender, e);
+ }
+ }
+
+ private void UpdateActualSampleRate()
+ {
+ if (this._device != null)
+ {
+ string[] array = this.sampleRateComboBox.Text.Split(' ');
+ this._sampleRate = (uint)(float.Parse((array == null || array.Length == 0) ? this.sampleRateComboBox.Text : array[0], CultureInfo.InvariantCulture) * 1000000f);
+ this._device.SampleRate = this._sampleRate;
+ this._device.DecimationStages = this.decimationComboBox.SelectedIndex;
+ this.displayBandwidthRateLabel.Text = ControllerPanel.SampleRateToString((uint)((float)(double)this._device.DecimatedSampleRate * this._owner.UsableSpectrumRatio)) + "Hz";
+ }
+ else
+ {
+ this.displayBandwidthRateLabel.Text = "Unknown";
+ }
+ }
+
+ private static string SampleRateToString(uint sampleRate)
+ {
+ double num;
+ if ((double)sampleRate >= 1000000.0)
+ {
+ num = (double)sampleRate * 1E-06;
+ return num.ToString(CultureInfo.InvariantCulture) + " M";
+ }
+ if ((double)sampleRate >= 1000.0)
+ {
+ num = (double)sampleRate * 0.001;
+ return num.ToString(CultureInfo.InvariantCulture) + " k";
+ }
+ return sampleRate.ToString(CultureInfo.InvariantCulture) + " ";
+ }
+
+ private void biasTeeCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ this._device.BiasTeeEnabled = this.biasTeeCheckBox.Checked;
+ }
+ }
+
+ private void spyVerterCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ this._device.SpyVerterEnabled = this.spyVerterCheckBox.Checked;
+ }
+ this.spyverterPPMnumericUpDown.Enabled = this.spyVerterCheckBox.Checked;
+ }
+
+ private void spyverterPPMnumericUpDown_ValueChanged(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ this._device.SpyVerterPPM = (float)this.spyverterPPMnumericUpDown.Value;
+ }
+ }
+
+ private void usePackingCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ ISharpControl sharpControl = this._owner.SharpControl;
+ if (sharpControl != null && sharpControl.IsPlaying && e != null)
+ {
+ sharpControl.StopRadio();
+ this._device.UsePacking = this.usePackingCheckBox.Checked;
+ sharpControl.StartRadio();
+ }
+ else
+ {
+ this._device.UsePacking = this.usePackingCheckBox.Checked;
+ }
+ }
+ }
+
+ private void refreshTimer_Tick(object sender, EventArgs e)
+ {
+ if (this._owner.IsDeviceHung)
+ {
+ this._owner.SharpControl.StopRadio();
+ }
+ AirspyDevice device = this._device;
+ if (device != null)
+ {
+ this.dynamicRangeCheckBox.Enabled = !device.IsStreaming;
+ }
+ else
+ {
+ this.dynamicRangeCheckBox.Enabled = true;
+ }
+ }
+
+ private void gainModeRadioButton_CheckedChanged(object sender, EventArgs e)
+ {
+ if (this.sensitivityRadioButton.Checked)
+ {
+ this.advancedTableLayoutPanel.Visible = false;
+ this.simplifiedTableLayoutPanel.Visible = true;
+ base.Height = this._baseHeight + this.simplifiedTableLayoutPanel.Height;
+ this._gainMode = AirspyGainMode.Sensitivity;
+ this.InitGains();
+ }
+ else if (this.linearityRadioButton.Checked)
+ {
+ this.advancedTableLayoutPanel.Visible = false;
+ this.simplifiedTableLayoutPanel.Visible = true;
+ base.Height = this._baseHeight + this.simplifiedTableLayoutPanel.Height;
+ this._gainMode = AirspyGainMode.Linearity;
+ this.InitGains();
+ }
+ else if (this.freeRadioButton.Checked)
+ {
+ this.simplifiedTableLayoutPanel.Visible = false;
+ this.advancedTableLayoutPanel.Visible = true;
+ base.Height = this._baseHeight + this.advancedTableLayoutPanel.Height;
+ this._gainMode = AirspyGainMode.Custom;
+ this.InitGains();
+ }
+ }
+
+ private void simplifiedTrackBar_Scroll(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ switch (this._gainMode)
+ {
+ case AirspyGainMode.Sensitivity:
+ this._device.SensitivityGain = (byte)this.simplifiedTrackBar.Value;
+ break;
+ case AirspyGainMode.Linearity:
+ this._device.LinearityGain = (byte)this.simplifiedTrackBar.Value;
+ break;
+ }
+ }
+ this.simplifiedGainLabel.Text = this.simplifiedTrackBar.Value.ToString();
+ }
+
+ private void trackingFilterCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ this._device.BypassTrackingFilter = !this.trackingFilterCheckBox.Checked;
+ }
+ }
+
+ private void dynamicRangeCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ this._device.UseDynamicRangeEnhancements = this.dynamicRangeCheckBox.Checked;
+ }
+ }
+
+ private void readTunerButton_Click(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ try
+ {
+ byte reg = this.ParseHex(this.tunerRegTextBox.Text);
+ byte r820TRegister = this._device.GetR820TRegister(reg);
+ string text = Convert.ToString(r820TRegister, 2);
+ text = text.PadLeft(8, '0');
+ this.tunerValTextBox.Text = text.Insert(4, " ");
+ }
+ catch (FormatException)
+ {
+ MessageBox.Show(this, "The register address is not a valid hex number", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
+ }
+ }
+ }
+
+ private void readClockButton_Click(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ try
+ {
+ byte reg = this.ParseHex(this.clockRegTextBox.Text);
+ byte si5351CRegister = this._device.GetSi5351CRegister(reg);
+ string text = Convert.ToString(si5351CRegister, 2);
+ text = text.PadLeft(8, '0');
+ this.clockValTextBox.Text = text.Insert(4, " ");
+ }
+ catch (FormatException)
+ {
+ MessageBox.Show(this, "The register address is not a valid hex number", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
+ }
+ }
+ }
+
+ private void writeTunerButton_Click(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ try
+ {
+ byte reg = this.ParseHex(this.tunerRegTextBox.Text);
+ byte value = this.ParseBin(this.tunerValTextBox.Text);
+ this._device.SetR820TRegister(reg, value);
+ }
+ catch (FormatException)
+ {
+ MessageBox.Show(this, "The register address is not a valid hex number", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
+ }
+ }
+ }
+
+ private void writeClockButton_Click(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ try
+ {
+ byte reg = this.ParseHex(this.clockRegTextBox.Text);
+ byte value = this.ParseBin(this.clockValTextBox.Text);
+ this._device.SetSi5351CRegister(reg, value);
+ }
+ catch (FormatException)
+ {
+ MessageBox.Show(this, "The register address is not a valid hex number", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
+ }
+ }
+ }
+
+ private byte ParseHex(string s)
+ {
+ s = s.Replace("0X", string.Empty).Replace("0x", string.Empty);
+ return byte.Parse(s, NumberStyles.HexNumber);
+ }
+
+ private byte ParseBin(string s)
+ {
+ s = s.Replace(" ", string.Empty);
+ return Convert.ToByte(s, 2);
+ }
+
+ private void tunerRegTextBox_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.KeyCode == Keys.Return)
+ {
+ this.readTunerButton_Click(sender, e);
+ }
+ }
+
+ private void clockRegTextBox_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.KeyCode == Keys.Return)
+ {
+ this.readClockButton_Click(sender, e);
+ }
+ }
+
+ private void tunerValTextBox_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.KeyCode == Keys.Return)
+ {
+ this.writeTunerButton_Click(sender, e);
+ }
+ }
+
+ private void clockValTextBox_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.KeyCode == Keys.Return)
+ {
+ this.writeClockButton_Click(sender, e);
+ }
+ }
+
+ private void gpioAddressTextBox_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.KeyCode == Keys.Return)
+ {
+ this.readGPIOButton_Click(sender, e);
+ }
+ }
+
+ private void gpioValueTextBox_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.KeyCode == Keys.Return)
+ {
+ this.writeGPIOButton_Click(sender, e);
+ }
+ }
+
+ private void readGPIOButton_Click(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ try
+ {
+ string[] array = this.gpioAddressTextBox.Text.Split(':');
+ int port = int.Parse(array[0]);
+ int pin = int.Parse(array[1]);
+ bool gPIO = this._device.GetGPIO((airspy_gpio_port_t)port, (airspy_gpio_pin_t)pin);
+ this.gpioValueTextBox.Text = (gPIO ? "1" : "0");
+ }
+ catch (FormatException)
+ {
+ MessageBox.Show(this, "The register address is not in the \"port:pin\" format", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
+ }
+ }
+ }
+
+ private void writeGPIOButton_Click(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ try
+ {
+ string[] array = this.gpioAddressTextBox.Text.Split(':');
+ int port = int.Parse(array[0]);
+ int pin = int.Parse(array[1]);
+ int num = int.Parse(this.gpioValueTextBox.Text);
+ this._device.SetGPIO((airspy_gpio_port_t)port, (airspy_gpio_pin_t)pin, num != 0);
+ }
+ catch (FormatException)
+ {
+ MessageBox.Show(this, "The register address is not in the \"port:pin\" format", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
+ }
+ }
+ }
+
+ private void dumpButton_Click(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ this.dumpButton.Enabled = false;
+ WaitCallback callBack = delegate
+ {
+ this._device.Dump("");
+ base.BeginInvoke((Action)delegate
+ {
+ this.dumpButton.Enabled = true;
+ });
+ };
+ ThreadPool.QueueUserWorkItem(callBack);
+ }
+ }
+
+ private void readMemoryButton_Click(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ try
+ {
+ uint address = uint.Parse(this.memoryAddressTextBox.Text.Remove(0, 2), NumberStyles.HexNumber);
+ uint memory = this._device.GetMemory(address);
+ this.memoryValueTextBox.Text = "0x" + memory.ToString("X").PadLeft(8, '0');
+ }
+ catch (FormatException)
+ {
+ MessageBox.Show(this, "The memory address is not in the hex format", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
+ }
+ }
+ }
+
+ private void memoryAddressTextBox_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.KeyCode == Keys.Return)
+ {
+ this.readMemoryButton_Click(sender, e);
+ }
+ }
+
+ private void calibrateButton_Click(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ this._device.CalibrateIF();
+ }
+ }
+
+ private void hpfNumericUpDown_ValueChanged(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ this._device.SetAnalogIFFilters((byte)this.lpfNumericUpDown.Value, (byte)this.hpfNumericUpDown.Value);
+ }
+ }
+
+ private void lpfNumericUpDown_ValueChanged(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ this._device.SetAnalogIFFilters((byte)this.lpfNumericUpDown.Value, (byte)this.hpfNumericUpDown.Value);
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && this.components != null)
+ {
+ this.components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ private void InitializeComponent()
+ {
+ this.components = new Container();
+ this.lnaTrackBar = new TrackBar();
+ this.label2 = new Label();
+ this.lnaAgcCheckBox = new CheckBox();
+ this.lnaGainLabel = new Label();
+ this.vgaGainLabel = new Label();
+ this.label3 = new Label();
+ this.vgaTrackBar = new TrackBar();
+ this.mixerGainLabel = new Label();
+ this.mixerAgcCheckBox = new CheckBox();
+ this.label6 = new Label();
+ this.mixerTrackBar = new TrackBar();
+ this.label8 = new Label();
+ this.decimationComboBox = new ComboBox();
+ this.sampleRateComboBox = new ComboBox();
+ this.label9 = new Label();
+ this.label10 = new Label();
+ this.displayBandwidthRateLabel = new Label();
+ this.mainTableLayoutPanel = new TableLayoutPanel();
+ this.debugPanel = new Panel();
+ this.hpfNumericUpDown = new NumericUpDown();
+ this.calibrateButton = new Button();
+ this.usePackingCheckBox = new CheckBox();
+ this.readMemoryButton = new Button();
+ this.memoryValueTextBox = new TextBox();
+ this.memoryAddressTextBox = new TextBox();
+ this.label13 = new Label();
+ this.dumpButton = new Button();
+ this.writeGPIOButton = new Button();
+ this.readGPIOButton = new Button();
+ this.gpioValueTextBox = new TextBox();
+ this.gpioAddressTextBox = new TextBox();
+ this.label12 = new Label();
+ this.label11 = new Label();
+ this.lpfNumericUpDown = new NumericUpDown();
+ this.label4 = new Label();
+ this.writeClockButton = new Button();
+ this.label1 = new Label();
+ this.readClockButton = new Button();
+ this.tunerRegTextBox = new TextBox();
+ this.clockValTextBox = new TextBox();
+ this.label5 = new Label();
+ this.clockRegTextBox = new TextBox();
+ this.tunerValTextBox = new TextBox();
+ this.label7 = new Label();
+ this.readTunerButton = new Button();
+ this.writeTunerButton = new Button();
+ this.biasTeeCheckBox = new CheckBox();
+ this.label14 = new Label();
+ this.advancedTableLayoutPanel = new TableLayoutPanel();
+ this.simplifiedTableLayoutPanel = new TableLayoutPanel();
+ this.simplifiedTrackBar = new TrackBar();
+ this.simplifiedGainLabel = new Label();
+ this.label18 = new Label();
+ this.spyverterPPMnumericUpDown = new NumericUpDown();
+ this.label16 = new Label();
+ this.tableLayoutPanel2 = new TableLayoutPanel();
+ this.freeRadioButton = new RadioButton();
+ this.sensitivityRadioButton = new RadioButton();
+ this.linearityRadioButton = new RadioButton();
+ this.trackingFilterCheckBox = new CheckBox();
+ this.dynamicRangeCheckBox = new CheckBox();
+ this.label15 = new Label();
+ this.spyVerterCheckBox = new CheckBox();
+ this.refreshTimer = new System.Windows.Forms.Timer(this.components);
+ ((ISupportInitialize)this.lnaTrackBar).BeginInit();
+ ((ISupportInitialize)this.vgaTrackBar).BeginInit();
+ ((ISupportInitialize)this.mixerTrackBar).BeginInit();
+ this.mainTableLayoutPanel.SuspendLayout();
+ this.debugPanel.SuspendLayout();
+ ((ISupportInitialize)this.hpfNumericUpDown).BeginInit();
+ ((ISupportInitialize)this.lpfNumericUpDown).BeginInit();
+ this.advancedTableLayoutPanel.SuspendLayout();
+ this.simplifiedTableLayoutPanel.SuspendLayout();
+ ((ISupportInitialize)this.simplifiedTrackBar).BeginInit();
+ ((ISupportInitialize)this.spyverterPPMnumericUpDown).BeginInit();
+ this.tableLayoutPanel2.SuspendLayout();
+ base.SuspendLayout();
+ this.lnaTrackBar.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.advancedTableLayoutPanel.SetColumnSpan(this.lnaTrackBar, 3);
+ this.lnaTrackBar.Location = new Point(0, 170);
+ this.lnaTrackBar.Margin = new Padding(0);
+ this.lnaTrackBar.Maximum = 15;
+ this.lnaTrackBar.Name = "lnaTrackBar";
+ this.lnaTrackBar.Size = new Size(202, 45);
+ this.lnaTrackBar.TabIndex = 4;
+ this.lnaTrackBar.Scroll += this.lnaTrackBar_Scroll;
+ this.label2.Anchor = AnchorStyles.Left;
+ this.label2.AutoSize = true;
+ this.label2.Location = new Point(0, 147);
+ this.label2.Margin = new Padding(0);
+ this.label2.Name = "label2";
+ this.label2.Size = new Size(53, 13);
+ this.label2.TabIndex = 22;
+ this.label2.Text = "LNA Gain";
+ this.lnaAgcCheckBox.Anchor = AnchorStyles.Left;
+ this.lnaAgcCheckBox.AutoSize = true;
+ this.lnaAgcCheckBox.Location = new Point(60, 145);
+ this.lnaAgcCheckBox.Name = "lnaAgcCheckBox";
+ this.lnaAgcCheckBox.Size = new Size(48, 17);
+ this.lnaAgcCheckBox.TabIndex = 3;
+ this.lnaAgcCheckBox.Text = "Auto";
+ this.lnaAgcCheckBox.UseVisualStyleBackColor = true;
+ this.lnaAgcCheckBox.CheckedChanged += this.lnaAgcCheckBox_CheckedChanged;
+ this.lnaGainLabel.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.lnaGainLabel.Location = new Point(111, 147);
+ this.lnaGainLabel.Margin = new Padding(0);
+ this.lnaGainLabel.Name = "lnaGainLabel";
+ this.lnaGainLabel.Size = new Size(91, 13);
+ this.lnaGainLabel.TabIndex = 26;
+ this.lnaGainLabel.Text = "0";
+ this.lnaGainLabel.TextAlign = ContentAlignment.MiddleRight;
+ this.vgaGainLabel.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.vgaGainLabel.Location = new Point(111, 0);
+ this.vgaGainLabel.Margin = new Padding(0);
+ this.vgaGainLabel.Name = "vgaGainLabel";
+ this.vgaGainLabel.Size = new Size(91, 13);
+ this.vgaGainLabel.TabIndex = 32;
+ this.vgaGainLabel.Text = "0";
+ this.vgaGainLabel.TextAlign = ContentAlignment.MiddleRight;
+ this.label3.Anchor = AnchorStyles.Left;
+ this.label3.AutoSize = true;
+ this.label3.Location = new Point(0, 0);
+ this.label3.Margin = new Padding(0);
+ this.label3.Name = "label3";
+ this.label3.Size = new Size(41, 13);
+ this.label3.TabIndex = 31;
+ this.label3.Text = "IF Gain";
+ this.vgaTrackBar.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.advancedTableLayoutPanel.SetColumnSpan(this.vgaTrackBar, 3);
+ this.vgaTrackBar.Location = new Point(0, 17);
+ this.vgaTrackBar.Margin = new Padding(0);
+ this.vgaTrackBar.Maximum = 15;
+ this.vgaTrackBar.Name = "vgaTrackBar";
+ this.vgaTrackBar.Size = new Size(202, 45);
+ this.vgaTrackBar.TabIndex = 0;
+ this.vgaTrackBar.Scroll += this.vgaTrackBar_Scroll;
+ this.mixerGainLabel.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.mixerGainLabel.Location = new Point(111, 71);
+ this.mixerGainLabel.Margin = new Padding(0);
+ this.mixerGainLabel.Name = "mixerGainLabel";
+ this.mixerGainLabel.Size = new Size(91, 13);
+ this.mixerGainLabel.TabIndex = 36;
+ this.mixerGainLabel.Text = "0";
+ this.mixerGainLabel.TextAlign = ContentAlignment.MiddleRight;
+ this.mixerAgcCheckBox.Anchor = AnchorStyles.Left;
+ this.mixerAgcCheckBox.AutoSize = true;
+ this.mixerAgcCheckBox.Location = new Point(60, 69);
+ this.mixerAgcCheckBox.Name = "mixerAgcCheckBox";
+ this.mixerAgcCheckBox.Size = new Size(48, 17);
+ this.mixerAgcCheckBox.TabIndex = 1;
+ this.mixerAgcCheckBox.Text = "Auto";
+ this.mixerAgcCheckBox.UseVisualStyleBackColor = true;
+ this.mixerAgcCheckBox.CheckedChanged += this.mixerAgcCheckBox_CheckedChanged;
+ this.label6.Anchor = AnchorStyles.Left;
+ this.label6.AutoSize = true;
+ this.label6.Location = new Point(0, 71);
+ this.label6.Margin = new Padding(0);
+ this.label6.Name = "label6";
+ this.label6.Size = new Size(57, 13);
+ this.label6.TabIndex = 35;
+ this.label6.Text = "Mixer Gain";
+ this.mixerTrackBar.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.advancedTableLayoutPanel.SetColumnSpan(this.mixerTrackBar, 3);
+ this.mixerTrackBar.Location = new Point(0, 93);
+ this.mixerTrackBar.Margin = new Padding(0);
+ this.mixerTrackBar.Maximum = 15;
+ this.mixerTrackBar.Name = "mixerTrackBar";
+ this.mixerTrackBar.Size = new Size(202, 45);
+ this.mixerTrackBar.TabIndex = 2;
+ this.mixerTrackBar.Scroll += this.mixerTrackBar_Scroll;
+ this.label8.Anchor = AnchorStyles.Left;
+ this.label8.AutoSize = true;
+ this.label8.Location = new Point(0, 352);
+ this.label8.Margin = new Padding(0);
+ this.label8.Name = "label8";
+ this.label8.Size = new Size(60, 13);
+ this.label8.TabIndex = 46;
+ this.label8.Text = "Decimation";
+ this.decimationComboBox.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.mainTableLayoutPanel.SetColumnSpan(this.decimationComboBox, 3);
+ this.decimationComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
+ this.decimationComboBox.FormattingEnabled = true;
+ this.decimationComboBox.Items.AddRange(new object[7]
+ {
+ "None",
+ "2",
+ "4",
+ "8",
+ "16",
+ "32",
+ "64"
+ });
+ this.decimationComboBox.Location = new Point(63, 348);
+ this.decimationComboBox.Margin = new Padding(0, 3, 0, 3);
+ this.decimationComboBox.Name = "decimationComboBox";
+ this.decimationComboBox.Size = new Size(139, 21);
+ this.decimationComboBox.TabIndex = 6;
+ this.decimationComboBox.SelectedIndexChanged += this.SampleRate_Changed;
+ this.sampleRateComboBox.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.mainTableLayoutPanel.SetColumnSpan(this.sampleRateComboBox, 3);
+ this.sampleRateComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
+ this.sampleRateComboBox.FormattingEnabled = true;
+ this.sampleRateComboBox.Location = new Point(63, 321);
+ this.sampleRateComboBox.Margin = new Padding(0, 3, 0, 3);
+ this.sampleRateComboBox.Name = "sampleRateComboBox";
+ this.sampleRateComboBox.Size = new Size(139, 21);
+ this.sampleRateComboBox.TabIndex = 5;
+ this.label9.Anchor = AnchorStyles.Left;
+ this.label9.AutoSize = true;
+ this.label9.Location = new Point(0, 325);
+ this.label9.Margin = new Padding(0);
+ this.label9.Name = "label9";
+ this.label9.Size = new Size(63, 13);
+ this.label9.TabIndex = 48;
+ this.label9.Text = "Sample rate";
+ this.label10.Anchor = AnchorStyles.Left;
+ this.label10.AutoSize = true;
+ this.label10.Location = new Point(0, 377);
+ this.label10.Margin = new Padding(0);
+ this.label10.Name = "label10";
+ this.label10.Size = new Size(41, 13);
+ this.label10.TabIndex = 49;
+ this.label10.Text = "Display";
+ this.displayBandwidthRateLabel.Anchor = AnchorStyles.Left;
+ this.displayBandwidthRateLabel.AutoSize = true;
+ this.mainTableLayoutPanel.SetColumnSpan(this.displayBandwidthRateLabel, 3);
+ this.displayBandwidthRateLabel.Font = new Font("Microsoft Sans Serif", 14f, FontStyle.Bold, GraphicsUnit.Point, 0);
+ this.displayBandwidthRateLabel.Location = new Point(66, 372);
+ this.displayBandwidthRateLabel.Name = "displayBandwidthRateLabel";
+ this.displayBandwidthRateLabel.Size = new Size(80, 24);
+ this.displayBandwidthRateLabel.TabIndex = 50;
+ this.displayBandwidthRateLabel.Text = "10 MHz";
+ this.mainTableLayoutPanel.ColumnCount = 4;
+ this.mainTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
+ this.mainTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
+ this.mainTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
+ this.mainTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
+ this.mainTableLayoutPanel.Controls.Add(this.label10, 0, 5);
+ this.mainTableLayoutPanel.Controls.Add(this.displayBandwidthRateLabel, 1, 5);
+ this.mainTableLayoutPanel.Controls.Add(this.label8, 0, 4);
+ this.mainTableLayoutPanel.Controls.Add(this.label9, 0, 3);
+ this.mainTableLayoutPanel.Controls.Add(this.sampleRateComboBox, 1, 3);
+ this.mainTableLayoutPanel.Controls.Add(this.decimationComboBox, 1, 4);
+ this.mainTableLayoutPanel.Controls.Add(this.debugPanel, 0, 10);
+ this.mainTableLayoutPanel.Controls.Add(this.biasTeeCheckBox, 1, 6);
+ this.mainTableLayoutPanel.Controls.Add(this.label14, 0, 6);
+ this.mainTableLayoutPanel.Controls.Add(this.advancedTableLayoutPanel, 0, 2);
+ this.mainTableLayoutPanel.Controls.Add(this.simplifiedTableLayoutPanel, 0, 1);
+ this.mainTableLayoutPanel.Controls.Add(this.spyverterPPMnumericUpDown, 3, 9);
+ this.mainTableLayoutPanel.Controls.Add(this.label16, 2, 9);
+ this.mainTableLayoutPanel.Controls.Add(this.tableLayoutPanel2, 0, 0);
+ this.mainTableLayoutPanel.Controls.Add(this.trackingFilterCheckBox, 2, 6);
+ this.mainTableLayoutPanel.Controls.Add(this.dynamicRangeCheckBox, 2, 7);
+ this.mainTableLayoutPanel.Controls.Add(this.label15, 0, 7);
+ this.mainTableLayoutPanel.Controls.Add(this.spyVerterCheckBox, 1, 7);
+ this.mainTableLayoutPanel.Dock = DockStyle.Fill;
+ this.mainTableLayoutPanel.Location = new Point(0, 0);
+ this.mainTableLayoutPanel.Margin = new Padding(0);
+ this.mainTableLayoutPanel.Name = "mainTableLayoutPanel";
+ this.mainTableLayoutPanel.RowCount = 11;
+ this.mainTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.mainTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.mainTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.mainTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.mainTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.mainTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.mainTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.mainTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.mainTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.mainTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.mainTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.mainTableLayoutPanel.Size = new Size(202, 648);
+ this.mainTableLayoutPanel.TabIndex = 51;
+ this.debugPanel.Anchor = AnchorStyles.None;
+ this.mainTableLayoutPanel.SetColumnSpan(this.debugPanel, 4);
+ this.debugPanel.Controls.Add(this.hpfNumericUpDown);
+ this.debugPanel.Controls.Add(this.calibrateButton);
+ this.debugPanel.Controls.Add(this.usePackingCheckBox);
+ this.debugPanel.Controls.Add(this.readMemoryButton);
+ this.debugPanel.Controls.Add(this.memoryValueTextBox);
+ this.debugPanel.Controls.Add(this.memoryAddressTextBox);
+ this.debugPanel.Controls.Add(this.label13);
+ this.debugPanel.Controls.Add(this.dumpButton);
+ this.debugPanel.Controls.Add(this.writeGPIOButton);
+ this.debugPanel.Controls.Add(this.readGPIOButton);
+ this.debugPanel.Controls.Add(this.gpioValueTextBox);
+ this.debugPanel.Controls.Add(this.gpioAddressTextBox);
+ this.debugPanel.Controls.Add(this.label12);
+ this.debugPanel.Controls.Add(this.label11);
+ this.debugPanel.Controls.Add(this.lpfNumericUpDown);
+ this.debugPanel.Controls.Add(this.label4);
+ this.debugPanel.Controls.Add(this.writeClockButton);
+ this.debugPanel.Controls.Add(this.label1);
+ this.debugPanel.Controls.Add(this.readClockButton);
+ this.debugPanel.Controls.Add(this.tunerRegTextBox);
+ this.debugPanel.Controls.Add(this.clockValTextBox);
+ this.debugPanel.Controls.Add(this.label5);
+ this.debugPanel.Controls.Add(this.clockRegTextBox);
+ this.debugPanel.Controls.Add(this.tunerValTextBox);
+ this.debugPanel.Controls.Add(this.label7);
+ this.debugPanel.Controls.Add(this.readTunerButton);
+ this.debugPanel.Controls.Add(this.writeTunerButton);
+ this.debugPanel.Location = new Point(0, 474);
+ this.debugPanel.Margin = new Padding(0);
+ this.debugPanel.Name = "debugPanel";
+ this.debugPanel.Size = new Size(202, 167);
+ this.debugPanel.TabIndex = 7;
+ this.hpfNumericUpDown.Location = new Point(105, 119);
+ this.hpfNumericUpDown.Maximum = new decimal(new int[4]
+ {
+ 15,
+ 0,
+ 0,
+ 0
+ });
+ this.hpfNumericUpDown.Name = "hpfNumericUpDown";
+ this.hpfNumericUpDown.Size = new Size(38, 20);
+ this.hpfNumericUpDown.TabIndex = 60;
+ this.hpfNumericUpDown.ValueChanged += this.hpfNumericUpDown_ValueChanged;
+ this.calibrateButton.Location = new Point(147, 142);
+ this.calibrateButton.Name = "calibrateButton";
+ this.calibrateButton.Size = new Size(47, 22);
+ this.calibrateButton.TabIndex = 59;
+ this.calibrateButton.Text = "Cal";
+ this.calibrateButton.UseVisualStyleBackColor = true;
+ this.calibrateButton.Click += this.calibrateButton_Click;
+ this.usePackingCheckBox.AutoSize = true;
+ this.usePackingCheckBox.Location = new Point(58, 146);
+ this.usePackingCheckBox.Name = "usePackingCheckBox";
+ this.usePackingCheckBox.Size = new Size(87, 17);
+ this.usePackingCheckBox.TabIndex = 58;
+ this.usePackingCheckBox.Text = "Use Packing";
+ this.usePackingCheckBox.TextAlign = ContentAlignment.MiddleRight;
+ this.usePackingCheckBox.UseVisualStyleBackColor = true;
+ this.usePackingCheckBox.CheckedChanged += this.usePackingCheckBox_CheckedChanged;
+ this.readMemoryButton.Location = new Point(147, 94);
+ this.readMemoryButton.Margin = new Padding(2);
+ this.readMemoryButton.Name = "readMemoryButton";
+ this.readMemoryButton.Size = new Size(21, 21);
+ this.readMemoryButton.TabIndex = 56;
+ this.readMemoryButton.Text = "R";
+ this.readMemoryButton.UseVisualStyleBackColor = true;
+ this.readMemoryButton.Click += this.readMemoryButton_Click;
+ this.memoryValueTextBox.Location = new Point(105, 95);
+ this.memoryValueTextBox.Margin = new Padding(2);
+ this.memoryValueTextBox.Name = "memoryValueTextBox";
+ this.memoryValueTextBox.Size = new Size(38, 20);
+ this.memoryValueTextBox.TabIndex = 55;
+ this.memoryAddressTextBox.Location = new Point(58, 95);
+ this.memoryAddressTextBox.Margin = new Padding(2);
+ this.memoryAddressTextBox.Name = "memoryAddressTextBox";
+ this.memoryAddressTextBox.Size = new Size(43, 20);
+ this.memoryAddressTextBox.TabIndex = 54;
+ this.memoryAddressTextBox.Text = "0x100800c0";
+ this.memoryAddressTextBox.KeyDown += this.memoryAddressTextBox_KeyDown;
+ this.label13.AutoSize = true;
+ this.label13.Location = new Point(9, 98);
+ this.label13.Name = "label13";
+ this.label13.Size = new Size(44, 13);
+ this.label13.TabIndex = 57;
+ this.label13.Text = "Memory";
+ this.dumpButton.Location = new Point(147, 119);
+ this.dumpButton.Name = "dumpButton";
+ this.dumpButton.Size = new Size(47, 22);
+ this.dumpButton.TabIndex = 53;
+ this.dumpButton.Text = "Dump";
+ this.dumpButton.UseVisualStyleBackColor = true;
+ this.dumpButton.Click += this.dumpButton_Click;
+ this.writeGPIOButton.Location = new Point(173, 70);
+ this.writeGPIOButton.Margin = new Padding(2);
+ this.writeGPIOButton.Name = "writeGPIOButton";
+ this.writeGPIOButton.Size = new Size(21, 21);
+ this.writeGPIOButton.TabIndex = 51;
+ this.writeGPIOButton.Text = "W";
+ this.writeGPIOButton.UseVisualStyleBackColor = true;
+ this.writeGPIOButton.Click += this.writeGPIOButton_Click;
+ this.readGPIOButton.Location = new Point(147, 70);
+ this.readGPIOButton.Margin = new Padding(2);
+ this.readGPIOButton.Name = "readGPIOButton";
+ this.readGPIOButton.Size = new Size(21, 21);
+ this.readGPIOButton.TabIndex = 50;
+ this.readGPIOButton.Text = "R";
+ this.readGPIOButton.UseVisualStyleBackColor = true;
+ this.readGPIOButton.Click += this.readGPIOButton_Click;
+ this.gpioValueTextBox.Location = new Point(105, 71);
+ this.gpioValueTextBox.Margin = new Padding(2);
+ this.gpioValueTextBox.Name = "gpioValueTextBox";
+ this.gpioValueTextBox.Size = new Size(38, 20);
+ this.gpioValueTextBox.TabIndex = 49;
+ this.gpioValueTextBox.KeyDown += this.gpioValueTextBox_KeyDown;
+ this.gpioAddressTextBox.Location = new Point(58, 71);
+ this.gpioAddressTextBox.Margin = new Padding(2);
+ this.gpioAddressTextBox.Name = "gpioAddressTextBox";
+ this.gpioAddressTextBox.Size = new Size(43, 20);
+ this.gpioAddressTextBox.TabIndex = 48;
+ this.gpioAddressTextBox.Text = "0:12";
+ this.gpioAddressTextBox.KeyDown += this.gpioAddressTextBox_KeyDown;
+ this.label12.AutoSize = true;
+ this.label12.Location = new Point(20, 74);
+ this.label12.Name = "label12";
+ this.label12.Size = new Size(33, 13);
+ this.label12.TabIndex = 52;
+ this.label12.Text = "GPIO";
+ this.label11.AutoSize = true;
+ this.label11.Location = new Point(11, 121);
+ this.label11.Name = "label11";
+ this.label11.Size = new Size(41, 13);
+ this.label11.TabIndex = 47;
+ this.label11.Text = "IF Filter";
+ this.lpfNumericUpDown.Location = new Point(58, 119);
+ this.lpfNumericUpDown.Maximum = new decimal(new int[4]
+ {
+ 63,
+ 0,
+ 0,
+ 0
+ });
+ this.lpfNumericUpDown.Name = "lpfNumericUpDown";
+ this.lpfNumericUpDown.Size = new Size(43, 20);
+ this.lpfNumericUpDown.TabIndex = 46;
+ this.lpfNumericUpDown.Value = new decimal(new int[4]
+ {
+ 55,
+ 0,
+ 0,
+ 0
+ });
+ this.lpfNumericUpDown.ValueChanged += this.lpfNumericUpDown_ValueChanged;
+ this.label4.AutoSize = true;
+ this.label4.Location = new Point(56, 6);
+ this.label4.Name = "label4";
+ this.label4.Size = new Size(45, 13);
+ this.label4.TabIndex = 40;
+ this.label4.Text = "Address";
+ this.writeClockButton.Location = new Point(173, 46);
+ this.writeClockButton.Margin = new Padding(2);
+ this.writeClockButton.Name = "writeClockButton";
+ this.writeClockButton.Size = new Size(21, 21);
+ this.writeClockButton.TabIndex = 14;
+ this.writeClockButton.Text = "W";
+ this.writeClockButton.UseVisualStyleBackColor = true;
+ this.writeClockButton.Click += this.writeClockButton_Click;
+ this.label1.AutoSize = true;
+ this.label1.Location = new Point(7, 26);
+ this.label1.Name = "label1";
+ this.label1.Size = new Size(46, 13);
+ this.label1.TabIndex = 38;
+ this.label1.Text = "R820T2";
+ this.readClockButton.Location = new Point(147, 46);
+ this.readClockButton.Margin = new Padding(2);
+ this.readClockButton.Name = "readClockButton";
+ this.readClockButton.Size = new Size(21, 21);
+ this.readClockButton.TabIndex = 13;
+ this.readClockButton.Text = "R";
+ this.readClockButton.UseVisualStyleBackColor = true;
+ this.readClockButton.Click += this.readClockButton_Click;
+ this.tunerRegTextBox.Location = new Point(58, 23);
+ this.tunerRegTextBox.Margin = new Padding(2);
+ this.tunerRegTextBox.Name = "tunerRegTextBox";
+ this.tunerRegTextBox.Size = new Size(43, 20);
+ this.tunerRegTextBox.TabIndex = 7;
+ this.tunerRegTextBox.Text = "0x0A";
+ this.tunerRegTextBox.KeyDown += this.tunerRegTextBox_KeyDown;
+ this.clockValTextBox.Location = new Point(105, 47);
+ this.clockValTextBox.Margin = new Padding(2);
+ this.clockValTextBox.Name = "clockValTextBox";
+ this.clockValTextBox.Size = new Size(38, 20);
+ this.clockValTextBox.TabIndex = 12;
+ this.clockValTextBox.KeyDown += this.clockValTextBox_KeyDown;
+ this.label5.AutoSize = true;
+ this.label5.Location = new Point(102, 6);
+ this.label5.Name = "label5";
+ this.label5.Size = new Size(34, 13);
+ this.label5.TabIndex = 41;
+ this.label5.Text = "Value";
+ this.clockRegTextBox.Location = new Point(58, 47);
+ this.clockRegTextBox.Margin = new Padding(2);
+ this.clockRegTextBox.Name = "clockRegTextBox";
+ this.clockRegTextBox.Size = new Size(43, 20);
+ this.clockRegTextBox.TabIndex = 11;
+ this.clockRegTextBox.Text = "0x00";
+ this.clockRegTextBox.KeyDown += this.clockRegTextBox_KeyDown;
+ this.tunerValTextBox.Location = new Point(105, 23);
+ this.tunerValTextBox.Margin = new Padding(2);
+ this.tunerValTextBox.Name = "tunerValTextBox";
+ this.tunerValTextBox.Size = new Size(38, 20);
+ this.tunerValTextBox.TabIndex = 8;
+ this.tunerValTextBox.KeyDown += this.tunerValTextBox_KeyDown;
+ this.label7.AutoSize = true;
+ this.label7.Location = new Point(6, 49);
+ this.label7.Name = "label7";
+ this.label7.Size = new Size(47, 13);
+ this.label7.TabIndex = 45;
+ this.label7.Text = "Si5351C";
+ this.readTunerButton.Location = new Point(147, 22);
+ this.readTunerButton.Margin = new Padding(2);
+ this.readTunerButton.Name = "readTunerButton";
+ this.readTunerButton.Size = new Size(21, 21);
+ this.readTunerButton.TabIndex = 9;
+ this.readTunerButton.Text = "R";
+ this.readTunerButton.UseVisualStyleBackColor = true;
+ this.readTunerButton.Click += this.readTunerButton_Click;
+ this.writeTunerButton.Location = new Point(173, 22);
+ this.writeTunerButton.Margin = new Padding(2);
+ this.writeTunerButton.Name = "writeTunerButton";
+ this.writeTunerButton.Size = new Size(21, 21);
+ this.writeTunerButton.TabIndex = 10;
+ this.writeTunerButton.Text = "W";
+ this.writeTunerButton.UseVisualStyleBackColor = true;
+ this.writeTunerButton.Click += this.writeTunerButton_Click;
+ this.biasTeeCheckBox.Anchor = AnchorStyles.Left;
+ this.biasTeeCheckBox.AutoSize = true;
+ this.biasTeeCheckBox.Location = new Point(66, 400);
+ this.biasTeeCheckBox.Name = "biasTeeCheckBox";
+ this.biasTeeCheckBox.Size = new Size(15, 14);
+ this.biasTeeCheckBox.TabIndex = 52;
+ this.biasTeeCheckBox.UseVisualStyleBackColor = true;
+ this.biasTeeCheckBox.CheckedChanged += this.biasTeeCheckBox_CheckedChanged;
+ this.label14.Anchor = AnchorStyles.Left;
+ this.label14.AutoSize = true;
+ this.label14.Location = new Point(0, 401);
+ this.label14.Margin = new Padding(0);
+ this.label14.Name = "label14";
+ this.label14.Size = new Size(49, 13);
+ this.label14.TabIndex = 51;
+ this.label14.Text = "Bias-Tee";
+ this.advancedTableLayoutPanel.ColumnCount = 3;
+ this.mainTableLayoutPanel.SetColumnSpan(this.advancedTableLayoutPanel, 4);
+ this.advancedTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
+ this.advancedTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
+ this.advancedTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
+ this.advancedTableLayoutPanel.Controls.Add(this.vgaTrackBar, 0, 1);
+ this.advancedTableLayoutPanel.Controls.Add(this.label3, 0, 0);
+ this.advancedTableLayoutPanel.Controls.Add(this.vgaGainLabel, 2, 0);
+ this.advancedTableLayoutPanel.Controls.Add(this.label6, 0, 2);
+ this.advancedTableLayoutPanel.Controls.Add(this.mixerAgcCheckBox, 1, 2);
+ this.advancedTableLayoutPanel.Controls.Add(this.mixerGainLabel, 2, 2);
+ this.advancedTableLayoutPanel.Controls.Add(this.lnaTrackBar, 0, 5);
+ this.advancedTableLayoutPanel.Controls.Add(this.lnaAgcCheckBox, 1, 4);
+ this.advancedTableLayoutPanel.Controls.Add(this.lnaGainLabel, 2, 4);
+ this.advancedTableLayoutPanel.Controls.Add(this.label2, 0, 4);
+ this.advancedTableLayoutPanel.Controls.Add(this.mixerTrackBar, 0, 3);
+ this.advancedTableLayoutPanel.Dock = DockStyle.Fill;
+ this.advancedTableLayoutPanel.Location = new Point(0, 98);
+ this.advancedTableLayoutPanel.Margin = new Padding(0);
+ this.advancedTableLayoutPanel.Name = "advancedTableLayoutPanel";
+ this.advancedTableLayoutPanel.RowCount = 6;
+ this.advancedTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.advancedTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 33.33333f));
+ this.advancedTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.advancedTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 33.33333f));
+ this.advancedTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.advancedTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 33.33333f));
+ this.advancedTableLayoutPanel.Size = new Size(202, 220);
+ this.advancedTableLayoutPanel.TabIndex = 58;
+ this.advancedTableLayoutPanel.Visible = false;
+ this.simplifiedTableLayoutPanel.ColumnCount = 2;
+ this.mainTableLayoutPanel.SetColumnSpan(this.simplifiedTableLayoutPanel, 4);
+ this.simplifiedTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
+ this.simplifiedTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
+ this.simplifiedTableLayoutPanel.Controls.Add(this.simplifiedTrackBar, 0, 1);
+ this.simplifiedTableLayoutPanel.Controls.Add(this.simplifiedGainLabel, 1, 0);
+ this.simplifiedTableLayoutPanel.Controls.Add(this.label18, 0, 0);
+ this.simplifiedTableLayoutPanel.Dock = DockStyle.Fill;
+ this.simplifiedTableLayoutPanel.Location = new Point(0, 30);
+ this.simplifiedTableLayoutPanel.Margin = new Padding(0);
+ this.simplifiedTableLayoutPanel.Name = "simplifiedTableLayoutPanel";
+ this.simplifiedTableLayoutPanel.RowCount = 2;
+ this.simplifiedTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.simplifiedTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
+ this.simplifiedTableLayoutPanel.Size = new Size(202, 68);
+ this.simplifiedTableLayoutPanel.TabIndex = 62;
+ this.simplifiedTableLayoutPanel.Visible = false;
+ this.simplifiedTrackBar.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.simplifiedTableLayoutPanel.SetColumnSpan(this.simplifiedTrackBar, 3);
+ this.simplifiedTrackBar.Location = new Point(0, 18);
+ this.simplifiedTrackBar.Margin = new Padding(0);
+ this.simplifiedTrackBar.Maximum = 21;
+ this.simplifiedTrackBar.Name = "simplifiedTrackBar";
+ this.simplifiedTrackBar.Size = new Size(202, 45);
+ this.simplifiedTrackBar.TabIndex = 35;
+ this.simplifiedTrackBar.Scroll += this.simplifiedTrackBar_Scroll;
+ this.simplifiedGainLabel.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.simplifiedGainLabel.Location = new Point(29, 1);
+ this.simplifiedGainLabel.Margin = new Padding(0);
+ this.simplifiedGainLabel.Name = "simplifiedGainLabel";
+ this.simplifiedGainLabel.Size = new Size(173, 10);
+ this.simplifiedGainLabel.TabIndex = 33;
+ this.simplifiedGainLabel.Text = "0";
+ this.simplifiedGainLabel.TextAlign = ContentAlignment.MiddleRight;
+ this.label18.Anchor = AnchorStyles.Left;
+ this.label18.AutoSize = true;
+ this.label18.Location = new Point(0, 0);
+ this.label18.Margin = new Padding(0);
+ this.label18.Name = "label18";
+ this.label18.Size = new Size(29, 13);
+ this.label18.TabIndex = 34;
+ this.label18.Text = "Gain";
+ this.spyverterPPMnumericUpDown.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.spyverterPPMnumericUpDown.DecimalPlaces = 2;
+ this.spyverterPPMnumericUpDown.Increment = new decimal(new int[4]
+ {
+ 5,
+ 0,
+ 0,
+ 131072
+ });
+ this.spyverterPPMnumericUpDown.Location = new Point(117, 445);
+ this.spyverterPPMnumericUpDown.Minimum = new decimal(new int[4]
+ {
+ 100,
+ 0,
+ 0,
+ -2147483648
+ });
+ this.spyverterPPMnumericUpDown.Name = "spyverterPPMnumericUpDown";
+ this.spyverterPPMnumericUpDown.Size = new Size(82, 20);
+ this.spyverterPPMnumericUpDown.TabIndex = 57;
+ this.spyverterPPMnumericUpDown.TextAlign = HorizontalAlignment.Right;
+ this.spyverterPPMnumericUpDown.ValueChanged += this.spyverterPPMnumericUpDown_ValueChanged;
+ this.label16.Anchor = AnchorStyles.Right;
+ this.label16.AutoSize = true;
+ this.label16.Location = new Point(84, 448);
+ this.label16.Margin = new Padding(0);
+ this.label16.Name = "label16";
+ this.label16.Size = new Size(30, 13);
+ this.label16.TabIndex = 56;
+ this.label16.Text = "PPM";
+ this.tableLayoutPanel2.ColumnCount = 3;
+ this.mainTableLayoutPanel.SetColumnSpan(this.tableLayoutPanel2, 4);
+ this.tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle());
+ this.tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle());
+ this.tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle());
+ this.tableLayoutPanel2.Controls.Add(this.freeRadioButton, 2, 0);
+ this.tableLayoutPanel2.Controls.Add(this.sensitivityRadioButton, 0, 0);
+ this.tableLayoutPanel2.Controls.Add(this.linearityRadioButton, 1, 0);
+ this.tableLayoutPanel2.Dock = DockStyle.Fill;
+ this.tableLayoutPanel2.Location = new Point(3, 3);
+ this.tableLayoutPanel2.Name = "tableLayoutPanel2";
+ this.tableLayoutPanel2.RowCount = 1;
+ this.tableLayoutPanel2.RowStyles.Add(new RowStyle());
+ this.tableLayoutPanel2.Size = new Size(196, 24);
+ this.tableLayoutPanel2.TabIndex = 63;
+ this.freeRadioButton.AutoSize = true;
+ this.freeRadioButton.Location = new Point(151, 3);
+ this.freeRadioButton.Name = "freeRadioButton";
+ this.freeRadioButton.Size = new Size(46, 17);
+ this.freeRadioButton.TabIndex = 61;
+ this.freeRadioButton.TabStop = true;
+ this.freeRadioButton.Text = "Free";
+ this.freeRadioButton.UseVisualStyleBackColor = true;
+ this.freeRadioButton.CheckedChanged += this.gainModeRadioButton_CheckedChanged;
+ this.sensitivityRadioButton.AutoSize = true;
+ this.sensitivityRadioButton.Location = new Point(3, 3);
+ this.sensitivityRadioButton.Name = "sensitivityRadioButton";
+ this.sensitivityRadioButton.Size = new Size(72, 17);
+ this.sensitivityRadioButton.TabIndex = 59;
+ this.sensitivityRadioButton.TabStop = true;
+ this.sensitivityRadioButton.Text = "Sensitivity";
+ this.sensitivityRadioButton.UseVisualStyleBackColor = true;
+ this.sensitivityRadioButton.CheckedChanged += this.gainModeRadioButton_CheckedChanged;
+ this.linearityRadioButton.AutoSize = true;
+ this.linearityRadioButton.Location = new Point(81, 3);
+ this.linearityRadioButton.Name = "linearityRadioButton";
+ this.linearityRadioButton.Size = new Size(64, 17);
+ this.linearityRadioButton.TabIndex = 60;
+ this.linearityRadioButton.TabStop = true;
+ this.linearityRadioButton.Text = "Linearity";
+ this.linearityRadioButton.UseVisualStyleBackColor = true;
+ this.linearityRadioButton.CheckedChanged += this.gainModeRadioButton_CheckedChanged;
+ this.trackingFilterCheckBox.Anchor = AnchorStyles.Right;
+ this.trackingFilterCheckBox.AutoSize = true;
+ this.trackingFilterCheckBox.CheckAlign = ContentAlignment.MiddleRight;
+ this.trackingFilterCheckBox.Checked = true;
+ this.trackingFilterCheckBox.CheckState = CheckState.Checked;
+ this.mainTableLayoutPanel.SetColumnSpan(this.trackingFilterCheckBox, 2);
+ this.trackingFilterCheckBox.Location = new Point(106, 399);
+ this.trackingFilterCheckBox.Name = "trackingFilterCheckBox";
+ this.trackingFilterCheckBox.Size = new Size(93, 17);
+ this.trackingFilterCheckBox.TabIndex = 65;
+ this.trackingFilterCheckBox.Text = "Tracking Filter";
+ this.trackingFilterCheckBox.UseVisualStyleBackColor = true;
+ this.trackingFilterCheckBox.CheckedChanged += this.trackingFilterCheckBox_CheckedChanged;
+ this.dynamicRangeCheckBox.Anchor = AnchorStyles.Right;
+ this.dynamicRangeCheckBox.AutoSize = true;
+ this.dynamicRangeCheckBox.CheckAlign = ContentAlignment.MiddleRight;
+ this.mainTableLayoutPanel.SetColumnSpan(this.dynamicRangeCheckBox, 2);
+ this.dynamicRangeCheckBox.Location = new Point(113, 422);
+ this.dynamicRangeCheckBox.Name = "dynamicRangeCheckBox";
+ this.dynamicRangeCheckBox.Size = new Size(86, 17);
+ this.dynamicRangeCheckBox.TabIndex = 66;
+ this.dynamicRangeCheckBox.Text = "Enable HDR";
+ this.dynamicRangeCheckBox.UseVisualStyleBackColor = true;
+ this.dynamicRangeCheckBox.CheckedChanged += this.dynamicRangeCheckBox_CheckedChanged;
+ this.label15.Anchor = AnchorStyles.Left;
+ this.label15.AutoSize = true;
+ this.label15.Location = new Point(0, 424);
+ this.label15.Margin = new Padding(0);
+ this.label15.Name = "label15";
+ this.label15.Size = new Size(53, 13);
+ this.label15.TabIndex = 54;
+ this.label15.Text = "SpyVerter";
+ this.spyVerterCheckBox.Anchor = AnchorStyles.Left;
+ this.spyVerterCheckBox.AutoSize = true;
+ this.spyVerterCheckBox.Location = new Point(66, 423);
+ this.spyVerterCheckBox.Name = "spyVerterCheckBox";
+ this.spyVerterCheckBox.Size = new Size(15, 14);
+ this.spyVerterCheckBox.TabIndex = 53;
+ this.spyVerterCheckBox.UseVisualStyleBackColor = true;
+ this.spyVerterCheckBox.CheckedChanged += this.spyVerterCheckBox_CheckedChanged;
+ this.refreshTimer.Tick += this.refreshTimer_Tick;
+ base.AutoScaleDimensions = new SizeF(6f, 13f);
+ base.AutoScaleMode = AutoScaleMode.Font;
+ base.Controls.Add(this.mainTableLayoutPanel);
+ base.Name = "ControllerPanel";
+ base.Size = new Size(202, 648);
+ ((ISupportInitialize)this.lnaTrackBar).EndInit();
+ ((ISupportInitialize)this.vgaTrackBar).EndInit();
+ ((ISupportInitialize)this.mixerTrackBar).EndInit();
+ this.mainTableLayoutPanel.ResumeLayout(false);
+ this.mainTableLayoutPanel.PerformLayout();
+ this.debugPanel.ResumeLayout(false);
+ this.debugPanel.PerformLayout();
+ ((ISupportInitialize)this.hpfNumericUpDown).EndInit();
+ ((ISupportInitialize)this.lpfNumericUpDown).EndInit();
+ this.advancedTableLayoutPanel.ResumeLayout(false);
+ this.advancedTableLayoutPanel.PerformLayout();
+ this.simplifiedTableLayoutPanel.ResumeLayout(false);
+ this.simplifiedTableLayoutPanel.PerformLayout();
+ ((ISupportInitialize)this.simplifiedTrackBar).EndInit();
+ ((ISupportInitialize)this.spyverterPPMnumericUpDown).EndInit();
+ this.tableLayoutPanel2.ResumeLayout(false);
+ this.tableLayoutPanel2.PerformLayout();
+ base.ResumeLayout(false);
+ }
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.Airspy/ControllerPanel.resx b/SDRSharp/SDRSharp.FrontEnds.Airspy/ControllerPanel.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.Airspy/ControllerPanel.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/SDRSharp/SDRSharp.FrontEnds.Airspy/ConversionFilters.cs b/SDRSharp/SDRSharp.FrontEnds.Airspy/ConversionFilters.cs
new file mode 100644
index 0000000..ca4b668
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.Airspy/ConversionFilters.cs
@@ -0,0 +1,109 @@
+namespace SDRSharp.FrontEnds.Airspy
+{
+ public static class ConversionFilters
+ {
+ private static readonly float[] Kernel_Dec16_110dB = new float[7]
+ {
+ -0.03183508f,
+ 0f,
+ 0.2818315f,
+ 0.5000073f,
+ 0.2818315f,
+ 0f,
+ -0.03183508f
+ };
+
+ private static readonly float[] Kernel_Dec8_100dB = new float[11]
+ {
+ 0.006633401f,
+ 0f,
+ -0.0510355234f,
+ 0f,
+ 0.2944033f,
+ 0.4999975f,
+ 0.2944033f,
+ 0f,
+ -0.0510355234f,
+ 0f,
+ 0.006633401f
+ };
+
+ private static readonly float[] Kernel_Dec4_90dB = new float[15]
+ {
+ -0.0024741888f,
+ 0f,
+ 0.0169657469f,
+ 0f,
+ -0.0676806f,
+ 0f,
+ 0.303180575f,
+ 0.500017047f,
+ 0.303180575f,
+ 0f,
+ -0.0676806f,
+ 0f,
+ 0.0169657469f,
+ 0f,
+ -0.0024741888f
+ };
+
+ private static readonly float[] Kernel_Dec2_80dB = new float[47]
+ {
+ -0.00019800663f,
+ 0f,
+ 0.000576853752f,
+ 0f,
+ -0.001352191f,
+ 0f,
+ 0.00272917747f,
+ 0f,
+ -0.00498819351f,
+ 0f,
+ 0.008499503f,
+ 0f,
+ -0.0137885809f,
+ 0f,
+ 0.0217131376f,
+ 0f,
+ -0.0339800119f,
+ 0f,
+ 0.0549448729f,
+ 0f,
+ -0.100657463f,
+ 0f,
+ 0.3164574f,
+ 0.5f,
+ 0.3164574f,
+ 0f,
+ -0.100657463f,
+ 0f,
+ 0.0549448729f,
+ 0f,
+ -0.0339800119f,
+ 0f,
+ 0.0217131376f,
+ 0f,
+ -0.0137885809f,
+ 0f,
+ 0.008499503f,
+ 0f,
+ -0.00498819351f,
+ 0f,
+ 0.00272917747f,
+ 0f,
+ -0.001352191f,
+ 0f,
+ 0.000576853752f,
+ 0f,
+ -0.00019800663f
+ };
+
+ public static readonly float[][] FirKernels100dB = new float[4][]
+ {
+ ConversionFilters.Kernel_Dec2_80dB,
+ ConversionFilters.Kernel_Dec4_90dB,
+ ConversionFilters.Kernel_Dec8_100dB,
+ ConversionFilters.Kernel_Dec16_110dB
+ };
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.Airspy/NativeMethods.cs b/SDRSharp/SDRSharp.FrontEnds.Airspy/NativeMethods.cs
new file mode 100644
index 0000000..62342e1
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.Airspy/NativeMethods.cs
@@ -0,0 +1,135 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SDRSharp.FrontEnds.Airspy
+{
+ public static class NativeMethods
+ {
+ private const string LibAirSpy = "airspy";
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_init();
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_exit();
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_open(out IntPtr dev);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_close(IntPtr dev);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_set_samplerate(IntPtr dev, uint samplerate);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public unsafe static extern airspy_error airspy_set_conversion_filter_float32(IntPtr dev, float* kernel, int len);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public unsafe static extern airspy_error airspy_set_conversion_filter_int16(IntPtr dev, short* kernel, int len);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public unsafe static extern airspy_error airspy_get_samplerates(IntPtr dev, uint* buffer, uint len);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_start_rx(IntPtr dev, airspy_sample_block_cb_fn cb, IntPtr rx_ctx);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_stop_rx(IntPtr dev);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_is_streaming(IntPtr dev);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl, EntryPoint = "airspy_board_id_name")]
+ private static extern IntPtr airspy_board_id_name_native(uint index);
+
+ public static string airspy_board_id_name(uint index)
+ {
+ try
+ {
+ IntPtr ptr = NativeMethods.airspy_board_id_name_native(index);
+ return Marshal.PtrToStringAnsi(ptr);
+ }
+ catch (EntryPointNotFoundException ex)
+ {
+ Console.WriteLine("{0}:\n {1}", ex.GetType().Name, ex.Message);
+ return "AirSpy";
+ }
+ }
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_set_sample_type(IntPtr dev, airspy_sample_type sample_type);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_set_freq(IntPtr dev, uint freq_hz);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_set_packing(IntPtr dev, [MarshalAs(UnmanagedType.U1)] bool value);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_set_lna_gain(IntPtr dev, byte value);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_set_mixer_gain(IntPtr dev, byte value);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_set_vga_gain(IntPtr dev, byte value);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_set_lna_agc(IntPtr dev, [MarshalAs(UnmanagedType.U1)] bool value);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_set_mixer_agc(IntPtr dev, [MarshalAs(UnmanagedType.U1)] bool value);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_set_linearity_gain(IntPtr dev, byte value);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_set_sensitivity_gain(IntPtr dev, byte value);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_r820t_write(IntPtr device, byte register_number, byte value);
+
+ public static airspy_error airspy_r820t_write_mask(IntPtr device, byte reg, byte value, byte mask)
+ {
+ byte b;
+ airspy_error airspy_error = NativeMethods.airspy_r820t_read(device, reg, out b);
+ if (airspy_error < airspy_error.AIRSPY_SUCCESS)
+ {
+ return airspy_error;
+ }
+ value = (byte)((b & ~mask) | (value & mask));
+ return NativeMethods.airspy_r820t_write(device, reg, value);
+ }
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_r820t_read(IntPtr device, byte register_number, out byte value);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_si5351c_write(IntPtr device, byte register_number, byte value);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_si5351c_read(IntPtr device, byte register_number, out byte value);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_set_rf_bias(IntPtr dev, [In] [MarshalAs(UnmanagedType.U1)] bool value);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_gpio_read(IntPtr device, airspy_gpio_port_t port, airspy_gpio_pin_t pin, [MarshalAs(UnmanagedType.U1)] out bool value);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_gpio_write(IntPtr device, airspy_gpio_port_t port, airspy_gpio_pin_t pin, [MarshalAs(UnmanagedType.U1)] bool value);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_spiflash_erase(IntPtr device);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public unsafe static extern airspy_error airspy_spiflash_write(IntPtr device, uint address, ushort length, byte* data);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public unsafe static extern airspy_error airspy_spiflash_read(IntPtr device, uint address, ushort length, byte* data);
+
+ [DllImport("airspy", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspy_error airspy_spiflash_erase_sector(IntPtr device, ushort sector_num);
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.Airspy/airspy_error.cs b/SDRSharp/SDRSharp.FrontEnds.Airspy/airspy_error.cs
new file mode 100644
index 0000000..c9648a9
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.Airspy/airspy_error.cs
@@ -0,0 +1,17 @@
+namespace SDRSharp.FrontEnds.Airspy
+{
+ public enum airspy_error
+ {
+ AIRSPY_SUCCESS,
+ AIRSPY_TRUE,
+ AIRSPY_ERROR_INVALID_PARAM = -2,
+ AIRSPY_ERROR_NOT_FOUND = -5,
+ AIRSPY_ERROR_BUSY = -6,
+ AIRSPY_ERROR_NO_MEM = -11,
+ AIRSPY_ERROR_LIBUSB = -1000,
+ AIRSPY_ERROR_THREAD = -1001,
+ AIRSPY_ERROR_STREAMING_THREAD_ERR = -1002,
+ AIRSPY_ERROR_STREAMING_STOPPED = -1003,
+ AIRSPY_ERROR_OTHER = -9999
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.Airspy/airspy_gpio_pin_t.cs b/SDRSharp/SDRSharp.FrontEnds.Airspy/airspy_gpio_pin_t.cs
new file mode 100644
index 0000000..d4d27f1
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.Airspy/airspy_gpio_pin_t.cs
@@ -0,0 +1,38 @@
+namespace SDRSharp.FrontEnds.Airspy
+{
+ public enum airspy_gpio_pin_t
+ {
+ GPIO_PIN0,
+ GPIO_PIN1,
+ GPIO_PIN2,
+ GPIO_PIN3,
+ GPIO_PIN4,
+ GPIO_PIN5,
+ GPIO_PIN6,
+ GPIO_PIN7,
+ GPIO_PIN8,
+ GPIO_PIN9,
+ GPIO_PIN10,
+ GPIO_PIN11,
+ GPIO_PIN12,
+ GPIO_PIN13,
+ GPIO_PIN14,
+ GPIO_PIN15,
+ GPIO_PIN16,
+ GPIO_PIN17,
+ GPIO_PIN18,
+ GPIO_PIN19,
+ GPIO_PIN20,
+ GPIO_PIN21,
+ GPIO_PIN22,
+ GPIO_PIN23,
+ GPIO_PIN24,
+ GPIO_PIN25,
+ GPIO_PIN26,
+ GPIO_PIN27,
+ GPIO_PIN28,
+ GPIO_PIN29,
+ GPIO_PIN30,
+ GPIO_PIN31
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.Airspy/airspy_gpio_port_t.cs b/SDRSharp/SDRSharp.FrontEnds.Airspy/airspy_gpio_port_t.cs
new file mode 100644
index 0000000..185b3db
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.Airspy/airspy_gpio_port_t.cs
@@ -0,0 +1,14 @@
+namespace SDRSharp.FrontEnds.Airspy
+{
+ public enum airspy_gpio_port_t
+ {
+ GPIO_PORT0,
+ GPIO_PORT1,
+ GPIO_PORT2,
+ GPIO_PORT3,
+ GPIO_PORT4,
+ GPIO_PORT5,
+ GPIO_PORT6,
+ GPIO_PORT7
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.Airspy/airspy_sample_block_cb_fn.cs b/SDRSharp/SDRSharp.FrontEnds.Airspy/airspy_sample_block_cb_fn.cs
new file mode 100644
index 0000000..a71e29e
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.Airspy/airspy_sample_block_cb_fn.cs
@@ -0,0 +1,7 @@
+using System.Runtime.InteropServices;
+
+namespace SDRSharp.FrontEnds.Airspy
+{
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public unsafe delegate int airspy_sample_block_cb_fn(airspy_transfer* ptr);
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.Airspy/airspy_sample_type.cs b/SDRSharp/SDRSharp.FrontEnds.Airspy/airspy_sample_type.cs
new file mode 100644
index 0000000..e5f5ac3
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.Airspy/airspy_sample_type.cs
@@ -0,0 +1,10 @@
+namespace SDRSharp.FrontEnds.Airspy
+{
+ public enum airspy_sample_type
+ {
+ AIRSPY_SAMPLE_FLOAT32_IQ,
+ AIRSPY_SAMPLE_FLOAT32_REAL,
+ AIRSPY_SAMPLE_INT16_IQ,
+ AIRSPY_SAMPLE_INT16_REAL
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.Airspy/airspy_transfer.cs b/SDRSharp/SDRSharp.FrontEnds.Airspy/airspy_transfer.cs
new file mode 100644
index 0000000..368cc27
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.Airspy/airspy_transfer.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace SDRSharp.FrontEnds.Airspy
+{
+ public struct airspy_transfer
+ {
+ public IntPtr device;
+
+ public IntPtr ctx;
+
+ public unsafe void* samples;
+
+ public int sample_count;
+
+ public ulong dropped_samples;
+
+ public airspy_sample_type sample_type;
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.AirspyHF/AirspyHFDevice.cs b/SDRSharp/SDRSharp.FrontEnds.AirspyHF/AirspyHFDevice.cs
new file mode 100644
index 0000000..9f43040
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.AirspyHF/AirspyHFDevice.cs
@@ -0,0 +1,245 @@
+using SDRSharp.Common;
+using SDRSharp.Radio;
+using System;
+using System.Runtime.InteropServices;
+
+namespace SDRSharp.FrontEnds.AirspyHF
+{
+ public class AirspyHFDevice : IDisposable
+ {
+ public const string DeviceName = "AIRSPY HF+";
+
+ public const uint DefaultFrequency = 7200000u;
+
+ public const uint DefaultSampleRate = 768000u;
+
+ public const uint DefaultIFShift = 192000u;
+
+ private uint _deviceSampleRate = 768000u;
+
+ private int _decimationStages;
+
+ private IntPtr _dev;
+
+ private uint _centerFrequency;
+
+ private bool _isStreaming;
+
+ private double _ifShift;
+
+ private GCHandle _gcHandle;
+
+ private DownConverter _ddc;
+
+ private unsafe static readonly airspyhf_sample_cb _airspyhfCallback = AirspyHFDevice.AirSpyHFSamplesAvailable;
+
+ public int DecimationStages
+ {
+ get
+ {
+ return this._decimationStages;
+ }
+ set
+ {
+ this._decimationStages = value;
+ this.SetDeviceFrequency();
+ }
+ }
+
+ public int CalibrationPPB
+ {
+ get
+ {
+ int result;
+ NativeMethods.airspyhf_get_calibration(this._dev, out result);
+ return result;
+ }
+ set
+ {
+ NativeMethods.airspyhf_set_calibration(this._dev, value);
+ }
+ }
+
+ public double SampleRate
+ {
+ get
+ {
+ return (double)this._deviceSampleRate / (double)(1 << this._decimationStages);
+ }
+ }
+
+ public uint Frequency
+ {
+ get
+ {
+ return this._centerFrequency;
+ }
+ set
+ {
+ this._centerFrequency = value;
+ this.SetDeviceFrequency();
+ }
+ }
+
+ public bool IsStreaming
+ {
+ get
+ {
+ return this._isStreaming;
+ }
+ }
+
+ public bool IsHung
+ {
+ get
+ {
+ if (this._isStreaming)
+ {
+ return !NativeMethods.airspyhf_is_streaming(this._dev);
+ }
+ return false;
+ }
+ }
+
+ public event SamplesAvailableDelegate SamplesAvailable;
+
+ public unsafe AirspyHFDevice()
+ {
+ if (NativeMethods.airspyhf_open(out this._dev) != 0)
+ {
+ throw new ApplicationException("Cannot open AIRSPY HF+ device");
+ }
+ uint num = default(uint);
+ if (NativeMethods.airspyhf_get_samplerates(this._dev, &num, 0u) == airspyhf_error.SUCCESS && num != 0)
+ {
+ uint[] array = new uint[num];
+ uint[] array2 = array;
+ airspyhf_error airspyhf_error;
+ fixed (uint* buffer = array2)
+ {
+ airspyhf_error = NativeMethods.airspyhf_get_samplerates(this._dev, buffer, num);
+ }
+ if (airspyhf_error == airspyhf_error.SUCCESS)
+ {
+ this._deviceSampleRate = array[0];
+ NativeMethods.airspyhf_set_samplerate(this._dev, this._deviceSampleRate);
+ }
+ }
+ this._gcHandle = GCHandle.Alloc(this);
+ }
+
+ ~AirspyHFDevice()
+ {
+ this.Dispose();
+ }
+
+ public void Dispose()
+ {
+ if (this._dev != IntPtr.Zero)
+ {
+ this.Stop();
+ NativeMethods.airspyhf_close(this._dev);
+ if (this._gcHandle.IsAllocated)
+ {
+ this._gcHandle.Free();
+ }
+ this._dev = IntPtr.Zero;
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ internal void FlashCalibration()
+ {
+ NativeMethods.airspyhf_flash_calibration(this._dev);
+ }
+
+ public unsafe void Start()
+ {
+ if (!this._isStreaming)
+ {
+ if (NativeMethods.airspyhf_start(this._dev, AirspyHFDevice._airspyhfCallback, (IntPtr)this._gcHandle) != 0)
+ {
+ throw new ApplicationException("airspy_start_rx() error");
+ }
+ this._isStreaming = true;
+ }
+ }
+
+ public void Stop()
+ {
+ if (this._isStreaming)
+ {
+ NativeMethods.airspyhf_stop(this._dev);
+ this._isStreaming = false;
+ }
+ }
+
+ private void SetDeviceFrequency()
+ {
+ if (!(this._dev == IntPtr.Zero))
+ {
+ uint num = this._centerFrequency;
+ if (this._decimationStages > 1)
+ {
+ this._ifShift = -192000.0;
+ num += 192000;
+ }
+ else
+ {
+ this._ifShift = 0.0;
+ }
+ NativeMethods.airspyhf_set_freq(this._dev, num);
+ }
+ }
+
+ protected unsafe virtual void OnComplexSamplesAvailable(Complex* buffer, int length, ulong droppedSamples)
+ {
+ SamplesAvailableDelegate samplesAvailable = this.SamplesAvailable;
+ if (samplesAvailable != null)
+ {
+ ComplexSamplesEventArgs complexSamplesEventArgs = new ComplexSamplesEventArgs();
+ complexSamplesEventArgs.Buffer = buffer;
+ complexSamplesEventArgs.Length = length;
+ complexSamplesEventArgs.DroppedSamples = droppedSamples;
+ samplesAvailable(this, complexSamplesEventArgs);
+ }
+ }
+
+ private unsafe static int AirSpyHFSamplesAvailable(airspyhf_transfer* data)
+ {
+ int length = data->sample_count;
+ Complex* samples = data->samples;
+ ulong num = data->dropped_samples;
+ IntPtr ctx = data->ctx;
+ GCHandle gCHandle = GCHandle.FromIntPtr(ctx);
+ if (!gCHandle.IsAllocated)
+ {
+ return -1;
+ }
+ AirspyHFDevice airspyHFDevice = (AirspyHFDevice)gCHandle.Target;
+ if (airspyHFDevice._decimationStages > 0)
+ {
+ int num2 = 1 << airspyHFDevice._decimationStages;
+ if (airspyHFDevice._ddc == null || airspyHFDevice._ddc.DecimationRatio != num2)
+ {
+ airspyHFDevice._ddc = new DownConverter((double)airspyHFDevice._deviceSampleRate, num2);
+ }
+ airspyHFDevice._ddc.Frequency = airspyHFDevice._ifShift;
+ length = airspyHFDevice._ddc.Process(samples, length);
+ num >>= airspyHFDevice._decimationStages;
+ }
+ airspyHFDevice.OnComplexSamplesAvailable(samples, length, num);
+ return 0;
+ }
+
+ public unsafe airspyhf_error TunerRead(uint address, byte* data)
+ {
+ return NativeMethods.airspyhf_tuner_read(this._dev, address, data);
+ }
+
+ public airspyhf_error TunerWrite(uint address, uint value)
+ {
+ return NativeMethods.airspyhf_tuner_write(this._dev, address, value);
+ }
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.AirspyHF/AirspyHFIO.cs b/SDRSharp/SDRSharp.FrontEnds.AirspyHF/AirspyHFIO.cs
new file mode 100644
index 0000000..41fd134
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.AirspyHF/AirspyHFIO.cs
@@ -0,0 +1,242 @@
+using SDRSharp.Common;
+using SDRSharp.Radio;
+using System;
+using System.Windows.Forms;
+
+namespace SDRSharp.FrontEnds.AirspyHF
+{
+ public class AirspyHFIO : IFrontendController, IIQStreamController, ITunableSource, IFrontendOffset, IControlAwareObject, ISpectrumProvider, IConfigurationPanelProvider, ISampleRateChangeSource, IDisposable
+ {
+ private const float AliasFreeRatio = 1f;
+
+ private bool _disposed;
+
+ private ControllerPanel _gui;
+
+ private AirspyHFDevice _device;
+
+ private ISharpControl _control;
+
+ private long _frequency = 7200000L;
+
+ private int _decimationStages;
+
+ private SamplesAvailableDelegate _callback;
+
+ public bool CanTune
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ public long MinimumTunableFrequency
+ {
+ get
+ {
+ return 0L;
+ }
+ }
+
+ public long MaximumTunableFrequency
+ {
+ get
+ {
+ return 1700000000L;
+ }
+ }
+
+ public ISharpControl SharpControl
+ {
+ get
+ {
+ return this._control;
+ }
+ }
+
+ public double Samplerate
+ {
+ get
+ {
+ return 768000.0 / Math.Pow(2.0, (double)this._decimationStages);
+ }
+ }
+
+ public long Frequency
+ {
+ get
+ {
+ return this._frequency;
+ }
+ set
+ {
+ this._frequency = value;
+ this.SetDeviceFrequency();
+ }
+ }
+
+ public float UsableSpectrumRatio
+ {
+ get
+ {
+ return 1f;
+ }
+ }
+
+ public UserControl Gui
+ {
+ get
+ {
+ return this._gui;
+ }
+ }
+
+ public bool IsDeviceHung
+ {
+ get
+ {
+ if (this._device != null)
+ {
+ return this._device.IsHung;
+ }
+ return false;
+ }
+ }
+
+ public int Offset
+ {
+ get
+ {
+ return 0;
+ }
+ }
+
+ public int DecimationStages
+ {
+ get
+ {
+ return this._decimationStages;
+ }
+ set
+ {
+ this._decimationStages = value;
+ if (this._device != null)
+ {
+ this._device.DecimationStages = value;
+ }
+ if (this._gui != null)
+ {
+ this.NotifySampleRateChanged();
+ }
+ }
+ }
+
+ public event EventHandler SampleRateChanged;
+
+ public AirspyHFIO()
+ {
+ this._gui = new ControllerPanel(this);
+ }
+
+ ~AirspyHFIO()
+ {
+ this.Dispose();
+ }
+
+ public void Dispose()
+ {
+ if (!this._disposed)
+ {
+ this._disposed = true;
+ this.Close();
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ public void Open()
+ {
+ this._device = new AirspyHFDevice();
+ this._device.DecimationStages = this._decimationStages;
+ this._device.Frequency = (uint)this._frequency;
+ this._gui.Device = this._device;
+ this._device.SamplesAvailable += this.Device_SamplesAvailable;
+ }
+
+ public void Close()
+ {
+ if (this._device != null)
+ {
+ this._gui.Device = null;
+ this._device.SamplesAvailable -= this.Device_SamplesAvailable;
+ this._device.Dispose();
+ this._device = null;
+ }
+ }
+
+ public unsafe void Start(SamplesAvailableDelegate callback)
+ {
+ this._callback = callback;
+ try
+ {
+ if (this._device == null)
+ {
+ this.Open();
+ }
+ this._device.Start();
+ this.SetDeviceFrequency();
+ }
+ catch
+ {
+ this.Close();
+ throw;
+ }
+ }
+
+ public void Stop()
+ {
+ if (this._device != null)
+ {
+ this._device.Stop();
+ }
+ }
+
+ public void ShowSettingGUI(IWin32Window parent)
+ {
+ }
+
+ public void HideSettingGUI()
+ {
+ }
+
+ public void SetControl(object control)
+ {
+ this._control = (ISharpControl)control;
+ }
+
+ private void SetDeviceFrequency()
+ {
+ if (this._device != null)
+ {
+ this._device.Frequency = (uint)this._frequency;
+ }
+ }
+
+ private unsafe void Device_SamplesAvailable(object sender, ComplexSamplesEventArgs e)
+ {
+ this._callback(this, e.Buffer, e.Length);
+ }
+
+ public void NotifySampleRateChanged()
+ {
+ this._gui.BeginInvoke((Action)delegate
+ {
+ EventHandler sampleRateChanged = this.SampleRateChanged;
+ if (sampleRateChanged != null)
+ {
+ sampleRateChanged(this, EventArgs.Empty);
+ }
+ });
+ }
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.AirspyHF/ControllerPanel.cs b/SDRSharp/SDRSharp.FrontEnds.AirspyHF/ControllerPanel.cs
new file mode 100644
index 0000000..1fef24d
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.AirspyHF/ControllerPanel.cs
@@ -0,0 +1,309 @@
+using SDRSharp.Radio;
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.Globalization;
+using System.Windows.Forms;
+
+namespace SDRSharp.FrontEnds.AirspyHF
+{
+ public class ControllerPanel : UserControl
+ {
+ private AirspyHFIO _owner;
+
+ private AirspyHFDevice _device;
+
+ private IContainer components;
+
+ private Label label18;
+
+ private TableLayoutPanel tableLayoutPanel;
+
+ private Label label1;
+
+ private TextBox valueTextBox;
+
+ private TextBox addressTextBox;
+
+ private Button readButton;
+
+ private Button writeButton;
+
+ private Label label2;
+
+ private ComboBox spanComboBox;
+
+ private TableLayoutPanel tableLayoutPanel1;
+
+ private Label label3;
+
+ private NumericUpDown calibrationNumericUpDown;
+
+ private Button flashButton;
+
+ public AirspyHFDevice Device
+ {
+ get
+ {
+ return this._device;
+ }
+ set
+ {
+ this._device = value;
+ if (this._device != null)
+ {
+ this.calibrationNumericUpDown.Value = this._device.CalibrationPPB;
+ }
+ else
+ {
+ this.calibrationNumericUpDown.Value = decimal.Zero;
+ }
+ }
+ }
+
+ public ControllerPanel(AirspyHFIO owner)
+ {
+ this._owner = owner;
+ this.InitializeComponent();
+ for (int i = 0; i < 5; i++)
+ {
+ double num = (double)((float)(double)(768000u >> i) * this._owner.UsableSpectrumRatio) * 0.001;
+ this.spanComboBox.Items.Add(num + " kHz");
+ }
+ this.spanComboBox.SelectedIndex = Utils.GetIntSetting("airspyhf.decimation", 0);
+ if (Utils.GetIntSetting("airspyhf.debug", 0) == 0)
+ {
+ this.tableLayoutPanel.Visible = false;
+ base.Height -= this.tableLayoutPanel.Height;
+ }
+ }
+
+ private void spanComboBox_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ this._owner.DecimationStages = this.spanComboBox.SelectedIndex;
+ Utils.SaveSetting("airspyhf.decimation", this.spanComboBox.SelectedIndex);
+ }
+
+ private void calibrationNumericUpDown_ValueChanged(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ this._device.CalibrationPPB = (int)this.calibrationNumericUpDown.Value;
+ }
+ }
+
+ private void flashButton_Click(object sender, EventArgs e)
+ {
+ if (this._device != null)
+ {
+ this._device.FlashCalibration();
+ }
+ }
+
+ private unsafe void readButton_Click(object sender, EventArgs e)
+ {
+ if (this._device != null && this.addressTextBox.Text.Length > 0)
+ {
+ uint address = this.ParseHex(this.addressTextBox.Text);
+ byte[] array = new byte[6];
+ byte[] array2 = array;
+ fixed (byte* data = array2)
+ {
+ this._device.TunerRead(address, data);
+ }
+ uint num = (uint)(array[0] << 16 | array[1] << 8 | array[2]);
+ this.valueTextBox.Text = "0x" + string.Format("{0:x6}", num);
+ }
+ }
+
+ private void writeButton_Click(object sender, EventArgs e)
+ {
+ if (this._device != null && this.addressTextBox.Text.Length > 0 && this.valueTextBox.Text.Length > 0)
+ {
+ uint address = this.ParseHex(this.addressTextBox.Text);
+ uint value = this.ParseHex(this.valueTextBox.Text);
+ this._device.TunerWrite(address, value);
+ }
+ }
+
+ private uint ParseHex(string s)
+ {
+ s = s.Replace("0X", string.Empty).Replace("0x", string.Empty);
+ return uint.Parse(s, NumberStyles.HexNumber);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && this.components != null)
+ {
+ this.components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ private void InitializeComponent()
+ {
+ this.tableLayoutPanel = new TableLayoutPanel();
+ this.valueTextBox = new TextBox();
+ this.label18 = new Label();
+ this.label1 = new Label();
+ this.addressTextBox = new TextBox();
+ this.readButton = new Button();
+ this.writeButton = new Button();
+ this.label2 = new Label();
+ this.spanComboBox = new ComboBox();
+ this.tableLayoutPanel1 = new TableLayoutPanel();
+ this.label3 = new Label();
+ this.calibrationNumericUpDown = new NumericUpDown();
+ this.flashButton = new Button();
+ this.tableLayoutPanel.SuspendLayout();
+ this.tableLayoutPanel1.SuspendLayout();
+ ((ISupportInitialize)this.calibrationNumericUpDown).BeginInit();
+ base.SuspendLayout();
+ this.tableLayoutPanel.Anchor = (AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right);
+ this.tableLayoutPanel.ColumnCount = 4;
+ this.tableLayoutPanel1.SetColumnSpan(this.tableLayoutPanel, 2);
+ this.tableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50f));
+ this.tableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50f));
+ this.tableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
+ this.tableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
+ this.tableLayoutPanel.Controls.Add(this.valueTextBox, 1, 2);
+ this.tableLayoutPanel.Controls.Add(this.label18, 0, 1);
+ this.tableLayoutPanel.Controls.Add(this.label1, 1, 1);
+ this.tableLayoutPanel.Controls.Add(this.addressTextBox, 0, 2);
+ this.tableLayoutPanel.Controls.Add(this.readButton, 2, 2);
+ this.tableLayoutPanel.Controls.Add(this.writeButton, 3, 2);
+ this.tableLayoutPanel.Controls.Add(this.label3, 0, 0);
+ this.tableLayoutPanel.Controls.Add(this.calibrationNumericUpDown, 1, 0);
+ this.tableLayoutPanel.Controls.Add(this.flashButton, 2, 0);
+ this.tableLayoutPanel.Location = new Point(0, 27);
+ this.tableLayoutPanel.Margin = new Padding(0);
+ this.tableLayoutPanel.Name = "tableLayoutPanel";
+ this.tableLayoutPanel.RowCount = 3;
+ this.tableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.tableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.tableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
+ this.tableLayoutPanel.Size = new Size(202, 73);
+ this.tableLayoutPanel.TabIndex = 63;
+ this.valueTextBox.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.valueTextBox.Location = new Point(78, 47);
+ this.valueTextBox.Name = "valueTextBox";
+ this.valueTextBox.Size = new Size(69, 20);
+ this.valueTextBox.TabIndex = 37;
+ this.label18.Anchor = AnchorStyles.Left;
+ this.label18.AutoSize = true;
+ this.label18.Location = new Point(0, 29);
+ this.label18.Margin = new Padding(0);
+ this.label18.Name = "label18";
+ this.label18.Size = new Size(45, 13);
+ this.label18.TabIndex = 34;
+ this.label18.Text = "Address";
+ this.label1.Anchor = AnchorStyles.Left;
+ this.label1.AutoSize = true;
+ this.label1.Location = new Point(75, 29);
+ this.label1.Margin = new Padding(0);
+ this.label1.Name = "label1";
+ this.label1.Size = new Size(34, 13);
+ this.label1.TabIndex = 35;
+ this.label1.Text = "Value";
+ this.addressTextBox.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.addressTextBox.Location = new Point(3, 47);
+ this.addressTextBox.Name = "addressTextBox";
+ this.addressTextBox.Size = new Size(69, 20);
+ this.addressTextBox.TabIndex = 36;
+ this.readButton.Anchor = AnchorStyles.None;
+ this.readButton.Location = new Point(153, 47);
+ this.readButton.Name = "readButton";
+ this.readButton.Size = new Size(20, 20);
+ this.readButton.TabIndex = 38;
+ this.readButton.Text = "R";
+ this.readButton.UseVisualStyleBackColor = true;
+ this.readButton.Click += this.readButton_Click;
+ this.writeButton.Anchor = AnchorStyles.None;
+ this.writeButton.Location = new Point(179, 47);
+ this.writeButton.Name = "writeButton";
+ this.writeButton.Size = new Size(20, 20);
+ this.writeButton.TabIndex = 39;
+ this.writeButton.Text = "W";
+ this.writeButton.UseVisualStyleBackColor = true;
+ this.writeButton.Click += this.writeButton_Click;
+ this.label2.Anchor = AnchorStyles.Left;
+ this.label2.AutoSize = true;
+ this.label2.Location = new Point(3, 7);
+ this.label2.Name = "label2";
+ this.label2.Size = new Size(57, 13);
+ this.label2.TabIndex = 40;
+ this.label2.Text = "Bandwidth";
+ this.spanComboBox.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.spanComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
+ this.spanComboBox.FormattingEnabled = true;
+ this.spanComboBox.Location = new Point(79, 3);
+ this.spanComboBox.Name = "spanComboBox";
+ this.spanComboBox.Size = new Size(120, 21);
+ this.spanComboBox.TabIndex = 41;
+ this.spanComboBox.SelectedIndexChanged += this.spanComboBox_SelectedIndexChanged;
+ this.tableLayoutPanel1.ColumnCount = 2;
+ this.tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 37.62376f));
+ this.tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 62.37624f));
+ this.tableLayoutPanel1.Controls.Add(this.label2, 0, 0);
+ this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel, 0, 1);
+ this.tableLayoutPanel1.Controls.Add(this.spanComboBox, 1, 0);
+ this.tableLayoutPanel1.Dock = DockStyle.Fill;
+ this.tableLayoutPanel1.Location = new Point(0, 0);
+ this.tableLayoutPanel1.Name = "tableLayoutPanel1";
+ this.tableLayoutPanel1.RowCount = 2;
+ this.tableLayoutPanel1.RowStyles.Add(new RowStyle());
+ this.tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
+ this.tableLayoutPanel1.Size = new Size(202, 100);
+ this.tableLayoutPanel1.TabIndex = 64;
+ this.label3.Anchor = AnchorStyles.Left;
+ this.label3.AutoSize = true;
+ this.label3.Location = new Point(3, 8);
+ this.label3.Name = "label3";
+ this.label3.Size = new Size(57, 13);
+ this.label3.TabIndex = 40;
+ this.label3.Text = "CLK (PPB)";
+ this.calibrationNumericUpDown.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.calibrationNumericUpDown.Location = new Point(78, 4);
+ this.calibrationNumericUpDown.Maximum = new decimal(new int[4]
+ {
+ 10000,
+ 0,
+ 0,
+ 0
+ });
+ this.calibrationNumericUpDown.Minimum = new decimal(new int[4]
+ {
+ 10000,
+ 0,
+ 0,
+ -2147483648
+ });
+ this.calibrationNumericUpDown.Name = "calibrationNumericUpDown";
+ this.calibrationNumericUpDown.Size = new Size(69, 20);
+ this.calibrationNumericUpDown.TabIndex = 41;
+ this.calibrationNumericUpDown.TextAlign = HorizontalAlignment.Right;
+ this.calibrationNumericUpDown.ValueChanged += this.calibrationNumericUpDown_ValueChanged;
+ this.flashButton.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.tableLayoutPanel.SetColumnSpan(this.flashButton, 2);
+ this.flashButton.Location = new Point(153, 3);
+ this.flashButton.Name = "flashButton";
+ this.flashButton.Size = new Size(46, 23);
+ this.flashButton.TabIndex = 42;
+ this.flashButton.Text = "Flash";
+ this.flashButton.UseVisualStyleBackColor = true;
+ this.flashButton.Click += this.flashButton_Click;
+ base.AutoScaleDimensions = new SizeF(6f, 13f);
+ base.AutoScaleMode = AutoScaleMode.Font;
+ base.Controls.Add(this.tableLayoutPanel1);
+ base.Name = "ControllerPanel";
+ base.Size = new Size(202, 100);
+ this.tableLayoutPanel.ResumeLayout(false);
+ this.tableLayoutPanel.PerformLayout();
+ this.tableLayoutPanel1.ResumeLayout(false);
+ this.tableLayoutPanel1.PerformLayout();
+ ((ISupportInitialize)this.calibrationNumericUpDown).EndInit();
+ base.ResumeLayout(false);
+ }
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.AirspyHF/ControllerPanel.resx b/SDRSharp/SDRSharp.FrontEnds.AirspyHF/ControllerPanel.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.AirspyHF/ControllerPanel.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/SDRSharp/SDRSharp.FrontEnds.AirspyHF/NativeMethods.cs b/SDRSharp/SDRSharp.FrontEnds.AirspyHF/NativeMethods.cs
new file mode 100644
index 0000000..79f0946
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.AirspyHF/NativeMethods.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SDRSharp.FrontEnds.AirspyHF
+{
+ public static class NativeMethods
+ {
+ private const string LibAirspyHF = "airspyhf";
+
+ [DllImport("airspyhf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspyhf_error airspyhf_open(out IntPtr dev);
+
+ [DllImport("airspyhf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspyhf_error airspyhf_close(IntPtr dev);
+
+ [DllImport("airspyhf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspyhf_error airspyhf_start(IntPtr dev, airspyhf_sample_cb cb, IntPtr ctx);
+
+ [DllImport("airspyhf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspyhf_error airspyhf_stop(IntPtr dev);
+
+ [DllImport("airspyhf", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool airspyhf_is_streaming(IntPtr dev);
+
+ [DllImport("airspyhf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspyhf_error airspyhf_set_freq(IntPtr dev, uint freq_hz);
+
+ [DllImport("airspyhf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspyhf_error airspyhf_i2c_write(IntPtr device, byte register_number, byte value);
+
+ [DllImport("airspyhf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspyhf_error airspyhf_i2c_read(IntPtr device, byte register_number, out byte value);
+
+ [DllImport("airspyhf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspyhf_error airspyhf_tuner_write(IntPtr device, uint address, uint value);
+
+ [DllImport("airspyhf", CallingConvention = CallingConvention.Cdecl)]
+ public unsafe static extern airspyhf_error airspyhf_tuner_read(IntPtr device, uint address, byte* data);
+
+ [DllImport("airspyhf", CallingConvention = CallingConvention.Cdecl)]
+ public unsafe static extern airspyhf_error airspyhf_get_samplerates(IntPtr device, uint* buffer, uint len);
+
+ [DllImport("airspyhf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspyhf_error airspyhf_set_samplerate(IntPtr device, uint samplerate);
+
+ [DllImport("airspyhf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspyhf_error airspyhf_get_calibration(IntPtr device, out int ppb);
+
+ [DllImport("airspyhf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspyhf_error airspyhf_set_calibration(IntPtr device, int ppb);
+
+ [DllImport("airspyhf", CallingConvention = CallingConvention.Cdecl)]
+ public static extern airspyhf_error airspyhf_flash_calibration(IntPtr dev);
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.AirspyHF/airspyhf_error.cs b/SDRSharp/SDRSharp.FrontEnds.AirspyHF/airspyhf_error.cs
new file mode 100644
index 0000000..633b8f5
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.AirspyHF/airspyhf_error.cs
@@ -0,0 +1,8 @@
+namespace SDRSharp.FrontEnds.AirspyHF
+{
+ public enum airspyhf_error
+ {
+ SUCCESS,
+ ERROR = -1
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.AirspyHF/airspyhf_sample_cb.cs b/SDRSharp/SDRSharp.FrontEnds.AirspyHF/airspyhf_sample_cb.cs
new file mode 100644
index 0000000..a080688
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.AirspyHF/airspyhf_sample_cb.cs
@@ -0,0 +1,7 @@
+using System.Runtime.InteropServices;
+
+namespace SDRSharp.FrontEnds.AirspyHF
+{
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public unsafe delegate int airspyhf_sample_cb(airspyhf_transfer* ptr);
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.AirspyHF/airspyhf_transfer.cs b/SDRSharp/SDRSharp.FrontEnds.AirspyHF/airspyhf_transfer.cs
new file mode 100644
index 0000000..7bfb3b0
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.AirspyHF/airspyhf_transfer.cs
@@ -0,0 +1,18 @@
+using SDRSharp.Radio;
+using System;
+
+namespace SDRSharp.FrontEnds.AirspyHF
+{
+ public struct airspyhf_transfer
+ {
+ public IntPtr device;
+
+ public IntPtr ctx;
+
+ public unsafe Complex* samples;
+
+ public int sample_count;
+
+ public ulong dropped_samples;
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.SpyServer/ClientHandshake.cs b/SDRSharp/SDRSharp.FrontEnds.SpyServer/ClientHandshake.cs
new file mode 100644
index 0000000..020acd0
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.SpyServer/ClientHandshake.cs
@@ -0,0 +1,9 @@
+namespace SDRSharp.FrontEnds.SpyServer
+{
+ public struct ClientHandshake
+ {
+ public uint ProtocolVersion;
+
+ public uint ClientNameLength;
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.SpyServer/ClientSync.cs b/SDRSharp/SDRSharp.FrontEnds.SpyServer/ClientSync.cs
new file mode 100644
index 0000000..6a96d1c
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.SpyServer/ClientSync.cs
@@ -0,0 +1,23 @@
+namespace SDRSharp.FrontEnds.SpyServer
+{
+ public struct ClientSync
+ {
+ public uint CanControl;
+
+ public uint Gain;
+
+ public uint DeviceCenterFrequency;
+
+ public uint IQCenterFrequency;
+
+ public uint FFTCenterFrequency;
+
+ public uint MinimumIQCenterFrequency;
+
+ public uint MaximumIQCenterFrequency;
+
+ public uint MinimumFFTCenterFrequency;
+
+ public uint MaximumFFTCenterFrequency;
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.SpyServer/CommandHeader.cs b/SDRSharp/SDRSharp.FrontEnds.SpyServer/CommandHeader.cs
new file mode 100644
index 0000000..00f5966
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.SpyServer/CommandHeader.cs
@@ -0,0 +1,9 @@
+namespace SDRSharp.FrontEnds.SpyServer
+{
+ public struct CommandHeader
+ {
+ public CommandType CommandType;
+
+ public uint BodySize;
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.SpyServer/CommandType.cs b/SDRSharp/SDRSharp.FrontEnds.SpyServer/CommandType.cs
new file mode 100644
index 0000000..4f9ca83
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.SpyServer/CommandType.cs
@@ -0,0 +1,10 @@
+namespace SDRSharp.FrontEnds.SpyServer
+{
+ public enum CommandType : uint
+ {
+ CMD_HELLO,
+ CMD_GET_SETTING,
+ CMD_SET_SETTING,
+ CMD_PING
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.SpyServer/Constants.cs b/SDRSharp/SDRSharp.FrontEnds.SpyServer/Constants.cs
new file mode 100644
index 0000000..3e0f73c
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.SpyServer/Constants.cs
@@ -0,0 +1,42 @@
+namespace SDRSharp.FrontEnds.SpyServer
+{
+ public static class Constants
+ {
+ public const uint SPYSERVER_PROTOCOL_VERSION = 33556024u;
+
+ public const uint SPYSERVER_MAX_COMMAND_BODY_SIZE = 256u;
+
+ public const uint SPYSERVER_MAX_MESSAGE_BODY_SIZE = 1048576u;
+
+ public const uint SPYSERVER_MAX_DISPLAY_PIXELS = 32768u;
+
+ public const uint SPYSERVER_MIN_DISPLAY_PIXELS = 100u;
+
+ public const uint SPYSERVER_MAX_FFT_DB_RANGE = 150u;
+
+ public const uint SPYSERVER_MIN_FFT_DB_RANGE = 10u;
+
+ public const uint SPYSERVER_MAX_FFT_DB_OFFSET = 100u;
+
+ public const uint SPYSERVER_DIGITAL_GAIN_AUTO = uint.MaxValue;
+
+ public const int SPYSERVER_MESSAGE_TYPE_BITS = 16;
+
+ public const uint SPYSERVER_MESSAGE_TYPE_MASK = 65535u;
+
+ public static string GetDeviceName(DeviceType deviceID)
+ {
+ switch (deviceID)
+ {
+ case DeviceType.DEVICE_AIRSPY_ONE:
+ return "Airspy One";
+ case DeviceType.DEVICE_AIRSPY_HF:
+ return "Airspy HF+";
+ case DeviceType.DEVICE_RTLSDR:
+ return "RTL-SDR";
+ default:
+ return "Unknown";
+ }
+ }
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.SpyServer/ControllerPanel.cs b/SDRSharp/SDRSharp.FrontEnds.SpyServer/ControllerPanel.cs
new file mode 100644
index 0000000..2a7d523
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.SpyServer/ControllerPanel.cs
@@ -0,0 +1,579 @@
+using SDRSharp.Radio;
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.IO;
+using System.Text;
+using System.Windows.Forms;
+
+namespace SDRSharp.FrontEnds.SpyServer
+{
+ public class ControllerPanel : UserControl
+ {
+ private const int DefaultPort = 5555;
+
+ private readonly string _historyFileName = ".\\spybrowser.history";
+
+ private SpyServerIO _owner;
+
+ private float _rate;
+
+ private int _minDecimation;
+
+ private IContainer components;
+
+ private TableLayoutPanel hostTableLayoutPanel;
+
+ private Button connectButton;
+
+ private TableLayoutPanel deviceInfoTableLayoutPanel;
+
+ private TableLayoutPanel bandwidthTableLayoutPanel;
+
+ private TableLayoutPanel iqFormatTableLayoutPanel;
+
+ private TableLayoutPanel gainTableLayoutPanel;
+
+ private Label label1;
+
+ private ComboBox bandwidthComboBox;
+
+ private Label gainLabel;
+
+ private Label label2;
+
+ private TrackBar gainTrackBar;
+
+ private Label deviceSerialLabel;
+
+ private Label deviceNameLabel;
+
+ private CheckBox useFullIQCheckBox;
+
+ private Label bitrateLabel;
+
+ private Timer downStreamTimer;
+
+ private Label label3;
+
+ private ComboBox streamFormatComboBox;
+
+ private ComboBox uriComboBox;
+
+ private Label serverVersionLabel;
+
+ public string Host
+ {
+ get
+ {
+ Uri uri = new Uri(this.uriComboBox.Text);
+ return uri.Host;
+ }
+ }
+
+ public int Port
+ {
+ get
+ {
+ Uri uri = new Uri(this.uriComboBox.Text);
+ if (uri.Port > 0)
+ {
+ return uri.Port;
+ }
+ return 5555;
+ }
+ }
+
+ public bool UseFullIQ
+ {
+ get
+ {
+ return this.useFullIQCheckBox.Checked;
+ }
+ set
+ {
+ this.useFullIQCheckBox.Checked = value;
+ }
+ }
+
+ public int Decimation
+ {
+ get
+ {
+ return Math.Max(0, this.bandwidthComboBox.SelectedIndex);
+ }
+ }
+
+ public ControllerPanel(SpyServerIO owner)
+ {
+ this._owner = owner;
+ this.InitializeComponent();
+ try
+ {
+ this.uriComboBox.Items.AddRange(File.ReadAllLines(this._historyFileName));
+ }
+ catch
+ {
+ }
+ this.uriComboBox.Text = Utils.GetStringSetting("spyserver.uri", "sdr://127.0.0.1:5555");
+ this.streamFormatComboBox.SelectedIndex = Utils.GetIntSetting("spyserver.streamFormat", 3);
+ this.useFullIQCheckBox.Checked = Utils.GetBooleanSetting("spyserver.fullIQ", false);
+ this.useFullIQCheckBox_CheckStateChanged(null, null);
+ this.streamFormatComboBox_SelectedIndexChanged(null, null);
+ this.useFullIQCheckBox_CheckStateChanged(null, null);
+ this.uriComboBox.SelectedValueChanged += this.connectButton_Click;
+ }
+
+ public void SaveSettings()
+ {
+ Utils.SaveSetting("spyserver.uri", this.uriComboBox.Text);
+ Utils.SaveSetting("spyserver.streamFormat", this.streamFormatComboBox.SelectedIndex);
+ Utils.SaveSetting("spyserver.fullIQ", this.useFullIQCheckBox.Checked);
+ StringBuilder stringBuilder = new StringBuilder();
+ for (int i = 0; i < this.uriComboBox.Items.Count; i++)
+ {
+ stringBuilder.AppendLine(this.uriComboBox.Items[i].ToString());
+ }
+ File.WriteAllText(this._historyFileName, stringBuilder.ToString());
+ }
+
+ public void Force8bit()
+ {
+ this.streamFormatComboBox.SelectedIndex = 3;
+ }
+
+ public void EnableURI(bool enable)
+ {
+ this.uriComboBox.Enabled = enable;
+ }
+
+ public void EnableFullIQ(bool enable)
+ {
+ if (base.InvokeRequired)
+ {
+ this.useFullIQCheckBox.Enabled = enable;
+ }
+ else if (base.IsHandleCreated)
+ {
+ base.BeginInvoke((Action)delegate
+ {
+ this.useFullIQCheckBox.Enabled = enable;
+ });
+ }
+ }
+
+ public void UpdateDisplaySections(bool connected, bool bandwidth, bool format, bool gain)
+ {
+ int num = this.hostTableLayoutPanel.Height;
+ this.deviceInfoTableLayoutPanel.Visible = connected;
+ this.bandwidthTableLayoutPanel.Visible = bandwidth;
+ this.iqFormatTableLayoutPanel.Visible = format;
+ this.gainTableLayoutPanel.Visible = gain;
+ if (connected)
+ {
+ num += this.deviceInfoTableLayoutPanel.Height;
+ }
+ if (bandwidth)
+ {
+ num += this.bandwidthTableLayoutPanel.Height;
+ }
+ if (format)
+ {
+ num += this.iqFormatTableLayoutPanel.Height;
+ }
+ if (gain)
+ {
+ num += this.gainTableLayoutPanel.Height;
+ }
+ base.Height = num;
+ }
+
+ public void UpdateGain(int value, bool canControl)
+ {
+ if (value >= this.gainTrackBar.Minimum && value <= this.gainTrackBar.Maximum)
+ {
+ this.gainTrackBar.Value = value;
+ }
+ this.gainTrackBar.Enabled = canControl;
+ }
+
+ internal void UpdateGain(object gain, bool canControl)
+ {
+ throw new NotImplementedException();
+ }
+
+ private void connectButton_Click(object sender, EventArgs e)
+ {
+ try
+ {
+ if (this._owner.Connected)
+ {
+ this._owner.Disconnect();
+ }
+ else
+ {
+ this._owner.Connect();
+ this.UpdateHistory();
+ }
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
+ }
+ }
+
+ private void UpdateBandwidthOptions(int[] bandwidthOptions)
+ {
+ this.bandwidthComboBox.Items.Clear();
+ for (int i = 0; i < bandwidthOptions.Length; i++)
+ {
+ string frequencyDisplay = Utils.GetFrequencyDisplay(bandwidthOptions[i], true);
+ this.bandwidthComboBox.Items.Add(frequencyDisplay);
+ }
+ if (bandwidthOptions.Length != 0)
+ {
+ this.bandwidthComboBox.SelectedIndex = 0;
+ }
+ }
+
+ public void UpdateControlOptions(string deviceName, string deviceSerial, string serverVersion, int[] bandwidthOptions, int minDecimation, int maxGain)
+ {
+ this.deviceNameLabel.Text = "Device: " + deviceName;
+ this.deviceSerialLabel.Text = "SN: " + deviceSerial;
+ this.serverVersionLabel.Text = "Server: " + serverVersion;
+ this._minDecimation = minDecimation;
+ this.UpdateBandwidthOptions(bandwidthOptions);
+ this._owner.SetDecimation(this.bandwidthComboBox.SelectedIndex);
+ this._owner.SetFormat((StreamFormat)(4 - this.streamFormatComboBox.SelectedIndex));
+ this.gainTrackBar.ValueChanged -= this.gainTrackBar_ValueChanged;
+ this.gainTrackBar.Maximum = maxGain;
+ this.gainTrackBar.Value = 0;
+ this.gainTrackBar.ValueChanged += this.gainTrackBar_ValueChanged;
+ this.gainLabel.Text = "0";
+ }
+
+ private void bandwidthComboBox_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ if (this.bandwidthComboBox.SelectedIndex < this._minDecimation)
+ {
+ this.UseFullIQ = false;
+ this.EnableFullIQ(false);
+ }
+ else
+ {
+ this.EnableFullIQ(!this._owner.Control.IsPlaying);
+ }
+ this._owner.SetDecimation(this.bandwidthComboBox.SelectedIndex);
+ }
+
+ private void streamFormatComboBox_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ if (this._owner != null)
+ {
+ this._owner.SetFormat((StreamFormat)(4 - this.streamFormatComboBox.SelectedIndex));
+ }
+ }
+
+ private void gainTrackBar_ValueChanged(object sender, EventArgs e)
+ {
+ this.gainLabel.Text = this.gainTrackBar.Value.ToString();
+ this._owner.SetGain(this.gainTrackBar.Value);
+ }
+
+ private void useFullIQCheckBox_CheckStateChanged(object sender, EventArgs e)
+ {
+ this._owner.FFTEnabled = !this.useFullIQCheckBox.Checked;
+ }
+
+ private void downStreamTimer_Tick(object sender, EventArgs e)
+ {
+ long downstreamBytes = this._owner.GetDownstreamBytes();
+ if (downstreamBytes == 0L)
+ {
+ this.bitrateLabel.Text = "0 kB/s";
+ this._rate = 0f;
+ }
+ else
+ {
+ float num = (float)downstreamBytes / (0.001f * (float)this.downStreamTimer.Interval);
+ float num2 = num - this._rate;
+ float num3 = (num2 > 0f) ? 0.8f : 0.2f;
+ this._rate += num3 * num2;
+ if (this._rate < 1000000f)
+ {
+ this.bitrateLabel.Text = Math.Round((double)(this._rate * 0.001f)) + " kB/s";
+ }
+ else
+ {
+ this.bitrateLabel.Text = Math.Round((double)(this._rate * 1E-06f), 1) + " MB/s";
+ }
+ }
+ }
+
+ private void uriComboBox_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.KeyCode == Keys.Return)
+ {
+ this.connectButton_Click(sender, e);
+ }
+ }
+
+ private void UpdateHistory()
+ {
+ string text = this.uriComboBox.Text;
+ Uri uri = new Uri(text);
+ if (uri.IsWellFormedOriginalString())
+ {
+ for (int i = 0; i < this.uriComboBox.Items.Count; i++)
+ {
+ Uri uri2 = new Uri(this.uriComboBox.Items[i].ToString());
+ if (string.Compare(uri2.AbsoluteUri, uri.AbsoluteUri) == 0)
+ {
+ return;
+ }
+ }
+ this.uriComboBox.Items.Add(uri.AbsoluteUri);
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && this.components != null)
+ {
+ this.components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ private void InitializeComponent()
+ {
+ this.components = new Container();
+ this.hostTableLayoutPanel = new TableLayoutPanel();
+ this.connectButton = new Button();
+ this.uriComboBox = new ComboBox();
+ this.bandwidthTableLayoutPanel = new TableLayoutPanel();
+ this.label1 = new Label();
+ this.bandwidthComboBox = new ComboBox();
+ this.label3 = new Label();
+ this.streamFormatComboBox = new ComboBox();
+ this.gainTableLayoutPanel = new TableLayoutPanel();
+ this.gainLabel = new Label();
+ this.label2 = new Label();
+ this.gainTrackBar = new TrackBar();
+ this.deviceInfoTableLayoutPanel = new TableLayoutPanel();
+ this.bitrateLabel = new Label();
+ this.deviceSerialLabel = new Label();
+ this.deviceNameLabel = new Label();
+ this.useFullIQCheckBox = new CheckBox();
+ this.downStreamTimer = new Timer(this.components);
+ this.iqFormatTableLayoutPanel = new TableLayoutPanel();
+ this.serverVersionLabel = new Label();
+ this.hostTableLayoutPanel.SuspendLayout();
+ this.bandwidthTableLayoutPanel.SuspendLayout();
+ this.gainTableLayoutPanel.SuspendLayout();
+ ((ISupportInitialize)this.gainTrackBar).BeginInit();
+ this.deviceInfoTableLayoutPanel.SuspendLayout();
+ this.iqFormatTableLayoutPanel.SuspendLayout();
+ base.SuspendLayout();
+ this.hostTableLayoutPanel.ColumnCount = 2;
+ this.hostTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
+ this.hostTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 32f));
+ this.hostTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 20f));
+ this.hostTableLayoutPanel.Controls.Add(this.connectButton, 1, 0);
+ this.hostTableLayoutPanel.Controls.Add(this.uriComboBox, 0, 0);
+ this.hostTableLayoutPanel.Dock = DockStyle.Top;
+ this.hostTableLayoutPanel.Location = new Point(0, 0);
+ this.hostTableLayoutPanel.Name = "hostTableLayoutPanel";
+ this.hostTableLayoutPanel.RowCount = 1;
+ this.hostTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
+ this.hostTableLayoutPanel.Size = new Size(202, 30);
+ this.hostTableLayoutPanel.TabIndex = 64;
+ this.connectButton.Location = new Point(173, 3);
+ this.connectButton.Name = "connectButton";
+ this.connectButton.Size = new Size(25, 23);
+ this.connectButton.TabIndex = 2;
+ this.connectButton.Text = "C";
+ this.connectButton.UseVisualStyleBackColor = true;
+ this.connectButton.Click += this.connectButton_Click;
+ this.uriComboBox.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.uriComboBox.FormattingEnabled = true;
+ this.uriComboBox.Location = new Point(3, 4);
+ this.uriComboBox.Name = "uriComboBox";
+ this.uriComboBox.Size = new Size(164, 21);
+ this.uriComboBox.TabIndex = 3;
+ this.uriComboBox.KeyDown += this.uriComboBox_KeyDown;
+ this.bandwidthTableLayoutPanel.ColumnCount = 2;
+ this.bandwidthTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 40f));
+ this.bandwidthTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 60f));
+ this.bandwidthTableLayoutPanel.Controls.Add(this.label1, 0, 0);
+ this.bandwidthTableLayoutPanel.Controls.Add(this.bandwidthComboBox, 1, 0);
+ this.bandwidthTableLayoutPanel.Dock = DockStyle.Top;
+ this.bandwidthTableLayoutPanel.Location = new Point(0, 106);
+ this.bandwidthTableLayoutPanel.Name = "bandwidthTableLayoutPanel";
+ this.bandwidthTableLayoutPanel.RowCount = 1;
+ this.bandwidthTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
+ this.bandwidthTableLayoutPanel.Size = new Size(202, 31);
+ this.bandwidthTableLayoutPanel.TabIndex = 65;
+ this.label1.Anchor = AnchorStyles.Left;
+ this.label1.AutoSize = true;
+ this.label1.Location = new Point(3, 9);
+ this.label1.Name = "label1";
+ this.label1.Size = new Size(57, 13);
+ this.label1.TabIndex = 0;
+ this.label1.Text = "Bandwidth";
+ this.bandwidthComboBox.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.bandwidthComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
+ this.bandwidthComboBox.FormattingEnabled = true;
+ this.bandwidthComboBox.Location = new Point(83, 5);
+ this.bandwidthComboBox.Name = "bandwidthComboBox";
+ this.bandwidthComboBox.Size = new Size(116, 21);
+ this.bandwidthComboBox.TabIndex = 1;
+ this.bandwidthComboBox.SelectedIndexChanged += this.bandwidthComboBox_SelectedIndexChanged;
+ this.label3.Anchor = AnchorStyles.Left;
+ this.label3.AutoSize = true;
+ this.label3.Location = new Point(3, 9);
+ this.label3.Name = "label3";
+ this.label3.Size = new Size(53, 13);
+ this.label3.TabIndex = 2;
+ this.label3.Text = "IQ Format";
+ this.streamFormatComboBox.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.streamFormatComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
+ this.streamFormatComboBox.FormattingEnabled = true;
+ this.streamFormatComboBox.Items.AddRange(new object[4]
+ {
+ "Float 32bit",
+ "PCM 24bit",
+ "PCM 16bit",
+ "PCM 8bit"
+ });
+ this.streamFormatComboBox.Location = new Point(83, 5);
+ this.streamFormatComboBox.Name = "streamFormatComboBox";
+ this.streamFormatComboBox.Size = new Size(116, 21);
+ this.streamFormatComboBox.TabIndex = 1;
+ this.streamFormatComboBox.SelectedIndexChanged += this.streamFormatComboBox_SelectedIndexChanged;
+ this.gainTableLayoutPanel.ColumnCount = 2;
+ this.gainTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50f));
+ this.gainTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50f));
+ this.gainTableLayoutPanel.Controls.Add(this.gainLabel, 1, 0);
+ this.gainTableLayoutPanel.Controls.Add(this.label2, 0, 0);
+ this.gainTableLayoutPanel.Controls.Add(this.gainTrackBar, 0, 1);
+ this.gainTableLayoutPanel.Dock = DockStyle.Top;
+ this.gainTableLayoutPanel.Location = new Point(0, 169);
+ this.gainTableLayoutPanel.Name = "gainTableLayoutPanel";
+ this.gainTableLayoutPanel.RowCount = 2;
+ this.gainTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.gainTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
+ this.gainTableLayoutPanel.Size = new Size(202, 57);
+ this.gainTableLayoutPanel.TabIndex = 66;
+ this.gainLabel.Anchor = AnchorStyles.Right;
+ this.gainLabel.AutoSize = true;
+ this.gainLabel.Location = new Point(180, 0);
+ this.gainLabel.Name = "gainLabel";
+ this.gainLabel.Size = new Size(19, 13);
+ this.gainLabel.TabIndex = 1;
+ this.gainLabel.Text = "10";
+ this.label2.Anchor = AnchorStyles.Left;
+ this.label2.AutoSize = true;
+ this.label2.Location = new Point(3, 0);
+ this.label2.Name = "label2";
+ this.label2.Size = new Size(29, 13);
+ this.label2.TabIndex = 0;
+ this.label2.Text = "Gain";
+ this.gainTrackBar.Anchor = (AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right);
+ this.gainTableLayoutPanel.SetColumnSpan(this.gainTrackBar, 2);
+ this.gainTrackBar.Location = new Point(3, 16);
+ this.gainTrackBar.Name = "gainTrackBar";
+ this.gainTrackBar.Size = new Size(196, 38);
+ this.gainTrackBar.TabIndex = 2;
+ this.gainTrackBar.ValueChanged += this.gainTrackBar_ValueChanged;
+ this.deviceInfoTableLayoutPanel.ColumnCount = 2;
+ this.deviceInfoTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50f));
+ this.deviceInfoTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50f));
+ this.deviceInfoTableLayoutPanel.Controls.Add(this.deviceSerialLabel, 0, 0);
+ this.deviceInfoTableLayoutPanel.Controls.Add(this.deviceNameLabel, 0, 0);
+ this.deviceInfoTableLayoutPanel.Controls.Add(this.useFullIQCheckBox, 0, 2);
+ this.deviceInfoTableLayoutPanel.Controls.Add(this.bitrateLabel, 1, 1);
+ this.deviceInfoTableLayoutPanel.Controls.Add(this.serverVersionLabel, 0, 1);
+ this.deviceInfoTableLayoutPanel.Dock = DockStyle.Top;
+ this.deviceInfoTableLayoutPanel.Location = new Point(0, 30);
+ this.deviceInfoTableLayoutPanel.Name = "deviceInfoTableLayoutPanel";
+ this.deviceInfoTableLayoutPanel.RowCount = 3;
+ this.deviceInfoTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 33.33333f));
+ this.deviceInfoTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 33.33333f));
+ this.deviceInfoTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 33.33333f));
+ this.deviceInfoTableLayoutPanel.Size = new Size(202, 76);
+ this.deviceInfoTableLayoutPanel.TabIndex = 67;
+ this.bitrateLabel.Anchor = AnchorStyles.Right;
+ this.bitrateLabel.AutoSize = true;
+ this.bitrateLabel.Location = new Point(160, 31);
+ this.bitrateLabel.Name = "bitrateLabel";
+ this.bitrateLabel.Size = new Size(39, 13);
+ this.bitrateLabel.TabIndex = 4;
+ this.bitrateLabel.Text = "0 kbps";
+ this.deviceSerialLabel.Anchor = AnchorStyles.Right;
+ this.deviceSerialLabel.AutoSize = true;
+ this.deviceSerialLabel.Location = new Point(165, 6);
+ this.deviceSerialLabel.Name = "deviceSerialLabel";
+ this.deviceSerialLabel.Size = new Size(34, 13);
+ this.deviceSerialLabel.TabIndex = 2;
+ this.deviceSerialLabel.Text = "SN: 0";
+ this.deviceNameLabel.Anchor = AnchorStyles.Left;
+ this.deviceNameLabel.AutoSize = true;
+ this.deviceNameLabel.Location = new Point(3, 6);
+ this.deviceNameLabel.Name = "deviceNameLabel";
+ this.deviceNameLabel.Size = new Size(58, 13);
+ this.deviceNameLabel.TabIndex = 1;
+ this.deviceNameLabel.Text = "Airspy One";
+ this.useFullIQCheckBox.AutoSize = true;
+ this.useFullIQCheckBox.Location = new Point(3, 53);
+ this.useFullIQCheckBox.Name = "useFullIQCheckBox";
+ this.useFullIQCheckBox.Size = new Size(75, 17);
+ this.useFullIQCheckBox.TabIndex = 3;
+ this.useFullIQCheckBox.Text = "Use full IQ";
+ this.useFullIQCheckBox.UseVisualStyleBackColor = true;
+ this.useFullIQCheckBox.CheckStateChanged += this.useFullIQCheckBox_CheckStateChanged;
+ this.downStreamTimer.Enabled = true;
+ this.downStreamTimer.Interval = 500;
+ this.downStreamTimer.Tick += this.downStreamTimer_Tick;
+ this.iqFormatTableLayoutPanel.ColumnCount = 2;
+ this.iqFormatTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 40f));
+ this.iqFormatTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 60f));
+ this.iqFormatTableLayoutPanel.Controls.Add(this.label3, 0, 0);
+ this.iqFormatTableLayoutPanel.Controls.Add(this.streamFormatComboBox, 1, 0);
+ this.iqFormatTableLayoutPanel.Dock = DockStyle.Top;
+ this.iqFormatTableLayoutPanel.Location = new Point(0, 137);
+ this.iqFormatTableLayoutPanel.Name = "iqFormatTableLayoutPanel";
+ this.iqFormatTableLayoutPanel.RowCount = 1;
+ this.iqFormatTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
+ this.iqFormatTableLayoutPanel.Size = new Size(202, 32);
+ this.iqFormatTableLayoutPanel.TabIndex = 68;
+ this.serverVersionLabel.Anchor = AnchorStyles.Left;
+ this.serverVersionLabel.AutoSize = true;
+ this.serverVersionLabel.Location = new Point(3, 31);
+ this.serverVersionLabel.Name = "serverVersionLabel";
+ this.serverVersionLabel.Size = new Size(86, 13);
+ this.serverVersionLabel.TabIndex = 5;
+ this.serverVersionLabel.Text = "Server: 2.0.1606";
+ base.AutoScaleDimensions = new SizeF(6f, 13f);
+ base.AutoScaleMode = AutoScaleMode.Font;
+ base.Controls.Add(this.gainTableLayoutPanel);
+ base.Controls.Add(this.iqFormatTableLayoutPanel);
+ base.Controls.Add(this.bandwidthTableLayoutPanel);
+ base.Controls.Add(this.deviceInfoTableLayoutPanel);
+ base.Controls.Add(this.hostTableLayoutPanel);
+ base.Name = "ControllerPanel";
+ base.Size = new Size(202, 30);
+ this.hostTableLayoutPanel.ResumeLayout(false);
+ this.bandwidthTableLayoutPanel.ResumeLayout(false);
+ this.bandwidthTableLayoutPanel.PerformLayout();
+ this.gainTableLayoutPanel.ResumeLayout(false);
+ this.gainTableLayoutPanel.PerformLayout();
+ ((ISupportInitialize)this.gainTrackBar).EndInit();
+ this.deviceInfoTableLayoutPanel.ResumeLayout(false);
+ this.deviceInfoTableLayoutPanel.PerformLayout();
+ this.iqFormatTableLayoutPanel.ResumeLayout(false);
+ this.iqFormatTableLayoutPanel.PerformLayout();
+ base.ResumeLayout(false);
+ }
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.SpyServer/ControllerPanel.resx b/SDRSharp/SDRSharp.FrontEnds.SpyServer/ControllerPanel.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.SpyServer/ControllerPanel.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/SDRSharp/SDRSharp.FrontEnds.SpyServer/DeviceInfo.cs b/SDRSharp/SDRSharp.FrontEnds.SpyServer/DeviceInfo.cs
new file mode 100644
index 0000000..6da5c8e
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.SpyServer/DeviceInfo.cs
@@ -0,0 +1,29 @@
+namespace SDRSharp.FrontEnds.SpyServer
+{
+ public struct DeviceInfo
+ {
+ public DeviceType DeviceType;
+
+ public uint DeviceSerial;
+
+ public uint MaximumSampleRate;
+
+ public uint MaximumBandwidth;
+
+ public uint DecimationStageCount;
+
+ public uint GainStageCount;
+
+ public uint MaximumGainIndex;
+
+ public uint MinimumFrequency;
+
+ public uint MaximumFrequency;
+
+ public uint Resolution;
+
+ public uint MinimumIQDecimation;
+
+ public uint ForcedIQFormat;
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.SpyServer/DeviceType.cs b/SDRSharp/SDRSharp.FrontEnds.SpyServer/DeviceType.cs
new file mode 100644
index 0000000..ce5c546
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.SpyServer/DeviceType.cs
@@ -0,0 +1,10 @@
+namespace SDRSharp.FrontEnds.SpyServer
+{
+ public enum DeviceType : uint
+ {
+ DEVICE_INVALID,
+ DEVICE_AIRSPY_ONE,
+ DEVICE_AIRSPY_HF,
+ DEVICE_RTLSDR
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.SpyServer/MessageHeader.cs b/SDRSharp/SDRSharp.FrontEnds.SpyServer/MessageHeader.cs
new file mode 100644
index 0000000..a56565c
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.SpyServer/MessageHeader.cs
@@ -0,0 +1,15 @@
+namespace SDRSharp.FrontEnds.SpyServer
+{
+ public struct MessageHeader
+ {
+ public uint ProtocolID;
+
+ public MessageType MessageType;
+
+ public StreamType StreamType;
+
+ public uint SequenceNumber;
+
+ public uint BodySize;
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.SpyServer/MessageType.cs b/SDRSharp/SDRSharp.FrontEnds.SpyServer/MessageType.cs
new file mode 100644
index 0000000..c59b41d
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.SpyServer/MessageType.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace SDRSharp.FrontEnds.SpyServer
+{
+ [Flags]
+ public enum MessageType : uint
+ {
+ MSG_TYPE_DEVICE_INFO = 0u,
+ MSG_TYPE_CLIENT_SYNC = 1u,
+ MSG_TYPE_PONG = 2u,
+ MSG_TYPE_READ_SETTING = 3u,
+ MSG_TYPE_UINT8_IQ = 0x64,
+ MSG_TYPE_INT16_IQ = 0x65,
+ MSG_TYPE_INT24_IQ = 0x66,
+ MSG_TYPE_FLOAT_IQ = 0x67,
+ MSG_TYPE_UINT8_AF = 0xC8,
+ MSG_TYPE_INT16_AF = 0xC9,
+ MSG_TYPE_INT24_AF = 0xCA,
+ MSG_TYPE_FLOAT_AF = 0xCB,
+ MSG_TYPE_DINT4_FFT = 0x12C,
+ MSG_TYPE_UINT8_FFT = 0x12D
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.SpyServer/SettingTarget.cs b/SDRSharp/SDRSharp.FrontEnds.SpyServer/SettingTarget.cs
new file mode 100644
index 0000000..87e6728
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.SpyServer/SettingTarget.cs
@@ -0,0 +1,9 @@
+namespace SDRSharp.FrontEnds.SpyServer
+{
+ public struct SettingTarget
+ {
+ public StreamType StreamType;
+
+ public SettingType SettingType;
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.SpyServer/SettingType.cs b/SDRSharp/SDRSharp.FrontEnds.SpyServer/SettingType.cs
new file mode 100644
index 0000000..f14d85a
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.SpyServer/SettingType.cs
@@ -0,0 +1,19 @@
+namespace SDRSharp.FrontEnds.SpyServer
+{
+ public enum SettingType : uint
+ {
+ SETTING_STREAMING_MODE,
+ SETTING_STREAMING_ENABLED,
+ SETTING_GAIN,
+ SETTING_IQ_FORMAT = 100u,
+ SETTING_IQ_FREQUENCY,
+ SETTING_IQ_DECIMATION,
+ SETTING_IQ_DIGITAL_GAIN,
+ SETTING_FFT_FORMAT = 200u,
+ SETTING_FFT_FREQUENCY,
+ SETTING_FFT_DECIMATION,
+ SETTING_FFT_DB_OFFSET,
+ SETTING_FFT_DB_RANGE,
+ SETTING_FFT_DISPLAY_PIXELS
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.SpyServer/SpyClient.cs b/SDRSharp/SDRSharp.FrontEnds.SpyServer/SpyClient.cs
new file mode 100644
index 0000000..9ecc87b
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.SpyServer/SpyClient.cs
@@ -0,0 +1,1074 @@
+using SDRSharp.Common;
+using SDRSharp.Radio;
+using SDRSharp.Radio.PortAudio;
+using System;
+using System.Net.Sockets;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Windows.Forms;
+
+namespace SDRSharp.FrontEnds.SpyServer
+{
+ public class SpyClient : IDisposable
+ {
+ private enum ParserPhase
+ {
+ AcquiringHeader,
+ ReadingData
+ }
+
+ private const float TimeConst = 0.05f;
+
+ private const int BufferSize = 65536;
+
+ private const int DefaultDisplayPixels = 1000;
+
+ private const int DefaultFFTRange = 127;
+
+ private Socket _s;
+
+ private Thread _receiveThread;
+
+ private bool _terminated;
+
+ private bool _streaming;
+
+ private StreamingMode _streamingMode = StreamingMode.STREAM_MODE_FFT_IQ;
+
+ private StreamFormat _streamFormat = StreamFormat.STREAM_FORMAT_UINT8;
+
+ private bool _optimizePropertyChanges = true;
+
+ private uint _channelCenterFrequency;
+
+ private uint _displayCenterFrequency;
+
+ private uint _deviceCenterFrequency;
+
+ private int _displayDecimationStageCount;
+
+ private int _channelDecimationStageCount;
+
+ private uint _minimumTunableFrequency;
+
+ private uint _maximumTunableFrequency;
+
+ private uint _autoGainDecibels;
+
+ private int _gain;
+
+ private int _fftOffset;
+
+ private int _fftRange = 127;
+
+ private int _displayPixels = 1000;
+
+ private int _messageSize;
+
+ private uint _lastSequenceNumber = uint.MaxValue;
+
+ private uint _droppedBuffers;
+
+ private long _down_stream_bytes;
+
+ private bool _gotDeviceInfo;
+
+ private bool _gotSyncInfo;
+
+ private bool _canControl;
+
+ private Exception _error;
+
+ private DeviceInfo _deviceInfo;
+
+ private ParserPhase _parserPhase;
+
+ private int _parserPosition;
+
+ private unsafe byte[] _headerData = new byte[sizeof(MessageHeader)];
+
+ private MessageHeader _header;
+
+ private UnsafeBuffer _bodyBuffer;
+
+ private UnsafeBuffer _uncompressedBuffer;
+
+ private UnsafeBuffer _messageBuffer;
+
+ private UnsafeBuffer _iqBuffer;
+
+ private string _serverVersion;
+
+ public string ServerVersion
+ {
+ get
+ {
+ return this._serverVersion;
+ }
+ }
+
+ public bool OptimizePropertyChanges
+ {
+ get
+ {
+ return this._optimizePropertyChanges;
+ }
+ set
+ {
+ this._optimizePropertyChanges = value;
+ }
+ }
+
+ public bool IsConnected
+ {
+ get
+ {
+ if (!this._terminated && this._s != null)
+ {
+ return this._s.Connected;
+ }
+ return false;
+ }
+ }
+
+ public bool IsSynchronized
+ {
+ get
+ {
+ if (this._gotDeviceInfo)
+ {
+ return this._gotSyncInfo;
+ }
+ return false;
+ }
+ }
+
+ public string DeviceName
+ {
+ get
+ {
+ return Constants.GetDeviceName(this._deviceInfo.DeviceType);
+ }
+ }
+
+ public uint DeviceSerial
+ {
+ get
+ {
+ return this._deviceInfo.DeviceSerial;
+ }
+ }
+
+ public uint MaximumDecimationStageCount
+ {
+ get
+ {
+ return this._deviceInfo.DecimationStageCount;
+ }
+ }
+
+ public uint MaximumBandwidth
+ {
+ get
+ {
+ return this._deviceInfo.MaximumBandwidth;
+ }
+ }
+
+ public uint MaximumSampleRate
+ {
+ get
+ {
+ return this._deviceInfo.MaximumSampleRate;
+ }
+ }
+
+ public uint MaximumGainIndex
+ {
+ get
+ {
+ return this._deviceInfo.MaximumGainIndex;
+ }
+ }
+
+ public double ChannelSamplerate
+ {
+ get
+ {
+ return (double)this._deviceInfo.MaximumSampleRate / (double)(1 << this._channelDecimationStageCount);
+ }
+ }
+
+ public int DisplayBandwidth
+ {
+ get
+ {
+ return (int)((double)this._deviceInfo.MaximumBandwidth / (double)(1 << this._displayDecimationStageCount));
+ }
+ }
+
+ public int ChannelBandwidth
+ {
+ get
+ {
+ return (int)((double)this._deviceInfo.MaximumBandwidth / (double)(1 << this._channelDecimationStageCount));
+ }
+ }
+
+ public bool Is8bitForced
+ {
+ get
+ {
+ return this._deviceInfo.ForcedIQFormat == 1;
+ }
+ }
+
+ public int MinimumIQDecimation
+ {
+ get
+ {
+ return (int)this._deviceInfo.MinimumIQDecimation;
+ }
+ }
+
+ public bool CanControl
+ {
+ get
+ {
+ return this._canControl;
+ }
+ }
+
+ public uint MaximumTunableFrequency
+ {
+ get
+ {
+ return this._maximumTunableFrequency;
+ }
+ }
+
+ public uint MinimumTunableFrequency
+ {
+ get
+ {
+ return this._minimumTunableFrequency;
+ }
+ }
+
+ public uint DeviceCenterFrequency
+ {
+ get
+ {
+ return this._deviceCenterFrequency;
+ }
+ }
+
+ public StreamingMode StreamingMode
+ {
+ get
+ {
+ return this._streamingMode;
+ }
+ set
+ {
+ if (this._streamingMode == value && this._optimizePropertyChanges)
+ {
+ return;
+ }
+ this._streamingMode = value;
+ this.SetSetting(SettingType.SETTING_STREAMING_MODE, (uint)this._streamingMode);
+ }
+ }
+
+ public StreamFormat StreamFormat
+ {
+ get
+ {
+ return this._streamFormat;
+ }
+ set
+ {
+ if (this._streamFormat == value && this._optimizePropertyChanges)
+ {
+ return;
+ }
+ this._streamFormat = value;
+ this.UpdateIQFormat();
+ }
+ }
+
+ public uint DisplayCenterFrequency
+ {
+ get
+ {
+ return this._displayCenterFrequency;
+ }
+ set
+ {
+ if (this._canControl)
+ {
+ this._deviceCenterFrequency = value;
+ }
+ if (this._displayCenterFrequency == value && this._optimizePropertyChanges)
+ {
+ return;
+ }
+ this._displayCenterFrequency = value;
+ this.SetSetting(SettingType.SETTING_FFT_FREQUENCY, this._displayCenterFrequency);
+ }
+ }
+
+ public uint ChannelCenterFrequency
+ {
+ get
+ {
+ return this._channelCenterFrequency;
+ }
+ set
+ {
+ if (this._channelCenterFrequency == value && this._optimizePropertyChanges)
+ {
+ return;
+ }
+ this._channelCenterFrequency = value;
+ this.SetSetting(SettingType.SETTING_IQ_FREQUENCY, this._channelCenterFrequency);
+ }
+ }
+
+ public int Gain
+ {
+ get
+ {
+ return this._gain;
+ }
+ set
+ {
+ if (this._gain == value && this._optimizePropertyChanges)
+ {
+ return;
+ }
+ this._gain = value;
+ this.SetSetting(SettingType.SETTING_GAIN, (uint)this._gain);
+ }
+ }
+
+ public int DisplayDecimationStageCount
+ {
+ get
+ {
+ return this._displayDecimationStageCount;
+ }
+ set
+ {
+ if (this._displayDecimationStageCount == value && this._optimizePropertyChanges)
+ {
+ return;
+ }
+ this._displayDecimationStageCount = value;
+ this.SetSetting(SettingType.SETTING_FFT_DECIMATION, (uint)this._displayDecimationStageCount);
+ }
+ }
+
+ public int ChannelDecimationStageCount
+ {
+ get
+ {
+ return this._channelDecimationStageCount;
+ }
+ set
+ {
+ if (this._channelDecimationStageCount == value && this._optimizePropertyChanges)
+ {
+ return;
+ }
+ this._channelDecimationStageCount = value;
+ this.SetSetting(SettingType.SETTING_IQ_DECIMATION, (uint)this._channelDecimationStageCount);
+ this.UpdateIQFormat();
+ }
+ }
+
+ public int FFTRange
+ {
+ get
+ {
+ return this._fftRange;
+ }
+ set
+ {
+ if (this._fftRange == value && this._optimizePropertyChanges)
+ {
+ return;
+ }
+ this._fftRange = value;
+ this.SetSetting(SettingType.SETTING_FFT_DB_RANGE, (uint)this._fftRange);
+ }
+ }
+
+ public int FFTOffset
+ {
+ get
+ {
+ return this._fftOffset;
+ }
+ set
+ {
+ if (this._fftOffset == value && this._optimizePropertyChanges)
+ {
+ return;
+ }
+ this._fftOffset = value;
+ this.SetSetting(SettingType.SETTING_FFT_DB_OFFSET, (uint)this._fftOffset);
+ }
+ }
+
+ public int DisplayPixels
+ {
+ get
+ {
+ return this._displayPixels;
+ }
+ set
+ {
+ if (this._displayPixels == value && this._optimizePropertyChanges)
+ {
+ return;
+ }
+ this._displayPixels = value;
+ this.SetSetting(SettingType.SETTING_FFT_DISPLAY_PIXELS, (uint)this._displayPixels);
+ }
+ }
+
+ public event EventHandler Connected;
+
+ public event EventHandler Disconnected;
+
+ public event EventHandler Synchronized;
+
+ public event SamplesAvailableDelegate SamplesAvailable;
+
+ public event SamplesAvailableDelegate FFTAvailable;
+
+ public void Dispose()
+ {
+ this.Disconnect();
+ }
+
+ public void Connect(string host, int port)
+ {
+ if (this._receiveThread == null)
+ {
+ this._s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
+ {
+ NoDelay = true
+ };
+ this._s.Connect(host, port);
+ this._s.Blocking = false;
+ this.SayHello();
+ this.Cleanup();
+ this._error = null;
+ this._terminated = false;
+ this._receiveThread = new Thread(this.ThreadProc)
+ {
+ Name = "Spy Server Receive Thread"
+ };
+ this._receiveThread.Start();
+ for (int i = 0; i < 2000; i++)
+ {
+ if (this._error != null)
+ {
+ break;
+ }
+ if (this._gotDeviceInfo)
+ {
+ if (this._deviceInfo.DeviceType == DeviceType.DEVICE_INVALID)
+ {
+ this._error = new ApplicationException("Server is up but no device is available.");
+ break;
+ }
+ if (this._gotSyncInfo)
+ {
+ this.OnConnect();
+ return;
+ }
+ }
+ Thread.Sleep(1);
+ Application.DoEvents();
+ }
+ this.Disconnect();
+ if (this._error != null)
+ {
+ Exception error = this._error;
+ this._error = null;
+ throw error;
+ }
+ throw new ApplicationException("Server didn't send the device capability and synchronization info.");
+ }
+ }
+
+ public void Disconnect()
+ {
+ this._terminated = true;
+ if (this._s != null)
+ {
+ this._s.Close();
+ this._s = null;
+ }
+ Thread receiveThread = this._receiveThread;
+ if (receiveThread != null)
+ {
+ receiveThread.Join();
+ this._receiveThread = null;
+ }
+ this.Cleanup();
+ }
+
+ private void OnConnect()
+ {
+ this.SetSetting(SettingType.SETTING_STREAMING_MODE, (uint)this._streamingMode);
+ this.SetSetting(SettingType.SETTING_FFT_DISPLAY_PIXELS, (uint)this._displayPixels);
+ this.SetSetting(SettingType.SETTING_FFT_DB_OFFSET, (uint)this._fftOffset);
+ this.SetSetting(SettingType.SETTING_FFT_DB_RANGE, (uint)this._fftRange);
+ if (!Utils.GetBooleanSetting("spyserver.disableAutoScaling"))
+ {
+ this.SetSetting(SettingType.SETTING_IQ_DIGITAL_GAIN, uint.MaxValue);
+ }
+ this.UpdateFFTFormat();
+ this.UpdateIQFormat();
+ }
+
+ public void StartStreaming()
+ {
+ if (!this._streaming)
+ {
+ this._streaming = true;
+ this._down_stream_bytes = 0L;
+ this.SetStreamState();
+ }
+ }
+
+ public void StopStreaming()
+ {
+ if (this._streaming)
+ {
+ this._streaming = false;
+ this._down_stream_bytes = 0L;
+ this.SetStreamState();
+ }
+ }
+
+ private void Cleanup()
+ {
+ this._deviceInfo.DeviceType = DeviceType.DEVICE_INVALID;
+ this._deviceInfo.DeviceSerial = 0u;
+ this._deviceInfo.DecimationStageCount = 0u;
+ this._deviceInfo.GainStageCount = 0u;
+ this._deviceInfo.MaximumSampleRate = 0u;
+ this._deviceInfo.MaximumBandwidth = 0u;
+ this._deviceInfo.MaximumGainIndex = 0u;
+ this._deviceInfo.MinimumFrequency = 0u;
+ this._deviceInfo.MaximumFrequency = 0u;
+ this._deviceInfo.Resolution = 0u;
+ this._deviceInfo.MinimumIQDecimation = 0u;
+ this._deviceInfo.ForcedIQFormat = 0u;
+ this._gain = 0;
+ this._displayCenterFrequency = 0u;
+ this._deviceCenterFrequency = 0u;
+ this._displayDecimationStageCount = 0;
+ this._channelDecimationStageCount = 0;
+ this._minimumTunableFrequency = 0u;
+ this._maximumTunableFrequency = 0u;
+ this._canControl = false;
+ this._gotDeviceInfo = false;
+ this._gotSyncInfo = false;
+ this._lastSequenceNumber = uint.MaxValue;
+ this._droppedBuffers = 0u;
+ this._down_stream_bytes = 0L;
+ this._parserPhase = ParserPhase.AcquiringHeader;
+ this._parserPosition = 0;
+ this._streaming = false;
+ this._terminated = true;
+ }
+
+ private void UpdateFFTFormat()
+ {
+ this.SetSetting(SettingType.SETTING_FFT_FORMAT, 1u);
+ }
+
+ private void UpdateIQFormat()
+ {
+ this.SetSetting(SettingType.SETTING_IQ_FORMAT, (uint)this._streamFormat);
+ }
+
+ private bool SetStreamState()
+ {
+ return this.SetSetting(SettingType.SETTING_STREAMING_ENABLED, (uint)(this._streaming ? 1 : 0));
+ }
+
+ private unsafe bool SetSetting(SettingType setting, params uint[] args)
+ {
+ byte[] array;
+ if (args != null && args.Length != 0)
+ {
+ array = new byte[4 + args.Length * 4];
+ byte* ptr = (byte*)(&setting);
+ for (int i = 0; i < 4; i++)
+ {
+ array[i] = ptr[i];
+ }
+ Buffer.BlockCopy(args, 0, array, 4, args.Length * 4);
+ }
+ else
+ {
+ array = null;
+ }
+ return this.SendCommand(CommandType.CMD_SET_SETTING, array);
+ }
+
+ private bool SayHello()
+ {
+ byte[] bytes = BitConverter.GetBytes(33556024u);
+ string s = string.Format("SDR# v{0} on {1}", Assembly.GetEntryAssembly().GetName().Version, Environment.OSVersion);
+ byte[] bytes2 = Encoding.ASCII.GetBytes(s);
+ byte[] array = new byte[bytes.Length + bytes2.Length];
+ Buffer.BlockCopy(bytes, 0, array, 0, bytes.Length);
+ Buffer.BlockCopy(bytes2, 0, array, bytes.Length, bytes2.Length);
+ return this.SendCommand(CommandType.CMD_HELLO, array);
+ }
+
+ private unsafe bool SendCommand(CommandType cmd, byte[] args)
+ {
+ if (this._s == null)
+ {
+ return false;
+ }
+ int num = sizeof(CommandHeader);
+ int num2 = (args != null) ? args.Length : 0;
+ byte[] array = new byte[num + num2];
+ CommandHeader commandHeader = default(CommandHeader);
+ commandHeader.CommandType = cmd;
+ commandHeader.BodySize = (ushort)num2;
+ byte* ptr = (byte*)(&commandHeader);
+ for (int i = 0; i < sizeof(CommandHeader); i++)
+ {
+ array[i] = ptr[i];
+ }
+ if (args != null)
+ {
+ byte[] array2 = array;
+ fixed (byte* ptr2 = array2)
+ {
+ byte* ptr3 = ptr2 + num;
+ for (int j = 0; j < args.Length; j++)
+ {
+ ptr3[j] = args[j];
+ }
+ }
+ }
+ try
+ {
+ this._s.Send(array);
+ }
+ catch
+ {
+ return false;
+ }
+ return true;
+ }
+
+ private unsafe void ThreadProc()
+ {
+ EventHandler connected = this.Connected;
+ if (connected != null)
+ {
+ connected(this, EventArgs.Empty);
+ }
+ this._parserPhase = ParserPhase.AcquiringHeader;
+ this._parserPosition = 0;
+ byte[] array = new byte[65536];
+ byte[] array2 = array;
+ fixed (byte* buffer = array2)
+ {
+ try
+ {
+ while (!this._terminated)
+ {
+ Socket s = this._s;
+ if (s != null && s.Poll(1000000, SelectMode.SelectRead))
+ {
+ if (this._terminated)
+ {
+ break;
+ }
+ int num = s.Receive(array, 0, array.Length, SocketFlags.None);
+ if (num > 0)
+ {
+ this.ParseMessage(buffer, num);
+ continue;
+ }
+ throw new ApplicationException("Device got disconnected");
+ }
+ }
+ }
+ catch (Exception error)
+ {
+ Exception ex = this._error = error;
+ }
+ }
+ if (this._bodyBuffer != null)
+ {
+ this._bodyBuffer.Dispose();
+ this._bodyBuffer = null;
+ }
+ if (this._uncompressedBuffer != null)
+ {
+ this._uncompressedBuffer.Dispose();
+ this._uncompressedBuffer = null;
+ }
+ this._messageBuffer = null;
+ this.Cleanup();
+ this._receiveThread = null;
+ EventHandler disconnected = this.Disconnected;
+ if (disconnected != null)
+ {
+ disconnected(this, EventArgs.Empty);
+ }
+ }
+
+ private unsafe int ParseHeader(byte* buffer, int length)
+ {
+ int num = 0;
+ byte[] headerData = this._headerData;
+ fixed (byte* ptr = headerData)
+ {
+ while (length > 0)
+ {
+ int num2 = Math.Min(sizeof(MessageHeader) - this._parserPosition, length);
+ Utils.Memcpy(ptr + this._parserPosition, buffer, num2);
+ length -= num2;
+ buffer += num2;
+ this._parserPosition += num2;
+ num += num2;
+ if (this._parserPosition == sizeof(MessageHeader))
+ {
+ this._parserPosition = 0;
+ MessageHeader messageHeader = default(MessageHeader);
+ Utils.Memcpy(&messageHeader, ptr, sizeof(MessageHeader));
+ this._header = messageHeader;
+ if (messageHeader.BodySize != 0)
+ {
+ this._parserPhase = ParserPhase.ReadingData;
+ }
+ return num;
+ }
+ }
+ }
+ return num;
+ }
+
+ private unsafe int ParseBody(byte* buffer, int length)
+ {
+ int num = 0;
+ byte* ptr = (byte*)(void*)this._bodyBuffer;
+ while (length > 0)
+ {
+ int num2 = Math.Min((int)this._header.BodySize - this._parserPosition, length);
+ Utils.Memcpy(ptr + this._parserPosition, buffer, num2);
+ length -= num2;
+ buffer += num2;
+ this._parserPosition += num2;
+ num += num2;
+ if (this._parserPosition == this._header.BodySize)
+ {
+ this._parserPosition = 0;
+ this._parserPhase = ParserPhase.AcquiringHeader;
+ return num;
+ }
+ }
+ return num;
+ }
+
+ public long GetDownstreamBytes()
+ {
+ return Interlocked.Exchange(ref this._down_stream_bytes, 0L);
+ }
+
+ private unsafe void ParseMessage(byte* buffer, int len)
+ {
+ Interlocked.Add(ref this._down_stream_bytes, len);
+ while (true)
+ {
+ if (len > 0 && !this._terminated)
+ {
+ if (this._parserPhase == ParserPhase.AcquiringHeader)
+ {
+ while (this._parserPhase == ParserPhase.AcquiringHeader && len > 0)
+ {
+ int num = this.ParseHeader(buffer, len);
+ buffer += num;
+ len -= num;
+ }
+ if (this._parserPhase == ParserPhase.ReadingData)
+ {
+ byte b = 2;
+ byte b2 = 0;
+ ushort num2 = 1592;
+ byte b3 = (byte)(this._header.ProtocolID >> 24);
+ byte b4 = (byte)(this._header.ProtocolID >> 16 & 0xFF);
+ ushort num3 = (ushort)(this._header.ProtocolID & 0xFFFF);
+ this._serverVersion = string.Format("{0}.{1}.{2}", b3, b4, num3);
+ if (b == b3 && b2 == b4)
+ {
+ if (this._header.BodySize <= 1048576)
+ {
+ if (this._bodyBuffer == null || this._bodyBuffer.Length < this._header.BodySize)
+ {
+ if (this._bodyBuffer != null)
+ {
+ this._bodyBuffer.Dispose();
+ }
+ this._bodyBuffer = UnsafeBuffer.Create((int)this._header.BodySize);
+ }
+ goto IL_017b;
+ }
+ break;
+ }
+ string message = string.Format("Server is running an unsupported protocol version.\r\nExpected {0}.{1}.* but got {3}.{4}.{5}.", b, b2, num2, b3, b4, num3);
+ throw new ApplicationException(message);
+ }
+ }
+ goto IL_017b;
+ }
+ return;
+ IL_017b:
+ if (this._parserPhase == ParserPhase.ReadingData)
+ {
+ int num = this.ParseBody(buffer, len);
+ buffer += num;
+ len -= num;
+ if (this._parserPhase == ParserPhase.AcquiringHeader)
+ {
+ if (this._header.StreamType == StreamType.STREAM_TYPE_IQ)
+ {
+ uint num4 = this._header.SequenceNumber - this._lastSequenceNumber - 1;
+ this._lastSequenceNumber = this._header.SequenceNumber;
+ this._droppedBuffers += num4;
+ }
+ this.HandleNewMessage();
+ }
+ }
+ }
+ throw new ApplicationException("The server is probably buggy");
+ }
+
+ private void HandleNewMessage()
+ {
+ if (!this._terminated)
+ {
+ this._messageBuffer = this._bodyBuffer;
+ this._messageSize = (int)this._header.BodySize;
+ switch (this._header.MessageType & (MessageType)65535u)
+ {
+ case MessageType.MSG_TYPE_DEVICE_INFO:
+ this.ProcessDeviceInfo();
+ break;
+ case MessageType.MSG_TYPE_CLIENT_SYNC:
+ this.ProcessClientSync();
+ break;
+ case MessageType.MSG_TYPE_UINT8_IQ:
+ this._autoGainDecibels = (uint)this._header.MessageType >> 16;
+ this.ProcessUInt8Samples();
+ break;
+ case MessageType.MSG_TYPE_INT16_IQ:
+ this._autoGainDecibels = (uint)this._header.MessageType >> 16;
+ this.ProcessInt16Samples();
+ break;
+ case MessageType.MSG_TYPE_INT24_IQ:
+ this._autoGainDecibels = (uint)this._header.MessageType >> 16;
+ this.ProcessInt24Samples();
+ break;
+ case MessageType.MSG_TYPE_FLOAT_IQ:
+ this._autoGainDecibels = (uint)this._header.MessageType >> 16;
+ this.ProcessFloatSamples();
+ break;
+ case MessageType.MSG_TYPE_UINT8_FFT:
+ this.ProcessUInt8FFT();
+ break;
+ }
+ }
+ }
+
+ private unsafe void ProcessDeviceInfo()
+ {
+ DeviceInfo deviceInfo = default(DeviceInfo);
+ Utils.Memcpy(&deviceInfo, this._messageBuffer, Math.Min(this._messageSize, sizeof(DeviceInfo)));
+ this._deviceInfo = deviceInfo;
+ if (this._deviceInfo.Resolution == 0)
+ {
+ switch (this._deviceInfo.DeviceType)
+ {
+ case DeviceType.DEVICE_AIRSPY_HF:
+ this._deviceInfo.Resolution = 16u;
+ break;
+ case DeviceType.DEVICE_AIRSPY_ONE:
+ this._deviceInfo.Resolution = 12u;
+ break;
+ case DeviceType.DEVICE_RTLSDR:
+ this._deviceInfo.Resolution = 8u;
+ break;
+ }
+ }
+ this._minimumTunableFrequency = this._deviceInfo.MinimumFrequency;
+ this._maximumTunableFrequency = this._deviceInfo.MaximumFrequency;
+ this._gotDeviceInfo = true;
+ }
+
+ private unsafe void ProcessClientSync()
+ {
+ ClientSync clientSync = default(ClientSync);
+ Utils.Memcpy(&clientSync, this._messageBuffer, Math.Min(this._messageSize, sizeof(ClientSync)));
+ this._canControl = (clientSync.CanControl != 0);
+ this._gain = (int)clientSync.Gain;
+ this._deviceCenterFrequency = clientSync.DeviceCenterFrequency;
+ this._channelCenterFrequency = clientSync.IQCenterFrequency;
+ this._displayCenterFrequency = clientSync.FFTCenterFrequency;
+ switch (this._streamingMode)
+ {
+ case StreamingMode.STREAM_MODE_FFT_ONLY:
+ case StreamingMode.STREAM_MODE_FFT_IQ:
+ this._minimumTunableFrequency = clientSync.MinimumFFTCenterFrequency;
+ this._maximumTunableFrequency = clientSync.MaximumFFTCenterFrequency;
+ break;
+ case StreamingMode.STREAM_MODE_IQ_ONLY:
+ this._minimumTunableFrequency = clientSync.MinimumIQCenterFrequency;
+ this._maximumTunableFrequency = clientSync.MaximumIQCenterFrequency;
+ break;
+ }
+ this._gotSyncInfo = true;
+ EventHandler synchronized = this.Synchronized;
+ if (synchronized != null)
+ {
+ synchronized(this, EventArgs.Empty);
+ }
+ }
+
+ private unsafe void ProcessUInt8Samples()
+ {
+ int num = this._messageSize / 2;
+ if (this._iqBuffer == null || this._iqBuffer.Length != num)
+ {
+ this._iqBuffer = UnsafeBuffer.Create(num, sizeof(Complex));
+ }
+ byte* ptr = (byte*)(void*)this._messageBuffer;
+ Complex* ptr2 = (Complex*)(void*)this._iqBuffer;
+ for (int i = 0; i < num; i++)
+ {
+ Complex* intPtr = ptr2 + i;
+ byte* intPtr2 = ptr;
+ ptr = intPtr2 + 1;
+ intPtr->Real = ((float)(int)(*intPtr2) - 128f) * 0.0078125f;
+ Complex* intPtr3 = ptr2 + i;
+ byte* intPtr4 = ptr;
+ ptr = intPtr4 + 1;
+ intPtr3->Imag = ((float)(int)(*intPtr4) - 128f) * 0.0078125f;
+ }
+ this.PushIQData(ptr2, num);
+ }
+
+ private unsafe void ProcessInt16Samples()
+ {
+ int num = this._messageSize / 4;
+ if (this._iqBuffer == null || this._iqBuffer.Length != num)
+ {
+ this._iqBuffer = UnsafeBuffer.Create(num, sizeof(Complex));
+ }
+ short* ptr = (short*)(void*)this._messageBuffer;
+ Complex* ptr2 = (Complex*)(void*)this._iqBuffer;
+ for (int i = 0; i < num; i++)
+ {
+ Complex* intPtr = ptr2 + i;
+ short* intPtr2 = ptr;
+ ptr = intPtr2 + 1;
+ intPtr->Real = (float)(*intPtr2) * 3.05175781E-05f;
+ Complex* intPtr3 = ptr2 + i;
+ short* intPtr4 = ptr;
+ ptr = intPtr4 + 1;
+ intPtr3->Imag = (float)(*intPtr4) * 3.05175781E-05f;
+ }
+ this.PushIQData(ptr2, num);
+ }
+
+ private unsafe void ProcessInt24Samples()
+ {
+ int num = this._messageSize / 6;
+ if (this._iqBuffer == null || this._iqBuffer.Length != num)
+ {
+ this._iqBuffer = UnsafeBuffer.Create(num, sizeof(Complex));
+ }
+ Int24* ptr = (Int24*)(void*)this._messageBuffer;
+ Complex* ptr2 = (Complex*)(void*)this._iqBuffer;
+ for (int i = 0; i < num; i++)
+ {
+ Complex* intPtr = ptr2 + i;
+ Int24* ptr3 = ptr;
+ ptr = ptr3 + 1;
+ intPtr->Real = (float)(*ptr3) * 1.1920929E-07f;
+ Complex* intPtr2 = ptr2 + i;
+ ptr3 = ptr;
+ ptr = ptr3 + 1;
+ intPtr2->Imag = (float)(*ptr3) * 1.1920929E-07f;
+ }
+ this.PushIQData(ptr2, num);
+ }
+
+ private unsafe void ProcessFloatSamples()
+ {
+ int count = this._messageSize / 8;
+ Complex* samples = (Complex*)(void*)this._messageBuffer;
+ this.PushIQData(samples, count);
+ }
+
+ private unsafe void PushIQData(Complex* samples, int count)
+ {
+ ComplexSamplesEventArgs e = new ComplexSamplesEventArgs
+ {
+ Buffer = samples,
+ Length = count,
+ DroppedSamples = (uint)((int)this._droppedBuffers * count)
+ };
+ this._droppedBuffers = 0u;
+ if (this._autoGainDecibels != 0)
+ {
+ float b = (float)Math.Pow(10.0, (double)((float)(double)this._autoGainDecibels * -0.05f));
+ for (int i = 0; i < count; i++)
+ {
+ Complex* intPtr = samples + i;
+ *intPtr *= b;
+ }
+ }
+ SamplesAvailableDelegate samplesAvailable = this.SamplesAvailable;
+ if (samplesAvailable != null)
+ {
+ samplesAvailable(this, e);
+ }
+ }
+
+ private unsafe void ProcessUInt8FFT()
+ {
+ ByteSamplesEventArgs e = new ByteSamplesEventArgs
+ {
+ Buffer = (byte*)(void*)this._messageBuffer,
+ Length = this._messageSize
+ };
+ SamplesAvailableDelegate fFTAvailable = this.FFTAvailable;
+ if (fFTAvailable != null)
+ {
+ fFTAvailable(this, e);
+ }
+ }
+
+ public unsafe SpyClient()
+ {
+ }
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.SpyServer/SpyServerIO.cs b/SDRSharp/SDRSharp.FrontEnds.SpyServer/SpyServerIO.cs
new file mode 100644
index 0000000..8bc32f8
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.SpyServer/SpyServerIO.cs
@@ -0,0 +1,584 @@
+using SDRSharp.Common;
+using SDRSharp.Radio;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace SDRSharp.FrontEnds.SpyServer
+{
+ public class SpyServerIO : IFrontendController, IIQStreamController, INonBlockingController, ITunableSource, ISampleRateChangeSource, IConnectableSource, IFrontendOffset, IControlAwareObject, ISpectrumProvider, IConfigurationPanelProvider, IFFTSource, IVFOSource, IDisposable
+ {
+ private bool _disposed;
+
+ private ControllerPanel _gui;
+
+ private ISharpControl _control;
+
+ private SpyClient _client;
+
+ private SamplesAvailableDelegate _callback;
+
+ public ISharpControl Control
+ {
+ get
+ {
+ return this._control;
+ }
+ }
+
+ public bool Connected
+ {
+ get
+ {
+ if (this._client != null)
+ {
+ return this._client.IsConnected;
+ }
+ return false;
+ }
+ }
+
+ public float UsableSpectrumRatio
+ {
+ get
+ {
+ if (this._client != null && this._client.IsSynchronized)
+ {
+ return (float)(double)this._client.MaximumBandwidth / (float)(double)this._client.MaximumSampleRate;
+ }
+ return 1f;
+ }
+ }
+
+ public UserControl Gui
+ {
+ get
+ {
+ return this._gui;
+ }
+ }
+
+ public int Offset
+ {
+ get
+ {
+ return 0;
+ }
+ }
+
+ public long Frequency
+ {
+ get
+ {
+ if (this._client != null && this._client.IsSynchronized)
+ {
+ return (this._client.StreamingMode == StreamingMode.STREAM_MODE_FFT_IQ) ? this._client.DisplayCenterFrequency : this._client.ChannelCenterFrequency;
+ }
+ return 0L;
+ }
+ set
+ {
+ if (this._client != null && this._client.IsSynchronized)
+ {
+ if (this._client.StreamingMode == StreamingMode.STREAM_MODE_FFT_IQ)
+ {
+ if (this.CanTune)
+ {
+ this._client.DisplayCenterFrequency = (uint)value;
+ }
+ }
+ else
+ {
+ this._client.ChannelCenterFrequency = (uint)value;
+ }
+ }
+ }
+ }
+
+ public bool CanTune
+ {
+ get
+ {
+ if (this._client != null && this._client.IsSynchronized)
+ {
+ if (!this._client.CanControl)
+ {
+ return this._client.MaximumTunableFrequency > this._client.MinimumTunableFrequency;
+ }
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public long MinimumTunableFrequency
+ {
+ get
+ {
+ if (this._client != null && this._client.IsSynchronized)
+ {
+ return this._client.MinimumTunableFrequency;
+ }
+ return 0L;
+ }
+ }
+
+ public long MaximumTunableFrequency
+ {
+ get
+ {
+ if (this._client != null && this._client.IsSynchronized)
+ {
+ return this._client.MaximumTunableFrequency;
+ }
+ return 9223372036854775807L;
+ }
+ }
+
+ public double Samplerate
+ {
+ get
+ {
+ if (this._client != null && this._client.IsSynchronized)
+ {
+ return this._client.ChannelSamplerate;
+ }
+ return 0.0;
+ }
+ }
+
+ public int FFTRange
+ {
+ get
+ {
+ if (this._client != null && this._client.IsSynchronized)
+ {
+ return this._client.FFTRange;
+ }
+ return 0;
+ }
+ set
+ {
+ if (this._client != null && this._client.IsSynchronized)
+ {
+ this._client.FFTRange = value;
+ }
+ }
+ }
+
+ public int FFTOffset
+ {
+ get
+ {
+ if (this._client != null && this._client.IsSynchronized)
+ {
+ return this._client.FFTOffset;
+ }
+ return 0;
+ }
+ set
+ {
+ if (this._client != null && this._client.IsSynchronized)
+ {
+ this._client.FFTOffset = value;
+ }
+ }
+ }
+
+ public int DisplayPixels
+ {
+ get
+ {
+ if (this._client != null && this._client.IsSynchronized)
+ {
+ return this._client.DisplayPixels;
+ }
+ return 0;
+ }
+ set
+ {
+ if (this._client != null && this._client.IsSynchronized)
+ {
+ this._client.DisplayPixels = value;
+ }
+ }
+ }
+
+ public int DisplayBandwidth
+ {
+ get
+ {
+ if (this._client != null && this._client.IsSynchronized)
+ {
+ return this._client.DisplayBandwidth;
+ }
+ return 0;
+ }
+ }
+
+ public long VFOFrequency
+ {
+ get
+ {
+ return (this._client != null) ? this._client.ChannelCenterFrequency : 0;
+ }
+ set
+ {
+ if (this._client == null && this._client.IsSynchronized)
+ {
+ return;
+ }
+ this._client.ChannelCenterFrequency = (uint)value;
+ }
+ }
+
+ public int VFODecimation
+ {
+ get
+ {
+ if (this._client != null)
+ {
+ return this._client.ChannelDecimationStageCount;
+ }
+ return 0;
+ }
+ set
+ {
+ if (this._client != null && this._client.IsSynchronized && this._client.ChannelDecimationStageCount != value)
+ {
+ this._client.ChannelDecimationStageCount = value;
+ }
+ }
+ }
+
+ public double VFOMaxSampleRate
+ {
+ get
+ {
+ if (this._client == null)
+ {
+ return 0.0;
+ }
+ return (double)this._client.MaximumSampleRate;
+ }
+ }
+
+ public int VFOMinIQDecimation
+ {
+ get
+ {
+ if (this._client == null)
+ {
+ return 0;
+ }
+ return this._client.MinimumIQDecimation;
+ }
+ }
+
+ public bool FFTEnabled
+ {
+ get
+ {
+ if (this._client != null && this._client.IsSynchronized)
+ {
+ return this._client.StreamingMode == StreamingMode.STREAM_MODE_FFT_IQ;
+ }
+ return false;
+ }
+ set
+ {
+ if (this._client != null && this._client.IsSynchronized)
+ {
+ this._client.StreamingMode = ((!value) ? StreamingMode.STREAM_MODE_IQ_ONLY : StreamingMode.STREAM_MODE_FFT_IQ);
+ this._client.OptimizePropertyChanges = false;
+ long num = this._control.FrequencyShiftEnabled ? this._control.FrequencyShift : 0;
+ switch (this._client.StreamingMode)
+ {
+ case StreamingMode.STREAM_MODE_FFT_IQ:
+ this._client.DisplayCenterFrequency = (uint)(this._control.CenterFrequency - num);
+ this._client.ChannelCenterFrequency = (uint)(this._control.Frequency - num);
+ this._client.DisplayDecimationStageCount = this._gui.Decimation;
+ this._client.ChannelDecimationStageCount = StreamControl.GetDecimationStageCount((double)this._client.MaximumSampleRate, this._control.DetectorType);
+ break;
+ case StreamingMode.STREAM_MODE_IQ_ONLY:
+ this._client.ChannelCenterFrequency = (uint)(this._control.CenterFrequency - num);
+ this._client.ChannelDecimationStageCount = this._gui.Decimation;
+ break;
+ default:
+ throw new ApplicationException("Streaming Mode is not supported: " + value.ToString());
+ }
+ this._client.OptimizePropertyChanges = true;
+ }
+ }
+ }
+
+ public event EventHandler SampleRateChanged;
+
+ public event SamplesAvailableDelegate FFTAvailable;
+
+ public SpyServerIO()
+ {
+ this._gui = new ControllerPanel(this);
+ }
+
+ ~SpyServerIO()
+ {
+ this.Dispose();
+ }
+
+ public void Dispose()
+ {
+ if (!this._disposed)
+ {
+ this._disposed = true;
+ this.DestroyClient();
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ private void DestroyClient()
+ {
+ if (this._client != null)
+ {
+ this._client.Synchronized -= this.Client_Synchronized;
+ this._client.Disconnected -= this.Client_Disconnected;
+ this._client.SamplesAvailable -= this.Client_SamplesAvailable;
+ this._client.FFTAvailable -= this.Client_FFTAvailable;
+ this._client.Dispose();
+ this._client = null;
+ }
+ }
+
+ public void Connect()
+ {
+ if (this._client == null)
+ {
+ this._client = new SpyClient();
+ this._client.Synchronized += this.Client_Synchronized;
+ this._client.Disconnected += this.Client_Disconnected;
+ this._client.SamplesAvailable += this.Client_SamplesAvailable;
+ this._client.FFTAvailable += this.Client_FFTAvailable;
+ }
+ this._client.Connect(this._gui.Host, this._gui.Port);
+ this._control.FrequencyShiftEnabled = false;
+ this._control.ResetFrequency(this._client.ChannelCenterFrequency);
+ this.UpdateTuningBoundaries();
+ bool bandwidth = this._client.MaximumDecimationStageCount != 0;
+ List list = new List();
+ for (int i = 0; i <= this._client.MaximumDecimationStageCount; i++)
+ {
+ list.Add((int)this._client.MaximumBandwidth >> i);
+ }
+ string deviceName = this._client.DeviceName;
+ string deviceSerial = this._client.DeviceSerial.ToString("X8");
+ int maximumGainIndex = (int)this._client.MaximumGainIndex;
+ bool flag = maximumGainIndex > 0;
+ if (this._client.Is8bitForced)
+ {
+ this._gui.Force8bit();
+ }
+ this._gui.EnableURI(false);
+ this._gui.UpdateControlOptions(deviceName, deviceSerial, this._client.ServerVersion, list.ToArray(), this._client.MinimumIQDecimation, maximumGainIndex);
+ this._gui.UpdateDisplaySections(true, bandwidth, !this._client.Is8bitForced, flag);
+ this.FFTEnabled = !this._gui.UseFullIQ;
+ if (flag)
+ {
+ this._gui.UpdateGain(this._client.Gain, this._client.CanControl);
+ }
+ }
+
+ private void Client_Disconnected(object sender, EventArgs e)
+ {
+ if (this._control.IsPlaying)
+ {
+ this._control.StopRadio();
+ }
+ if (this._gui.IsHandleCreated)
+ {
+ this._gui.BeginInvoke((Action)delegate
+ {
+ this._gui.EnableURI(true);
+ this._gui.UpdateDisplaySections(false, false, false, false);
+ this._gui.EnableFullIQ(true);
+ });
+ }
+ }
+
+ private void Client_Synchronized(object sender, EventArgs e)
+ {
+ if (this._client.IsConnected)
+ {
+ this.UpdateTuningStyle();
+ this.UpdateTuningBoundaries();
+ if (this._gui.IsHandleCreated)
+ {
+ this._gui.BeginInvoke((Action)delegate
+ {
+ this._gui.UpdateGain(this._client.Gain, this._client.CanControl);
+ });
+ }
+ }
+ }
+
+ private unsafe void Client_SamplesAvailable(object sender, ComplexSamplesEventArgs e)
+ {
+ this._callback(this, e.Buffer, e.Length);
+ }
+
+ private void Client_FFTAvailable(object sender, ByteSamplesEventArgs e)
+ {
+ SamplesAvailableDelegate fFTAvailable = this.FFTAvailable;
+ if (fFTAvailable != null)
+ {
+ fFTAvailable(sender, e);
+ }
+ }
+
+ public void Disconnect()
+ {
+ this._client.Disconnect();
+ }
+
+ public void SetFormat(StreamFormat format)
+ {
+ if (this._client != null)
+ {
+ this._client.StreamFormat = format;
+ }
+ }
+
+ public long GetDownstreamBytes()
+ {
+ if (this._client == null)
+ {
+ return 0L;
+ }
+ return this._client.GetDownstreamBytes();
+ }
+
+ public void SetControl(object control)
+ {
+ this._control = (ISharpControl)control;
+ if (this._control != null)
+ {
+ this._control.PropertyChanged += this.Control_PropertyChanged;
+ }
+ }
+
+ private void Control_PropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (this._client != null)
+ {
+ string propertyName = e.PropertyName;
+ if (!(propertyName == "StartRadio"))
+ {
+ if (propertyName == "StopRadio")
+ {
+ this._gui.EnableFullIQ(this._gui.Decimation >= this._client.MinimumIQDecimation);
+ }
+ }
+ else
+ {
+ this._gui.EnableFullIQ(false);
+ }
+ }
+ }
+
+ public void Open()
+ {
+ this._gui.EnableURI(true);
+ this._control.TuningStyle = TuningStyle.Free;
+ this._control.TuningStyleFreezed = true;
+ }
+
+ public void Close()
+ {
+ if (this._client != null)
+ {
+ this._client.Dispose();
+ this._client = null;
+ }
+ this._control.TuningStyleFreezed = false;
+ this._gui.SaveSettings();
+ }
+
+ public unsafe void Start(SamplesAvailableDelegate callback)
+ {
+ if (this._client != null && this._client.IsSynchronized)
+ {
+ this._callback = callback;
+ this._client.StartStreaming();
+ return;
+ }
+ throw new ApplicationException("Not connected to a server");
+ }
+
+ public void Stop()
+ {
+ if (this._client != null)
+ {
+ this._client.StopStreaming();
+ }
+ }
+
+ public void SetGain(int value)
+ {
+ this._client.Gain = value;
+ }
+
+ public void SetDecimation(int value)
+ {
+ switch (this._client.StreamingMode)
+ {
+ case StreamingMode.STREAM_MODE_IQ_ONLY:
+ this._client.ChannelDecimationStageCount = value;
+ break;
+ case StreamingMode.STREAM_MODE_FFT_IQ:
+ this._client.DisplayDecimationStageCount = value;
+ break;
+ }
+ EventHandler sampleRateChanged = this.SampleRateChanged;
+ if (sampleRateChanged != null)
+ {
+ sampleRateChanged(this, EventArgs.Empty);
+ }
+ if (this._client.StreamingMode == StreamingMode.STREAM_MODE_IQ_ONLY)
+ {
+ this._control.ResetFrequency(this._client.DeviceCenterFrequency);
+ }
+ }
+
+ private void UpdateTuningStyle()
+ {
+ if (!this.CanTune)
+ {
+ this._control.TuningStyle = TuningStyle.Free;
+ this._control.TuningStyleFreezed = true;
+ }
+ else
+ {
+ this._control.TuningStyleFreezed = false;
+ }
+ }
+
+ private void UpdateTuningBoundaries()
+ {
+ if (this._client != null)
+ {
+ switch (this._client.StreamingMode)
+ {
+ case StreamingMode.STREAM_MODE_IQ_ONLY:
+ if (!this._client.IsSynchronized)
+ {
+ this._control.ResetFrequency(this._client.ChannelCenterFrequency);
+ }
+ break;
+ case StreamingMode.STREAM_MODE_FFT_IQ:
+ this._control.ResetFrequency(this._client.ChannelCenterFrequency, this._client.DisplayCenterFrequency);
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.SpyServer/StreamFormat.cs b/SDRSharp/SDRSharp.FrontEnds.SpyServer/StreamFormat.cs
new file mode 100644
index 0000000..b5882dd
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.SpyServer/StreamFormat.cs
@@ -0,0 +1,12 @@
+namespace SDRSharp.FrontEnds.SpyServer
+{
+ public enum StreamFormat : uint
+ {
+ STREAM_FORMAT_INVALID,
+ STREAM_FORMAT_UINT8,
+ STREAM_FORMAT_INT16,
+ STREAM_FORMAT_INT24,
+ STREAM_FORMAT_FLOAT,
+ STREAM_FORMAT_DINT4
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.SpyServer/StreamType.cs b/SDRSharp/SDRSharp.FrontEnds.SpyServer/StreamType.cs
new file mode 100644
index 0000000..99ff647
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.SpyServer/StreamType.cs
@@ -0,0 +1,10 @@
+namespace SDRSharp.FrontEnds.SpyServer
+{
+ public enum StreamType : uint
+ {
+ STREAM_TYPE_STATUS,
+ STREAM_TYPE_IQ,
+ STREAM_TYPE_AF,
+ STREAM_TYPE_FFT = 4u
+ }
+}
diff --git a/SDRSharp/SDRSharp.FrontEnds.SpyServer/StreamingMode.cs b/SDRSharp/SDRSharp.FrontEnds.SpyServer/StreamingMode.cs
new file mode 100644
index 0000000..80ba480
--- /dev/null
+++ b/SDRSharp/SDRSharp.FrontEnds.SpyServer/StreamingMode.cs
@@ -0,0 +1,11 @@
+namespace SDRSharp.FrontEnds.SpyServer
+{
+ public enum StreamingMode : uint
+ {
+ STREAM_MODE_IQ_ONLY = 1u,
+ STREAM_MODE_AF_ONLY,
+ STREAM_MODE_FFT_ONLY = 4u,
+ STREAM_MODE_FFT_IQ,
+ STREAM_MODE_FFT_AF
+ }
+}
diff --git a/SDRSharp/SDRSharp.csproj b/SDRSharp/SDRSharp.csproj
new file mode 100644
index 0000000..db71f12
--- /dev/null
+++ b/SDRSharp/SDRSharp.csproj
@@ -0,0 +1,133 @@
+
+
+
+ {4331A8D2-EF97-40C6-AE40-F1D8066B466C}
+ Debug
+ x86
+ WinExe
+ SDRSharp
+ .NETFramework
+ v4.6
+ 4
+ True
+
+
+ x86
+
+
+ bin\Debug\
+ true
+ full
+ false
+
+
+ bin\Release\
+ true
+ pdbonly
+ true
+
+
+
+ ..\SDRSharp.CollapsiblePanel\bin\Debug\SDRSharp.CollapsiblePanel.dll
+
+
+ ..\SDRSharp.Common\bin\Debug\SDRSharp.Common.dll
+
+
+ False
+ ..\SDRSharp.FrequencyEdit\SDRSharp.FrequencyEdit\bin\Debug\SDRSharp.FrequencyEdit.dll
+
+
+ ..\SDRSharp.PanView\bin\Debug\SDRSharp.PanView.dll
+
+
+ ..\SDRSharp.Radio\bin\Debug\SDRSharp.Radio.dll
+
+
+ C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089\System.Windows.Forms.dll
+
+
+ C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll
+
+
+ C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Drawing\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll
+
+
+ C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+
+
+
+ UserControl
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ ControllerPanel.cs
+
+
+ ControllerPanel.cs
+
+
+ ControllerPanel.cs
+
+
+ MainForm.cs
+
+
+
+
\ No newline at end of file
diff --git a/SDRSharp/SDRSharp.sln b/SDRSharp/SDRSharp.sln
new file mode 100644
index 0000000..67291b7
--- /dev/null
+++ b/SDRSharp/SDRSharp.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27428.2015
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SDRSharp", "SDRSharp.csproj", "{4331A8D2-EF97-40C6-AE40-F1D8066B466C}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x86 = Debug|x86
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {4331A8D2-EF97-40C6-AE40-F1D8066B466C}.Debug|x86.ActiveCfg = Debug|x86
+ {4331A8D2-EF97-40C6-AE40-F1D8066B466C}.Debug|x86.Build.0 = Debug|x86
+ {4331A8D2-EF97-40C6-AE40-F1D8066B466C}.Release|x86.ActiveCfg = Release|x86
+ {4331A8D2-EF97-40C6-AE40-F1D8066B466C}.Release|x86.Build.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {379FF569-D44F-4EE1-9196-41EA04C154C2}
+ EndGlobalSection
+EndGlobal
diff --git a/SDRSharp/SDRSharp/App.config b/SDRSharp/SDRSharp/App.config
new file mode 100644
index 0000000..f16e512
--- /dev/null
+++ b/SDRSharp/SDRSharp/App.config
@@ -0,0 +1,213 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SDRSharp/SDRSharp/MainForm.cs b/SDRSharp/SDRSharp/MainForm.cs
new file mode 100644
index 0000000..c3b3355
--- /dev/null
+++ b/SDRSharp/SDRSharp/MainForm.cs
@@ -0,0 +1,5611 @@
+using Properties;
+using SDRSharp.CollapsiblePanel;
+using SDRSharp.Common;
+using SDRSharp.FrequencyEdit;
+using SDRSharp.FrontEnds.Airspy;
+using SDRSharp.FrontEnds.AirspyHF;
+using SDRSharp.FrontEnds.SpyServer;
+using SDRSharp.PanView;
+//using SDRSharp.Properties;
+using SDRSharp.Radio;
+using SDRSharp.Radio.PortAudio;
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Configuration;
+using System.Diagnostics;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Globalization;
+using System.IO;
+using System.Reflection;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Windows.Forms;
+
+namespace SDRSharp
+{
+ public class MainForm : Form, ISharpControl, INotifyPropertyChanged
+ {
+ private class IQCorrectionProcessor : IIQProcessor, IStreamProcessor, IBaseProcessor
+ {
+ private readonly IQBalancer _iqBalancer = new IQBalancer();
+
+ public double SampleRate
+ {
+ set
+ {
+ }
+ }
+
+ public bool Enabled
+ {
+ get
+ {
+ return true;
+ }
+ set
+ {
+ }
+ }
+
+ public IQBalancer Engine
+ {
+ get
+ {
+ return this._iqBalancer;
+ }
+ }
+
+ public unsafe void Process(Complex* buffer, int length)
+ {
+ this._iqBalancer.Process(buffer, length);
+ }
+ }
+
+ private enum FrequencyInitType
+ {
+ None,
+ Vfo,
+ Device
+ }
+
+ private IContainer components;
+
+ private Button playStopButton;
+
+ private OpenFileDialog openDlg;
+
+ private CheckBox agcCheckBox;
+
+ private Label label4;
+
+ private NumericUpDown agcThresholdNumericUpDown;
+
+ private SpectrumAnalyzer spectrumAnalyzer;
+
+ private Waterfall waterfall;
+
+ private Label label10;
+
+ private NumericUpDown agcDecayNumericUpDown;
+
+ private Label label12;
+
+ private ComboBox outputDeviceComboBox;
+
+ private Label label11;
+
+ private ComboBox inputDeviceComboBox;
+
+ private Label label13;
+
+ private ComboBox sampleRateComboBox;
+
+ private Label label7;
+
+ private ComboBox viewComboBox;
+
+ private Label label8;
+
+ private ComboBox fftWindowComboBox;
+
+ private System.Windows.Forms.Timer iqTimer;
+
+ private Button gradientButton;
+
+ private Label label14;
+
+ private TrackBar fftContrastTrackBar;
+
+ private TrackBar fftZoomTrackBar;
+
+ private Label label19;
+
+ private Label label20;
+
+ private Label label21;
+
+ private ComboBox fftResolutionComboBox;
+
+ private NumericUpDown agcSlopeNumericUpDown;
+
+ private Label label22;
+
+ private RadioButton nfmRadioButton;
+
+ private RadioButton rawRadioButton;
+
+ private RadioButton cwRadioButton;
+
+ private RadioButton amRadioButton;
+
+ private RadioButton dsbRadioButton;
+
+ private RadioButton wfmRadioButton;
+
+ private Button configureSourceButton;
+
+ private RadioButton lsbRadioButton;
+
+ private Label label18;
+
+ private RadioButton usbRadioButton;
+
+ private ComboBox stepSizeComboBox;
+
+ private NumericUpDown filterBandwidthNumericUpDown;
+
+ private Label label1;
+
+ private NumericUpDown squelchNumericUpDown;
+
+ private NumericUpDown filterOrderNumericUpDown;
+
+ private Label label16;
+
+ private Label label5;
+
+ private ComboBox iqSourceComboBox;
+
+ private ComboBox filterTypeComboBox;
+
+ private CheckBox swapIQCheckBox;
+
+ private CheckBox correctIQCheckBox;
+
+ private SDRSharp.CollapsiblePanel.CollapsiblePanel radioCollapsiblePanel;
+
+ private SDRSharp.CollapsiblePanel.CollapsiblePanel audioCollapsiblePanel;
+
+ private SDRSharp.CollapsiblePanel.CollapsiblePanel agcCollapsiblePanel;
+
+ private CheckBox agcUseHangCheckBox;
+
+ private SDRSharp.CollapsiblePanel.CollapsiblePanel fftCollapsiblePanel;
+
+ private NumericUpDown latencyNumericUpDown;
+
+ private Label label6;
+
+ private Label label15;
+
+ private NumericUpDown cwShiftNumericUpDown;
+
+ private Panel controlPanel;
+
+ private Label label25;
+
+ private Label label26;
+
+ private Label label24;
+
+ private Label label23;
+
+ private TrackBar wDecayTrackBar;
+
+ private TrackBar wAttackTrackBar;
+
+ private TrackBar sDecayTrackBar;
+
+ private TrackBar sAttackTrackBar;
+
+ private CheckBox snapFrequencyCheckBox;
+
+ private TrackBar audioGainTrackBar;
+
+ private CheckBox fmStereoCheckBox;
+
+ private CheckBox filterAudioCheckBox;
+
+ private CheckBox useSquelchCheckBox;
+
+ private CheckBox frequencyShiftCheckBox;
+
+ private NumericUpDown frequencyShiftNumericUpDown;
+
+ private CheckBox markPeaksCheckBox;
+
+ private CheckBox useTimestampsCheckBox;
+
+ private Label label17;
+
+ private TrackBar fftSpeedTrackBar;
+
+ private GroupBox groupBox1;
+
+ private TrackBar fftOffsetTrackBar;
+
+ private TrackBar fftRangeTrackBar;
+
+ private Label label28;
+
+ private GroupBox smoothingGroupBox;
+
+ private Panel scrollPanel;
+
+ private SDRSharp.FrequencyEdit.FrequencyEdit vfoFrequencyEdit;
+
+ private CheckBox unityGainCheckBox;
+
+ private TableLayoutPanel rightTableLayoutPanel;
+
+ private TableLayoutPanel settingsTableLayoutPanel;
+
+ private Panel centerPanel;
+
+ private TableLayoutPanel leftPluginPanel;
+
+ private TableLayoutPanel rightPluginPanel;
+
+ private Splitter rightSplitter;
+
+ private Splitter leftSplitter;
+
+ private Panel spectrumPanel;
+
+ private Splitter spectrumSplitter;
+
+ private TableLayoutPanel radioTableLayoutPanel;
+
+ private TableLayoutPanel tableLayoutPanel1;
+
+ private TableLayoutPanel tableLayoutPanel2;
+
+ private TableLayoutPanel tableLayoutPanel3;
+
+ private TableLayoutPanel tableLayoutPanel5;
+
+ private TableLayoutPanel tableLayoutPanel4;
+
+ private Button muteButton;
+
+ private Splitter bottomSplitter;
+
+ private TableLayoutPanel bottomPluginPanel;
+
+ private Splitter topSplitter;
+
+ private TableLayoutPanel topPluginPanel;
+
+ private SDRSharp.CollapsiblePanel.CollapsiblePanel sourceCollapsiblePanel;
+
+ private TableLayoutPanel sourceTableLayoutPanel;
+
+ private TableLayoutPanel tableLayoutPanel7;
+
+ private Button toggleMenuButton;
+
+ private Panel menuSpacerPanel;
+
+ private Label label2;
+
+ private CheckBox lockCarrierCheckBox;
+
+ private CheckBox useAntiFadingCheckBox;
+
+ private Button tuningStyleButton;
+
+ private Label label3;
+
+ private ComboBox spectrumStyleComboBox;
+
+ private PictureBox logoPictureBox;
+
+ private static readonly string _baseTitle = "SDR# v" + Assembly.GetExecutingAssembly().GetName().Version;
+
+ private static readonly int[] _defaultNFMState = new int[12]
+ {
+ 8000,
+ 1000,
+ 3,
+ 50,
+ 1,
+ 1000,
+ 1,
+ 12,
+ 0,
+ 0,
+ 0,
+ 0
+ };
+
+ private static readonly int[] _defaultWFMState = new int[12]
+ {
+ 200000,
+ 250,
+ 3,
+ 50,
+ 0,
+ 1000,
+ 1,
+ 17,
+ 0,
+ 0,
+ 0,
+ 0
+ };
+
+ private static readonly int[] _defaultAMState = new int[12]
+ {
+ 10000,
+ 1000,
+ 3,
+ 50,
+ 0,
+ 1000,
+ 1,
+ 4,
+ 0,
+ 1,
+ 0,
+ 0
+ };
+
+ private static readonly int[] _defaultSSBState = new int[12]
+ {
+ 2400,
+ 1000,
+ 3,
+ 50,
+ 0,
+ 1000,
+ 1,
+ 1,
+ 0,
+ 1,
+ 0,
+ 0
+ };
+
+ private static readonly int[] _defaultDSBState = new int[12]
+ {
+ 6000,
+ 1000,
+ 3,
+ 50,
+ 0,
+ 1000,
+ 1,
+ 1,
+ 0,
+ 1,
+ 0,
+ 0
+ };
+
+ private static readonly int[] _defaultCWState = new int[12]
+ {
+ 300,
+ 1000,
+ 3,
+ 50,
+ 0,
+ 1000,
+ 1,
+ 1,
+ 0,
+ 1,
+ 0,
+ 0
+ };
+
+ private static readonly int[] _defaultRAWState = new int[12]
+ {
+ 10000,
+ 1000,
+ 3,
+ 50,
+ 0,
+ 1000,
+ 1,
+ 4,
+ 1,
+ 0,
+ 0,
+ 0
+ };
+
+ private const long DefaultFrontEndFrequency = 103000000L;
+
+ private const int DefaultFrontEndSpectrumWidth = 250000;
+
+ private const float DefaultUsableSpectrumRatio = 0.9f;
+
+ private const double DefaultSoundCardSampleRate = 48000.0;
+
+ private const int FFTFrameQueueLength = 3;
+
+ private const int SpectrumAnalyzerInterval = 20;
+
+ private const int MaxLockTime = 300000;
+
+ private WindowType _fftWindowType;
+
+ private IFrontendController _frontendController;
+
+ private readonly Dictionary _frontendControllers = new Dictionary();
+
+ private readonly List _builtinControllers = new List();
+
+ private readonly IQCorrectionProcessor _iqBalancerProcessor = new IQCorrectionProcessor();
+
+ private readonly Vfo _vfo;
+
+ private readonly HookManager _hookManager;
+
+ private readonly StreamControl _streamControl;
+
+ private readonly ComplexFifoStream _fftStream = new ComplexFifoStream(BlockMode.BlockingRead);
+
+ private readonly SharpEvent _fftEvent = new SharpEvent(false);
+
+ private readonly ReaderWriterLock _fftResolutionLock = new ReaderWriterLock();
+
+ private FloatCircularBuffer _fftFrames;
+
+ private UnsafeBuffer _iqBuffer;
+
+ private unsafe Complex* _iqPtr;
+
+ private UnsafeBuffer _fftBuffer;
+
+ private unsafe Complex* _fftPtr;
+
+ private UnsafeBuffer _fftWindow;
+
+ private unsafe float* _fftWindowPtr;
+
+ private UnsafeBuffer _fftSpectrum;
+
+ private unsafe float* _fftSpectrumPtr;
+
+ private unsafe float* _fftDisplayPtr;
+
+ private int _fftDisplaySize;
+
+ private UnsafeBuffer _scaledFFTSpectrum;
+
+ private unsafe byte* _scaledFFTSpectrumPtr;
+
+ private System.Windows.Forms.Timer _waterfallTimer;
+
+ private System.Windows.Forms.Timer _spectrumAnalyzerTimer;
+
+ private long _centerFrequency;
+
+ private long _frequencySet;
+
+ private long _frequencyShift;
+
+ private int _fftFramesCount;
+
+ private int _fftcorrectionFPS;
+
+ private float _fftAverageFPS;
+
+ private DateTime _lastFFTTick;
+
+ private int _inputBufferLength;
+
+ private int _fftBins;
+
+ private int _stepSize;
+
+ private int _usableSpectrumWidth;
+
+ private volatile bool _fftIsRunning;
+
+ private bool _changingStickySpot;
+
+ private bool _changingCenterSpot;
+
+ private bool _changingSampleRate;
+
+ private bool _configuringSnap;
+
+ private bool _configuringSquelch;
+
+ private bool _terminated;
+
+ private string _waveFile;
+
+ private Point _lastLocation;
+
+ private Size _lastSize;
+
+ private string _lastSourceName;
+
+ private bool _initializing;
+
+ private int _oldTopSplitterPosition = 200;
+
+ private int _oldBottomSplitterPosition = 200;
+
+ private int _oldLeftSplitterPosition = 200;
+
+ private int _oldRightSplitterPosition = 200;
+
+ private int _sourcePanelHeight;
+
+ private int _ifOffset;
+
+ private float _usableSpectrumRatio = 0.9f;
+
+ private TuningStyle _tuningStyle;
+
+ private bool _tuningStyleFreezed;
+
+ private float _tuningLimit = (float)Math.Min(0.5, Utils.GetDoubleSetting("tuningLimit", 0.4));
+
+ private readonly ToolTip _tooltip = new ToolTip();
+
+ private readonly float _fftOffset = (float)Utils.GetDoubleSetting("fftOffset", -40.0);
+
+ private readonly int _minOutputSampleRate = Utils.GetIntSetting("minOutputSampleRate", 24000);
+
+ private readonly Dictionary _sharpPlugins = new Dictionary();
+
+ private readonly Dictionary _modeStates = new Dictionary();
+
+ private SharpControlProxy _sharpControlProxy;
+
+ public DetectorType DetectorType
+ {
+ get
+ {
+ return this._vfo.DetectorType;
+ }
+ set
+ {
+ switch (value)
+ {
+ case DetectorType.AM:
+ this.amRadioButton.Checked = true;
+ break;
+ case DetectorType.CW:
+ this.cwRadioButton.Checked = true;
+ break;
+ case DetectorType.DSB:
+ this.dsbRadioButton.Checked = true;
+ break;
+ case DetectorType.LSB:
+ this.lsbRadioButton.Checked = true;
+ break;
+ case DetectorType.USB:
+ this.usbRadioButton.Checked = true;
+ break;
+ case DetectorType.NFM:
+ this.nfmRadioButton.Checked = true;
+ break;
+ case DetectorType.WFM:
+ this.wfmRadioButton.Checked = true;
+ break;
+ case DetectorType.RAW:
+ this.rawRadioButton.Checked = true;
+ break;
+ }
+ }
+ }
+
+ public WindowType FilterType
+ {
+ get
+ {
+ return (WindowType)(this.filterTypeComboBox.SelectedIndex + 1);
+ }
+ set
+ {
+ this.filterTypeComboBox.SelectedIndex = (int)(value - 1);
+ }
+ }
+
+ public bool IsPlaying
+ {
+ get
+ {
+ return this._streamControl.IsPlaying;
+ }
+ }
+
+ public long Frequency
+ {
+ get
+ {
+ return this.vfoFrequencyEdit.Frequency;
+ }
+ set
+ {
+ this.SetFrequency(value, false);
+ }
+ }
+
+ public long CenterFrequency
+ {
+ get
+ {
+ return this._centerFrequency;
+ }
+ set
+ {
+ if (this._frontendController == null)
+ {
+ throw new ApplicationException("Cannot set the center frequency when no front end is connected");
+ }
+ this.SetCenterFrequency(value);
+ }
+ }
+
+ public long FrequencyShift
+ {
+ get
+ {
+ return (long)this.frequencyShiftNumericUpDown.Value;
+ }
+ set
+ {
+ this.frequencyShiftNumericUpDown.Value = value;
+ }
+ }
+
+ public bool FrequencyShiftEnabled
+ {
+ get
+ {
+ return this.frequencyShiftCheckBox.Checked;
+ }
+ set
+ {
+ this.frequencyShiftCheckBox.Checked = value;
+ }
+ }
+
+ public int FilterBandwidth
+ {
+ get
+ {
+ return (int)this.filterBandwidthNumericUpDown.Value;
+ }
+ set
+ {
+ if ((decimal)value <= this.filterBandwidthNumericUpDown.Maximum)
+ {
+ this.filterBandwidthNumericUpDown.Value = value;
+ }
+ else
+ {
+ this.filterBandwidthNumericUpDown.Value = this.filterBandwidthNumericUpDown.Maximum;
+ }
+ }
+ }
+
+ public int FilterOrder
+ {
+ get
+ {
+ return (int)this.filterOrderNumericUpDown.Value;
+ }
+ set
+ {
+ this.filterOrderNumericUpDown.Value = value;
+ }
+ }
+
+ public bool SquelchEnabled
+ {
+ get
+ {
+ return this.useSquelchCheckBox.Checked;
+ }
+ set
+ {
+ this.useSquelchCheckBox.Checked = value;
+ }
+ }
+
+ public int SquelchThreshold
+ {
+ get
+ {
+ return (int)this.squelchNumericUpDown.Value;
+ }
+ set
+ {
+ this.squelchNumericUpDown.Value = value;
+ }
+ }
+
+ public int CWShift
+ {
+ get
+ {
+ return (int)this.cwShiftNumericUpDown.Value;
+ }
+ set
+ {
+ this.cwShiftNumericUpDown.Value = value;
+ }
+ }
+
+ public bool SnapToGrid
+ {
+ get
+ {
+ return this.snapFrequencyCheckBox.Checked;
+ }
+ set
+ {
+ this.snapFrequencyCheckBox.Checked = value;
+ }
+ }
+
+ public bool SwapIq
+ {
+ get
+ {
+ return this.swapIQCheckBox.Checked;
+ }
+ set
+ {
+ this.swapIQCheckBox.Checked = value;
+ }
+ }
+
+ public bool FmStereo
+ {
+ get
+ {
+ return this.fmStereoCheckBox.Checked;
+ }
+ set
+ {
+ this.fmStereoCheckBox.Checked = value;
+ }
+ }
+
+ public bool MarkPeaks
+ {
+ get
+ {
+ return this.markPeaksCheckBox.Checked;
+ }
+ set
+ {
+ this.markPeaksCheckBox.Checked = value;
+ }
+ }
+
+ public int AudioGain
+ {
+ get
+ {
+ return this.audioGainTrackBar.Value;
+ }
+ set
+ {
+ this.audioGainTrackBar.Value = value;
+ }
+ }
+
+ public bool AudioIsMuted
+ {
+ get
+ {
+ return this._vfo.Muted;
+ }
+ set
+ {
+ this._vfo.Muted = value;
+ this.UpdateMuteButton();
+ }
+ }
+
+ public string SourceName
+ {
+ get
+ {
+ if (this.SourceIsWaveFile)
+ {
+ if (File.Exists(this._waveFile))
+ {
+ Uri uri = new Uri(this._waveFile);
+ return uri.AbsolutePath;
+ }
+ }
+ else
+ {
+ if (this.SourceIsSoundCard)
+ {
+ return "Sound Card";
+ }
+ if (this.SourceIsFrontEnd)
+ {
+ if (this._frontendController == null)
+ {
+ return string.Empty;
+ }
+ return this._frontendController.GetType().AssemblyQualifiedName;
+ }
+ }
+ return string.Empty;
+ }
+ }
+
+ public Type SourceType
+ {
+ get
+ {
+ if (this.SourceIsFrontEnd)
+ {
+ if (this._frontendController == null)
+ {
+ return null;
+ }
+ return this._frontendController.GetType();
+ }
+ return null;
+ }
+ }
+
+ public object Source
+ {
+ get
+ {
+ if (this.SourceIsFrontEnd)
+ {
+ if (this._frontendController == null)
+ {
+ return null;
+ }
+ return this._frontendController;
+ }
+ return null;
+ }
+ }
+
+ public bool FilterAudio
+ {
+ get
+ {
+ return this.filterAudioCheckBox.Checked;
+ }
+ set
+ {
+ this.filterAudioCheckBox.Checked = value;
+ }
+ }
+
+ public bool UnityGain
+ {
+ get
+ {
+ return this.unityGainCheckBox.Checked;
+ }
+ set
+ {
+ this.unityGainCheckBox.Checked = value;
+ }
+ }
+
+ public bool UseAgc
+ {
+ get
+ {
+ return this.agcCheckBox.Checked;
+ }
+ set
+ {
+ this.agcCheckBox.Checked = value;
+ }
+ }
+
+ public bool AgcHang
+ {
+ get
+ {
+ return this.agcUseHangCheckBox.Checked;
+ }
+ set
+ {
+ this.agcUseHangCheckBox.Checked = value;
+ }
+ }
+
+ public int AgcThreshold
+ {
+ get
+ {
+ return (int)this.agcThresholdNumericUpDown.Value;
+ }
+ set
+ {
+ this.agcThresholdNumericUpDown.Value = value;
+ }
+ }
+
+ public int AgcDecay
+ {
+ get
+ {
+ return (int)this.agcDecayNumericUpDown.Value;
+ }
+ set
+ {
+ this.agcDecayNumericUpDown.Value = value;
+ }
+ }
+
+ public int AgcSlope
+ {
+ get
+ {
+ return (int)this.agcSlopeNumericUpDown.Value;
+ }
+ set
+ {
+ this.agcSlopeNumericUpDown.Value = value;
+ }
+ }
+
+ public float SAttack
+ {
+ get
+ {
+ return (float)this.sAttackTrackBar.Value / (float)this.sAttackTrackBar.Maximum;
+ }
+ set
+ {
+ this.sAttackTrackBar.Value = (int)(value * (float)this.sAttackTrackBar.Maximum);
+ }
+ }
+
+ public float SDecay
+ {
+ get
+ {
+ return (float)this.sDecayTrackBar.Value / (float)this.sDecayTrackBar.Maximum;
+ }
+ set
+ {
+ this.sDecayTrackBar.Value = (int)(value * (float)this.sDecayTrackBar.Maximum);
+ }
+ }
+
+ public float WAttack
+ {
+ get
+ {
+ return (float)this.wAttackTrackBar.Value / (float)this.wAttackTrackBar.Maximum;
+ }
+ set
+ {
+ this.wAttackTrackBar.Value = (int)(value * (float)this.wAttackTrackBar.Maximum);
+ }
+ }
+
+ public float WDecay
+ {
+ get
+ {
+ return (float)this.wDecayTrackBar.Value / (float)this.wDecayTrackBar.Maximum;
+ }
+ set
+ {
+ this.wDecayTrackBar.Value = (int)(value * (float)this.wDecayTrackBar.Maximum);
+ }
+ }
+
+ public int Zoom
+ {
+ get
+ {
+ return this.fftZoomTrackBar.Value;
+ }
+ set
+ {
+ this.fftZoomTrackBar.Value = value;
+ }
+ }
+
+ public bool UseTimeMarkers
+ {
+ get
+ {
+ return this.useTimestampsCheckBox.Checked;
+ }
+ set
+ {
+ this.useTimestampsCheckBox.Checked = value;
+ }
+ }
+
+ public string RdsProgramService
+ {
+ get
+ {
+ return this._vfo.RdsStationName;
+ }
+ }
+
+ public string RdsRadioText
+ {
+ get
+ {
+ return this._vfo.RdsStationText;
+ }
+ }
+
+ public ushort RdsPICode
+ {
+ get
+ {
+ return this._vfo.RdsPICode;
+ }
+ }
+
+ public bool RdsUseFEC
+ {
+ get
+ {
+ return this._vfo.RdsUseFEC;
+ }
+ set
+ {
+ this._vfo.RdsUseFEC = value;
+ }
+ }
+
+ public int RFBandwidth
+ {
+ get
+ {
+ return (int)this._vfo.SampleRate;
+ }
+ }
+
+ public int RFDisplayBandwidth
+ {
+ get
+ {
+ return this._usableSpectrumWidth;
+ }
+ }
+
+ public bool SourceIsWaveFile
+ {
+ get
+ {
+ return this.iqSourceComboBox.SelectedIndex == this.iqSourceComboBox.Items.Count - 2;
+ }
+ }
+
+ public bool SourceIsSoundCard
+ {
+ get
+ {
+ return this.iqSourceComboBox.SelectedIndex == this.iqSourceComboBox.Items.Count - 1;
+ }
+ }
+
+ public bool SourceIsFrontEnd
+ {
+ get
+ {
+ if (!this.SourceIsSoundCard)
+ {
+ return !this.SourceIsWaveFile;
+ }
+ return false;
+ }
+ }
+
+ public bool SourceIsTunable
+ {
+ get
+ {
+ if (!this.SourceIsSoundCard && !this.SourceIsWaveFile)
+ {
+ if (this._frontendController == null)
+ {
+ return false;
+ }
+ if (this._frontendController is ITunableSource)
+ {
+ return ((ITunableSource)this._frontendController).CanTune;
+ }
+ return false;
+ }
+ return false;
+ }
+ }
+
+ public bool IsSquelchOpen
+ {
+ get
+ {
+ return this._vfo.IsSquelchOpen;
+ }
+ }
+
+ public int FFTResolution
+ {
+ get
+ {
+ return this._fftBins;
+ }
+ }
+
+ public float FFTRange
+ {
+ get
+ {
+ return (float)this.spectrumAnalyzer.DisplayRange;
+ }
+ }
+
+ public float FFTOffset
+ {
+ get
+ {
+ return (float)this.spectrumAnalyzer.DisplayOffset;
+ }
+ }
+
+ public int FFTContrast
+ {
+ get
+ {
+ return this.spectrumAnalyzer.Contrast;
+ }
+ }
+
+ public ColorBlend Gradient
+ {
+ get
+ {
+ return this.spectrumAnalyzer.VerticalLinesGradient;
+ }
+ }
+
+ public SpectrumStyle FFTSpectrumStyle
+ {
+ get
+ {
+ return this.spectrumAnalyzer.SpectrumStyle;
+ }
+ }
+
+ public int StepSize
+ {
+ get
+ {
+ return this._stepSize;
+ }
+ set
+ {
+ if (this.SetStepSize(value))
+ {
+ this._stepSize = value;
+ }
+ }
+ }
+
+ public bool BypassDemodulation
+ {
+ get
+ {
+ return this._vfo.BypassDemodulation;
+ }
+ set
+ {
+ this._vfo.BypassDemodulation = value;
+ }
+ }
+
+ public int TunableBandwidth
+ {
+ get
+ {
+ return (int)Math.Ceiling((double)((float)this._usableSpectrumWidth * this._tuningLimit * 2f));
+ }
+ }
+
+ public TuningStyle TuningStyle
+ {
+ get
+ {
+ return this._tuningStyle;
+ }
+ set
+ {
+ if (!this._tuningStyleFreezed)
+ {
+ this._tuningStyle = value;
+ }
+ this.UpdateTuningStyle();
+ }
+ }
+
+ public int IFOffset
+ {
+ get
+ {
+ return this._ifOffset;
+ }
+ }
+
+ public float TuningLimit
+ {
+ get
+ {
+ return this._tuningLimit;
+ }
+ set
+ {
+ if (value != this._tuningLimit)
+ {
+ this._tuningLimit = value;
+ this._tuningLimit = Math.Min(0.5f, this._tuningLimit);
+ this._tuningLimit = Math.Max(0.1f, this._tuningLimit);
+ this.UpdateTunableBandwidth();
+ }
+ }
+ }
+
+ public float VisualSNR
+ {
+ get
+ {
+ return this.spectrumAnalyzer.VisualSNR;
+ }
+ }
+
+ public bool TuningStyleFreezed
+ {
+ get
+ {
+ return this._tuningStyleFreezed;
+ }
+ set
+ {
+ this._tuningStyleFreezed = value;
+ this.tuningStyleButton.Enabled = !this._tuningStyleFreezed;
+ }
+ }
+
+ public bool UseFFTSource
+ {
+ get
+ {
+ if (this._frontendController is IFFTSource)
+ {
+ return (this._frontendController as IFFTSource).FFTEnabled;
+ }
+ return false;
+ }
+ }
+
+ public double AudioSampleRate
+ {
+ get
+ {
+ return this._streamControl.AudioSampleRate;
+ }
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public event CustomPaintEventHandler WaterfallCustomPaint;
+
+ public event CustomPaintEventHandler SpectrumAnalyzerCustomPaint;
+
+ public event CustomPaintEventHandler SpectrumAnalyzerBackgroundCustomPaint;
+
+ private void InitializeComponent()
+ {
+ this.components = new Container();
+ ComponentResourceManager componentResourceManager = new ComponentResourceManager(typeof(MainForm));
+ this.openDlg = new OpenFileDialog();
+ this.iqTimer = new System.Windows.Forms.Timer(this.components);
+ this.fftContrastTrackBar = new TrackBar();
+ this.fftZoomTrackBar = new TrackBar();
+ this.label19 = new Label();
+ this.label20 = new Label();
+ this.controlPanel = new Panel();
+ this.sourceCollapsiblePanel = new SDRSharp.CollapsiblePanel.CollapsiblePanel();
+ this.sourceTableLayoutPanel = new TableLayoutPanel();
+ this.iqSourceComboBox = new ComboBox();
+ this.radioCollapsiblePanel = new SDRSharp.CollapsiblePanel.CollapsiblePanel();
+ this.radioTableLayoutPanel = new TableLayoutPanel();
+ this.swapIQCheckBox = new CheckBox();
+ this.frequencyShiftCheckBox = new CheckBox();
+ this.tableLayoutPanel7 = new TableLayoutPanel();
+ this.amRadioButton = new RadioButton();
+ this.nfmRadioButton = new RadioButton();
+ this.lsbRadioButton = new RadioButton();
+ this.usbRadioButton = new RadioButton();
+ this.wfmRadioButton = new RadioButton();
+ this.dsbRadioButton = new RadioButton();
+ this.cwRadioButton = new RadioButton();
+ this.rawRadioButton = new RadioButton();
+ this.useSquelchCheckBox = new CheckBox();
+ this.stepSizeComboBox = new ComboBox();
+ this.cwShiftNumericUpDown = new NumericUpDown();
+ this.label18 = new Label();
+ this.label15 = new Label();
+ this.squelchNumericUpDown = new NumericUpDown();
+ this.filterBandwidthNumericUpDown = new NumericUpDown();
+ this.label1 = new Label();
+ this.label5 = new Label();
+ this.filterOrderNumericUpDown = new NumericUpDown();
+ this.frequencyShiftNumericUpDown = new NumericUpDown();
+ this.filterTypeComboBox = new ComboBox();
+ this.label16 = new Label();
+ this.correctIQCheckBox = new CheckBox();
+ this.snapFrequencyCheckBox = new CheckBox();
+ this.lockCarrierCheckBox = new CheckBox();
+ this.fmStereoCheckBox = new CheckBox();
+ this.useAntiFadingCheckBox = new CheckBox();
+ this.audioCollapsiblePanel = new SDRSharp.CollapsiblePanel.CollapsiblePanel();
+ this.tableLayoutPanel1 = new TableLayoutPanel();
+ this.label13 = new Label();
+ this.sampleRateComboBox = new ComboBox();
+ this.label11 = new Label();
+ this.inputDeviceComboBox = new ComboBox();
+ this.label12 = new Label();
+ this.outputDeviceComboBox = new ComboBox();
+ this.latencyNumericUpDown = new NumericUpDown();
+ this.label6 = new Label();
+ this.unityGainCheckBox = new CheckBox();
+ this.filterAudioCheckBox = new CheckBox();
+ this.agcCollapsiblePanel = new SDRSharp.CollapsiblePanel.CollapsiblePanel();
+ this.tableLayoutPanel2 = new TableLayoutPanel();
+ this.agcCheckBox = new CheckBox();
+ this.agcSlopeNumericUpDown = new NumericUpDown();
+ this.agcUseHangCheckBox = new CheckBox();
+ this.label22 = new Label();
+ this.label4 = new Label();
+ this.agcDecayNumericUpDown = new NumericUpDown();
+ this.label10 = new Label();
+ this.agcThresholdNumericUpDown = new NumericUpDown();
+ this.fftCollapsiblePanel = new SDRSharp.CollapsiblePanel.CollapsiblePanel();
+ this.tableLayoutPanel3 = new TableLayoutPanel();
+ this.label7 = new Label();
+ this.viewComboBox = new ComboBox();
+ this.label8 = new Label();
+ this.fftWindowComboBox = new ComboBox();
+ this.label21 = new Label();
+ this.fftResolutionComboBox = new ComboBox();
+ this.groupBox1 = new GroupBox();
+ this.tableLayoutPanel5 = new TableLayoutPanel();
+ this.label28 = new Label();
+ this.fftSpeedTrackBar = new TrackBar();
+ this.smoothingGroupBox = new GroupBox();
+ this.tableLayoutPanel4 = new TableLayoutPanel();
+ this.label23 = new Label();
+ this.wDecayTrackBar = new TrackBar();
+ this.wAttackTrackBar = new TrackBar();
+ this.label25 = new Label();
+ this.sDecayTrackBar = new TrackBar();
+ this.sAttackTrackBar = new TrackBar();
+ this.label24 = new Label();
+ this.label26 = new Label();
+ this.markPeaksCheckBox = new CheckBox();
+ this.useTimestampsCheckBox = new CheckBox();
+ this.label14 = new Label();
+ this.gradientButton = new Button();
+ this.label3 = new Label();
+ this.spectrumStyleComboBox = new ComboBox();
+ this.fftOffsetTrackBar = new TrackBar();
+ this.fftRangeTrackBar = new TrackBar();
+ this.audioGainTrackBar = new TrackBar();
+ this.label17 = new Label();
+ this.scrollPanel = new Panel();
+ this.rightTableLayoutPanel = new TableLayoutPanel();
+ this.label2 = new Label();
+ this.centerPanel = new Panel();
+ this.spectrumPanel = new Panel();
+ this.spectrumSplitter = new Splitter();
+ this.waterfall = new Waterfall();
+ this.spectrumAnalyzer = new SpectrumAnalyzer();
+ this.bottomSplitter = new Splitter();
+ this.bottomPluginPanel = new TableLayoutPanel();
+ this.topSplitter = new Splitter();
+ this.topPluginPanel = new TableLayoutPanel();
+ this.rightSplitter = new Splitter();
+ this.leftSplitter = new Splitter();
+ this.leftPluginPanel = new TableLayoutPanel();
+ this.rightPluginPanel = new TableLayoutPanel();
+ this.settingsTableLayoutPanel = new TableLayoutPanel();
+ this.playStopButton = new Button();
+ this.configureSourceButton = new Button();
+ this.toggleMenuButton = new Button();
+ this.muteButton = new Button();
+ this.vfoFrequencyEdit = new SDRSharp.FrequencyEdit.FrequencyEdit();
+ this.tuningStyleButton = new Button();
+ this.logoPictureBox = new PictureBox();
+ this.menuSpacerPanel = new Panel();
+ ((ISupportInitialize)this.fftContrastTrackBar).BeginInit();
+ ((ISupportInitialize)this.fftZoomTrackBar).BeginInit();
+ this.controlPanel.SuspendLayout();
+ this.sourceCollapsiblePanel.Content.SuspendLayout();
+ this.sourceCollapsiblePanel.SuspendLayout();
+ this.sourceTableLayoutPanel.SuspendLayout();
+ this.radioCollapsiblePanel.Content.SuspendLayout();
+ this.radioCollapsiblePanel.SuspendLayout();
+ this.radioTableLayoutPanel.SuspendLayout();
+ this.tableLayoutPanel7.SuspendLayout();
+ ((ISupportInitialize)this.cwShiftNumericUpDown).BeginInit();
+ ((ISupportInitialize)this.squelchNumericUpDown).BeginInit();
+ ((ISupportInitialize)this.filterBandwidthNumericUpDown).BeginInit();
+ ((ISupportInitialize)this.filterOrderNumericUpDown).BeginInit();
+ ((ISupportInitialize)this.frequencyShiftNumericUpDown).BeginInit();
+ this.audioCollapsiblePanel.Content.SuspendLayout();
+ this.audioCollapsiblePanel.SuspendLayout();
+ this.tableLayoutPanel1.SuspendLayout();
+ ((ISupportInitialize)this.latencyNumericUpDown).BeginInit();
+ this.agcCollapsiblePanel.Content.SuspendLayout();
+ this.agcCollapsiblePanel.SuspendLayout();
+ this.tableLayoutPanel2.SuspendLayout();
+ ((ISupportInitialize)this.agcSlopeNumericUpDown).BeginInit();
+ ((ISupportInitialize)this.agcDecayNumericUpDown).BeginInit();
+ ((ISupportInitialize)this.agcThresholdNumericUpDown).BeginInit();
+ this.fftCollapsiblePanel.Content.SuspendLayout();
+ this.fftCollapsiblePanel.SuspendLayout();
+ this.tableLayoutPanel3.SuspendLayout();
+ this.groupBox1.SuspendLayout();
+ this.tableLayoutPanel5.SuspendLayout();
+ ((ISupportInitialize)this.fftSpeedTrackBar).BeginInit();
+ this.smoothingGroupBox.SuspendLayout();
+ this.tableLayoutPanel4.SuspendLayout();
+ ((ISupportInitialize)this.wDecayTrackBar).BeginInit();
+ ((ISupportInitialize)this.wAttackTrackBar).BeginInit();
+ ((ISupportInitialize)this.sDecayTrackBar).BeginInit();
+ ((ISupportInitialize)this.sAttackTrackBar).BeginInit();
+ ((ISupportInitialize)this.fftOffsetTrackBar).BeginInit();
+ ((ISupportInitialize)this.fftRangeTrackBar).BeginInit();
+ ((ISupportInitialize)this.audioGainTrackBar).BeginInit();
+ this.scrollPanel.SuspendLayout();
+ this.rightTableLayoutPanel.SuspendLayout();
+ this.centerPanel.SuspendLayout();
+ this.spectrumPanel.SuspendLayout();
+ this.settingsTableLayoutPanel.SuspendLayout();
+ ((ISupportInitialize)this.logoPictureBox).BeginInit();
+ base.SuspendLayout();
+ this.openDlg.DefaultExt = "wav";
+ this.openDlg.Filter = "WAV files|*.wav";
+ this.iqTimer.Enabled = true;
+ this.iqTimer.Tick += this.iqTimer_Tick;
+ this.fftContrastTrackBar.Anchor = (AnchorStyles.Top | AnchorStyles.Bottom);
+ this.fftContrastTrackBar.Location = new Point(3, 186);
+ this.fftContrastTrackBar.Maximum = 24;
+ this.fftContrastTrackBar.Minimum = -24;
+ this.fftContrastTrackBar.Name = "fftContrastTrackBar";
+ this.fftContrastTrackBar.Orientation = Orientation.Vertical;
+ this.fftContrastTrackBar.RightToLeftLayout = true;
+ this.fftContrastTrackBar.Size = new Size(45, 151);
+ this.fftContrastTrackBar.TabIndex = 1;
+ this.fftContrastTrackBar.TickFrequency = 6;
+ this.fftContrastTrackBar.TickStyle = TickStyle.Both;
+ this.fftContrastTrackBar.ValueChanged += this.fftContrastTrackBar_Changed;
+ this.fftZoomTrackBar.Anchor = (AnchorStyles.Top | AnchorStyles.Bottom);
+ this.fftZoomTrackBar.Location = new Point(3, 16);
+ this.fftZoomTrackBar.Maximum = 50;
+ this.fftZoomTrackBar.Name = "fftZoomTrackBar";
+ this.fftZoomTrackBar.Orientation = Orientation.Vertical;
+ this.fftZoomTrackBar.RightToLeftLayout = true;
+ this.fftZoomTrackBar.Size = new Size(45, 151);
+ this.fftZoomTrackBar.TabIndex = 0;
+ this.fftZoomTrackBar.TickFrequency = 5;
+ this.fftZoomTrackBar.TickStyle = TickStyle.Both;
+ this.fftZoomTrackBar.ValueChanged += this.fftZoomTrackBar_ValueChanged;
+ this.label19.Anchor = AnchorStyles.None;
+ this.label19.AutoSize = true;
+ this.label19.Location = new Point(9, 0);
+ this.label19.Name = "label19";
+ this.label19.Size = new Size(34, 13);
+ this.label19.TabIndex = 19;
+ this.label19.Text = "Zoom";
+ this.label19.TextAlign = ContentAlignment.MiddleCenter;
+ this.label20.Anchor = AnchorStyles.None;
+ this.label20.AutoSize = true;
+ this.label20.Location = new Point(3, 170);
+ this.label20.Name = "label20";
+ this.label20.Size = new Size(46, 13);
+ this.label20.TabIndex = 20;
+ this.label20.Text = "Contrast";
+ this.label20.TextAlign = ContentAlignment.MiddleCenter;
+ this.controlPanel.AutoSize = true;
+ this.controlPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
+ this.controlPanel.Controls.Add(this.sourceCollapsiblePanel);
+ this.controlPanel.Controls.Add(this.radioCollapsiblePanel);
+ this.controlPanel.Controls.Add(this.audioCollapsiblePanel);
+ this.controlPanel.Controls.Add(this.fftCollapsiblePanel);
+ this.controlPanel.Controls.Add(this.agcCollapsiblePanel);
+ this.controlPanel.Location = new Point(0, 0);
+ this.controlPanel.Margin = new Padding(0);
+ this.controlPanel.Name = "controlPanel";
+ this.controlPanel.Size = new Size(227, 1218);
+ this.controlPanel.TabIndex = 25;
+ this.sourceCollapsiblePanel.AutoHeight = true;
+ this.sourceCollapsiblePanel.Content.Controls.Add(this.sourceTableLayoutPanel);
+ this.sourceCollapsiblePanel.Content.Location = new Point(0, 24);
+ this.sourceCollapsiblePanel.Content.Margin = new Padding(2);
+ this.sourceCollapsiblePanel.Content.Name = "Content";
+ this.sourceCollapsiblePanel.Content.Size = new Size(224, 28);
+ this.sourceCollapsiblePanel.Content.TabIndex = 1;
+ this.sourceCollapsiblePanel.Location = new Point(0, 0);
+ this.sourceCollapsiblePanel.Margin = new Padding(0);
+ this.sourceCollapsiblePanel.Name = "sourceCollapsiblePanel";
+ this.sourceCollapsiblePanel.NextPanel = this.radioCollapsiblePanel;
+ this.sourceCollapsiblePanel.PanelTitle = "Source";
+ this.sourceCollapsiblePanel.Size = new Size(224, 52);
+ this.sourceCollapsiblePanel.TabIndex = 4;
+ this.sourceTableLayoutPanel.Anchor = (AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right);
+ this.sourceTableLayoutPanel.AutoSize = true;
+ this.sourceTableLayoutPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
+ this.sourceTableLayoutPanel.ColumnCount = 1;
+ this.sourceTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
+ this.sourceTableLayoutPanel.Controls.Add(this.iqSourceComboBox, 0, 0);
+ this.sourceTableLayoutPanel.Location = new Point(0, 0);
+ this.sourceTableLayoutPanel.Margin = new Padding(0);
+ this.sourceTableLayoutPanel.Name = "sourceTableLayoutPanel";
+ this.sourceTableLayoutPanel.RowCount = 2;
+ this.sourceTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.sourceTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
+ this.sourceTableLayoutPanel.Size = new Size(224, 27);
+ this.sourceTableLayoutPanel.TabIndex = 0;
+ this.iqSourceComboBox.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.iqSourceComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
+ this.iqSourceComboBox.DropDownWidth = 250;
+ this.iqSourceComboBox.FormattingEnabled = true;
+ this.iqSourceComboBox.Location = new Point(3, 3);
+ this.iqSourceComboBox.Name = "iqSourceComboBox";
+ this.iqSourceComboBox.Size = new Size(218, 21);
+ this.iqSourceComboBox.TabIndex = 1;
+ this.iqSourceComboBox.SelectedIndexChanged += this.iqSourceComboBox_SelectedIndexChanged;
+ this.radioCollapsiblePanel.AutoHeight = false;
+ this.radioCollapsiblePanel.Content.Controls.Add(this.radioTableLayoutPanel);
+ this.radioCollapsiblePanel.Content.Location = new Point(0, 24);
+ this.radioCollapsiblePanel.Content.Name = "Content";
+ this.radioCollapsiblePanel.Content.Size = new Size(224, 341);
+ this.radioCollapsiblePanel.Content.TabIndex = 1;
+ this.radioCollapsiblePanel.Location = new Point(0, 52);
+ this.radioCollapsiblePanel.Margin = new Padding(0);
+ this.radioCollapsiblePanel.Name = "radioCollapsiblePanel";
+ this.radioCollapsiblePanel.NextPanel = this.audioCollapsiblePanel;
+ this.radioCollapsiblePanel.PanelTitle = "Radio";
+ this.radioCollapsiblePanel.Size = new Size(224, 365);
+ this.radioCollapsiblePanel.TabIndex = 0;
+ this.radioTableLayoutPanel.Anchor = (AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right);
+ this.radioTableLayoutPanel.ColumnCount = 4;
+ this.radioTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
+ this.radioTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
+ this.radioTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
+ this.radioTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
+ this.radioTableLayoutPanel.Controls.Add(this.swapIQCheckBox, 2, 10);
+ this.radioTableLayoutPanel.Controls.Add(this.frequencyShiftCheckBox, 0, 1);
+ this.radioTableLayoutPanel.Controls.Add(this.tableLayoutPanel7, 0, 0);
+ this.radioTableLayoutPanel.Controls.Add(this.useSquelchCheckBox, 0, 5);
+ this.radioTableLayoutPanel.Controls.Add(this.stepSizeComboBox, 2, 8);
+ this.radioTableLayoutPanel.Controls.Add(this.cwShiftNumericUpDown, 2, 6);
+ this.radioTableLayoutPanel.Controls.Add(this.label18, 2, 7);
+ this.radioTableLayoutPanel.Controls.Add(this.label15, 2, 5);
+ this.radioTableLayoutPanel.Controls.Add(this.squelchNumericUpDown, 0, 6);
+ this.radioTableLayoutPanel.Controls.Add(this.filterBandwidthNumericUpDown, 0, 4);
+ this.radioTableLayoutPanel.Controls.Add(this.label1, 0, 3);
+ this.radioTableLayoutPanel.Controls.Add(this.label5, 2, 3);
+ this.radioTableLayoutPanel.Controls.Add(this.filterOrderNumericUpDown, 2, 4);
+ this.radioTableLayoutPanel.Controls.Add(this.frequencyShiftNumericUpDown, 1, 1);
+ this.radioTableLayoutPanel.Controls.Add(this.filterTypeComboBox, 1, 2);
+ this.radioTableLayoutPanel.Controls.Add(this.label16, 0, 2);
+ this.radioTableLayoutPanel.Controls.Add(this.correctIQCheckBox, 2, 9);
+ this.radioTableLayoutPanel.Controls.Add(this.snapFrequencyCheckBox, 0, 8);
+ this.radioTableLayoutPanel.Controls.Add(this.lockCarrierCheckBox, 0, 9);
+ this.radioTableLayoutPanel.Controls.Add(this.fmStereoCheckBox, 0, 7);
+ this.radioTableLayoutPanel.Controls.Add(this.useAntiFadingCheckBox, 0, 10);
+ this.radioTableLayoutPanel.Location = new Point(0, 0);
+ this.radioTableLayoutPanel.Name = "radioTableLayoutPanel";
+ this.radioTableLayoutPanel.RowCount = 11;
+ this.radioTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
+ this.radioTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.radioTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.radioTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.radioTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.radioTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.radioTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.radioTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.radioTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.radioTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.radioTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.radioTableLayoutPanel.Size = new Size(224, 341);
+ this.radioTableLayoutPanel.TabIndex = 34;
+ this.swapIQCheckBox.Anchor = AnchorStyles.Right;
+ this.swapIQCheckBox.AutoSize = true;
+ this.radioTableLayoutPanel.SetColumnSpan(this.swapIQCheckBox, 2);
+ this.swapIQCheckBox.Location = new Point(143, 321);
+ this.swapIQCheckBox.Name = "swapIQCheckBox";
+ this.swapIQCheckBox.RightToLeft = RightToLeft.Yes;
+ this.swapIQCheckBox.Size = new Size(79, 17);
+ this.swapIQCheckBox.TabIndex = 25;
+ this.swapIQCheckBox.Text = "Swap I && Q";
+ this.swapIQCheckBox.UseVisualStyleBackColor = true;
+ this.swapIQCheckBox.CheckedChanged += this.swapIQCheckBox_CheckedChanged;
+ this.frequencyShiftCheckBox.Anchor = AnchorStyles.Left;
+ this.frequencyShiftCheckBox.AutoSize = true;
+ this.frequencyShiftCheckBox.Location = new Point(3, 106);
+ this.frequencyShiftCheckBox.Name = "frequencyShiftCheckBox";
+ this.frequencyShiftCheckBox.Size = new Size(47, 17);
+ this.frequencyShiftCheckBox.TabIndex = 7;
+ this.frequencyShiftCheckBox.Text = "Shift";
+ this.frequencyShiftCheckBox.UseVisualStyleBackColor = true;
+ this.frequencyShiftCheckBox.CheckedChanged += this.frequencyShiftCheckBox_CheckStateChanged;
+ this.tableLayoutPanel7.Anchor = (AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right);
+ this.tableLayoutPanel7.ColumnCount = 4;
+ this.radioTableLayoutPanel.SetColumnSpan(this.tableLayoutPanel7, 4);
+ this.tableLayoutPanel7.ColumnStyles.Add(new ColumnStyle());
+ this.tableLayoutPanel7.ColumnStyles.Add(new ColumnStyle());
+ this.tableLayoutPanel7.ColumnStyles.Add(new ColumnStyle());
+ this.tableLayoutPanel7.ColumnStyles.Add(new ColumnStyle());
+ this.tableLayoutPanel7.Controls.Add(this.amRadioButton, 1, 0);
+ this.tableLayoutPanel7.Controls.Add(this.nfmRadioButton, 0, 0);
+ this.tableLayoutPanel7.Controls.Add(this.lsbRadioButton, 2, 0);
+ this.tableLayoutPanel7.Controls.Add(this.usbRadioButton, 3, 0);
+ this.tableLayoutPanel7.Controls.Add(this.wfmRadioButton, 0, 1);
+ this.tableLayoutPanel7.Controls.Add(this.dsbRadioButton, 1, 1);
+ this.tableLayoutPanel7.Controls.Add(this.cwRadioButton, 2, 1);
+ this.tableLayoutPanel7.Controls.Add(this.rawRadioButton, 3, 1);
+ this.tableLayoutPanel7.Location = new Point(3, 3);
+ this.tableLayoutPanel7.Name = "tableLayoutPanel7";
+ this.tableLayoutPanel7.RowCount = 2;
+ this.tableLayoutPanel7.RowStyles.Add(new RowStyle(SizeType.Percent, 50f));
+ this.tableLayoutPanel7.RowStyles.Add(new RowStyle(SizeType.Percent, 50f));
+ this.tableLayoutPanel7.Size = new Size(219, 94);
+ this.tableLayoutPanel7.TabIndex = 32;
+ this.amRadioButton.Anchor = AnchorStyles.Left;
+ this.amRadioButton.AutoSize = true;
+ this.amRadioButton.Location = new Point(60, 15);
+ this.amRadioButton.Name = "amRadioButton";
+ this.amRadioButton.Size = new Size(41, 17);
+ this.amRadioButton.TabIndex = 1;
+ this.amRadioButton.Text = "AM";
+ this.amRadioButton.UseVisualStyleBackColor = true;
+ this.amRadioButton.CheckedChanged += this.modeRadioButton_CheckStateChanged;
+ this.nfmRadioButton.Anchor = AnchorStyles.Left;
+ this.nfmRadioButton.AutoSize = true;
+ this.nfmRadioButton.Location = new Point(3, 15);
+ this.nfmRadioButton.Name = "nfmRadioButton";
+ this.nfmRadioButton.Size = new Size(48, 17);
+ this.nfmRadioButton.TabIndex = 0;
+ this.nfmRadioButton.Text = "NFM";
+ this.nfmRadioButton.UseVisualStyleBackColor = true;
+ this.nfmRadioButton.CheckedChanged += this.modeRadioButton_CheckStateChanged;
+ this.lsbRadioButton.Anchor = AnchorStyles.Left;
+ this.lsbRadioButton.AutoSize = true;
+ this.lsbRadioButton.Location = new Point(113, 15);
+ this.lsbRadioButton.Name = "lsbRadioButton";
+ this.lsbRadioButton.Size = new Size(45, 17);
+ this.lsbRadioButton.TabIndex = 2;
+ this.lsbRadioButton.Text = "LSB";
+ this.lsbRadioButton.UseVisualStyleBackColor = true;
+ this.lsbRadioButton.CheckedChanged += this.modeRadioButton_CheckStateChanged;
+ this.usbRadioButton.Anchor = AnchorStyles.Left;
+ this.usbRadioButton.AutoSize = true;
+ this.usbRadioButton.Location = new Point(164, 15);
+ this.usbRadioButton.Name = "usbRadioButton";
+ this.usbRadioButton.Size = new Size(47, 17);
+ this.usbRadioButton.TabIndex = 3;
+ this.usbRadioButton.Text = "USB";
+ this.usbRadioButton.UseVisualStyleBackColor = true;
+ this.usbRadioButton.CheckedChanged += this.modeRadioButton_CheckStateChanged;
+ this.wfmRadioButton.Anchor = AnchorStyles.Left;
+ this.wfmRadioButton.AutoSize = true;
+ this.wfmRadioButton.Location = new Point(3, 62);
+ this.wfmRadioButton.Name = "wfmRadioButton";
+ this.wfmRadioButton.Size = new Size(51, 17);
+ this.wfmRadioButton.TabIndex = 4;
+ this.wfmRadioButton.Text = "WFM";
+ this.wfmRadioButton.UseVisualStyleBackColor = true;
+ this.wfmRadioButton.CheckedChanged += this.modeRadioButton_CheckStateChanged;
+ this.dsbRadioButton.Anchor = AnchorStyles.Left;
+ this.dsbRadioButton.AutoSize = true;
+ this.dsbRadioButton.Location = new Point(60, 62);
+ this.dsbRadioButton.Name = "dsbRadioButton";
+ this.dsbRadioButton.Size = new Size(47, 17);
+ this.dsbRadioButton.TabIndex = 5;
+ this.dsbRadioButton.Text = "DSB";
+ this.dsbRadioButton.UseVisualStyleBackColor = true;
+ this.dsbRadioButton.CheckedChanged += this.modeRadioButton_CheckStateChanged;
+ this.cwRadioButton.Anchor = AnchorStyles.Left;
+ this.cwRadioButton.AutoSize = true;
+ this.cwRadioButton.Location = new Point(113, 62);
+ this.cwRadioButton.Name = "cwRadioButton";
+ this.cwRadioButton.Size = new Size(43, 17);
+ this.cwRadioButton.TabIndex = 6;
+ this.cwRadioButton.Text = "CW";
+ this.cwRadioButton.UseVisualStyleBackColor = true;
+ this.cwRadioButton.CheckedChanged += this.modeRadioButton_CheckStateChanged;
+ this.rawRadioButton.Anchor = AnchorStyles.Left;
+ this.rawRadioButton.AutoSize = true;
+ this.rawRadioButton.Location = new Point(164, 62);
+ this.rawRadioButton.Name = "rawRadioButton";
+ this.rawRadioButton.Size = new Size(51, 17);
+ this.rawRadioButton.TabIndex = 6;
+ this.rawRadioButton.Text = "RAW";
+ this.rawRadioButton.UseVisualStyleBackColor = true;
+ this.rawRadioButton.CheckedChanged += this.modeRadioButton_CheckStateChanged;
+ this.useSquelchCheckBox.Anchor = AnchorStyles.Left;
+ this.useSquelchCheckBox.AutoSize = true;
+ this.radioTableLayoutPanel.SetColumnSpan(this.useSquelchCheckBox, 2);
+ this.useSquelchCheckBox.Location = new Point(3, 199);
+ this.useSquelchCheckBox.Name = "useSquelchCheckBox";
+ this.useSquelchCheckBox.Size = new Size(65, 17);
+ this.useSquelchCheckBox.TabIndex = 15;
+ this.useSquelchCheckBox.Text = "Squelch";
+ this.useSquelchCheckBox.UseVisualStyleBackColor = true;
+ this.useSquelchCheckBox.CheckedChanged += this.useSquelchCheckBox_CheckedChanged;
+ this.stepSizeComboBox.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.radioTableLayoutPanel.SetColumnSpan(this.stepSizeComboBox, 2);
+ this.stepSizeComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
+ this.stepSizeComboBox.FormattingEnabled = true;
+ this.stepSizeComboBox.Location = new Point(114, 271);
+ this.stepSizeComboBox.Name = "stepSizeComboBox";
+ this.stepSizeComboBox.Size = new Size(108, 21);
+ this.stepSizeComboBox.TabIndex = 21;
+ this.stepSizeComboBox.SelectedIndexChanged += this.stepSizeComboBox_SelectedIndexChanged;
+ this.cwShiftNumericUpDown.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.radioTableLayoutPanel.SetColumnSpan(this.cwShiftNumericUpDown, 2);
+ this.cwShiftNumericUpDown.Enabled = false;
+ this.cwShiftNumericUpDown.Location = new Point(114, 222);
+ this.cwShiftNumericUpDown.Maximum = new decimal(new int[4]
+ {
+ 2000,
+ 0,
+ 0,
+ 0
+ });
+ this.cwShiftNumericUpDown.Minimum = new decimal(new int[4]
+ {
+ 2000,
+ 0,
+ 0,
+ -2147483648
+ });
+ this.cwShiftNumericUpDown.Name = "cwShiftNumericUpDown";
+ this.cwShiftNumericUpDown.Size = new Size(108, 20);
+ this.cwShiftNumericUpDown.TabIndex = 18;
+ this.cwShiftNumericUpDown.TextAlign = HorizontalAlignment.Right;
+ this.cwShiftNumericUpDown.ThousandsSeparator = true;
+ this.cwShiftNumericUpDown.Value = new decimal(new int[4]
+ {
+ 1000,
+ 0,
+ 0,
+ 0
+ });
+ this.cwShiftNumericUpDown.ValueChanged += this.cwShiftNumericUpDown_ValueChanged;
+ this.label18.Anchor = AnchorStyles.Left;
+ this.label18.AutoSize = true;
+ this.radioTableLayoutPanel.SetColumnSpan(this.label18, 2);
+ this.label18.Location = new Point(114, 250);
+ this.label18.Name = "label18";
+ this.label18.Size = new Size(52, 13);
+ this.label18.TabIndex = 19;
+ this.label18.Text = "Step Size";
+ this.label18.TextAlign = ContentAlignment.MiddleLeft;
+ this.label15.Anchor = AnchorStyles.Left;
+ this.label15.AutoSize = true;
+ this.radioTableLayoutPanel.SetColumnSpan(this.label15, 2);
+ this.label15.Location = new Point(114, 201);
+ this.label15.Name = "label15";
+ this.label15.Size = new Size(49, 13);
+ this.label15.TabIndex = 16;
+ this.label15.Text = "CW Shift";
+ this.label15.TextAlign = ContentAlignment.MiddleLeft;
+ this.squelchNumericUpDown.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.radioTableLayoutPanel.SetColumnSpan(this.squelchNumericUpDown, 2);
+ this.squelchNumericUpDown.Enabled = false;
+ this.squelchNumericUpDown.Location = new Point(3, 222);
+ this.squelchNumericUpDown.Name = "squelchNumericUpDown";
+ this.squelchNumericUpDown.Size = new Size(105, 20);
+ this.squelchNumericUpDown.TabIndex = 17;
+ this.squelchNumericUpDown.TextAlign = HorizontalAlignment.Right;
+ this.squelchNumericUpDown.ValueChanged += this.squelchNumericUpDown_ValueChanged;
+ this.filterBandwidthNumericUpDown.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.radioTableLayoutPanel.SetColumnSpan(this.filterBandwidthNumericUpDown, 2);
+ this.filterBandwidthNumericUpDown.Increment = new decimal(new int[4]
+ {
+ 10,
+ 0,
+ 0,
+ 0
+ });
+ this.filterBandwidthNumericUpDown.Location = new Point(3, 173);
+ this.filterBandwidthNumericUpDown.Maximum = new decimal(new int[4]
+ {
+ 250000,
+ 0,
+ 0,
+ 0
+ });
+ this.filterBandwidthNumericUpDown.Minimum = new decimal(new int[4]
+ {
+ 10,
+ 0,
+ 0,
+ 0
+ });
+ this.filterBandwidthNumericUpDown.Name = "filterBandwidthNumericUpDown";
+ this.filterBandwidthNumericUpDown.Size = new Size(105, 20);
+ this.filterBandwidthNumericUpDown.TabIndex = 13;
+ this.filterBandwidthNumericUpDown.TextAlign = HorizontalAlignment.Right;
+ this.filterBandwidthNumericUpDown.ThousandsSeparator = true;
+ this.filterBandwidthNumericUpDown.Value = new decimal(new int[4]
+ {
+ 10000,
+ 0,
+ 0,
+ 0
+ });
+ this.filterBandwidthNumericUpDown.ValueChanged += this.filterBandwidthNumericUpDown_ValueChanged;
+ this.label1.Anchor = AnchorStyles.Left;
+ this.label1.AutoSize = true;
+ this.radioTableLayoutPanel.SetColumnSpan(this.label1, 2);
+ this.label1.Location = new Point(3, 157);
+ this.label1.Name = "label1";
+ this.label1.Size = new Size(57, 13);
+ this.label1.TabIndex = 11;
+ this.label1.Text = "Bandwidth";
+ this.label1.TextAlign = ContentAlignment.MiddleLeft;
+ this.label5.Anchor = AnchorStyles.Left;
+ this.label5.AutoSize = true;
+ this.radioTableLayoutPanel.SetColumnSpan(this.label5, 2);
+ this.label5.Location = new Point(114, 157);
+ this.label5.Name = "label5";
+ this.label5.Size = new Size(33, 13);
+ this.label5.TabIndex = 12;
+ this.label5.Text = "Order";
+ this.label5.TextAlign = ContentAlignment.MiddleLeft;
+ this.filterOrderNumericUpDown.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.radioTableLayoutPanel.SetColumnSpan(this.filterOrderNumericUpDown, 2);
+ this.filterOrderNumericUpDown.Increment = new decimal(new int[4]
+ {
+ 10,
+ 0,
+ 0,
+ 0
+ });
+ this.filterOrderNumericUpDown.Location = new Point(114, 173);
+ this.filterOrderNumericUpDown.Maximum = new decimal(new int[4]
+ {
+ 9999,
+ 0,
+ 0,
+ 0
+ });
+ this.filterOrderNumericUpDown.Minimum = new decimal(new int[4]
+ {
+ 10,
+ 0,
+ 0,
+ 0
+ });
+ this.filterOrderNumericUpDown.Name = "filterOrderNumericUpDown";
+ this.filterOrderNumericUpDown.Size = new Size(108, 20);
+ this.filterOrderNumericUpDown.TabIndex = 14;
+ this.filterOrderNumericUpDown.TextAlign = HorizontalAlignment.Right;
+ this.filterOrderNumericUpDown.ThousandsSeparator = true;
+ this.filterOrderNumericUpDown.Value = new decimal(new int[4]
+ {
+ 400,
+ 0,
+ 0,
+ 0
+ });
+ this.filterOrderNumericUpDown.ValueChanged += this.filterOrderNumericUpDown_ValueChanged;
+ this.frequencyShiftNumericUpDown.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.radioTableLayoutPanel.SetColumnSpan(this.frequencyShiftNumericUpDown, 3);
+ this.frequencyShiftNumericUpDown.Enabled = false;
+ this.frequencyShiftNumericUpDown.Font = new Font("Microsoft Sans Serif", 11.25f, FontStyle.Bold, GraphicsUnit.Point, 0);
+ this.frequencyShiftNumericUpDown.Increment = new decimal(new int[4]
+ {
+ 10,
+ 0,
+ 0,
+ 0
+ });
+ this.frequencyShiftNumericUpDown.Location = new Point(56, 103);
+ this.frequencyShiftNumericUpDown.Maximum = new decimal(new int[4]
+ {
+ 276447232,
+ 23283,
+ 0,
+ 0
+ });
+ this.frequencyShiftNumericUpDown.Minimum = new decimal(new int[4]
+ {
+ 276447232,
+ 23283,
+ 0,
+ -2147483648
+ });
+ this.frequencyShiftNumericUpDown.Name = "frequencyShiftNumericUpDown";
+ this.frequencyShiftNumericUpDown.Size = new Size(166, 24);
+ this.frequencyShiftNumericUpDown.TabIndex = 8;
+ this.frequencyShiftNumericUpDown.TextAlign = HorizontalAlignment.Right;
+ this.frequencyShiftNumericUpDown.ThousandsSeparator = true;
+ this.frequencyShiftNumericUpDown.ValueChanged += this.frequencyShiftNumericUpDown_ValueChanged;
+ this.filterTypeComboBox.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.radioTableLayoutPanel.SetColumnSpan(this.filterTypeComboBox, 3);
+ this.filterTypeComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
+ this.filterTypeComboBox.FormattingEnabled = true;
+ this.filterTypeComboBox.Items.AddRange(new object[6]
+ {
+ "Hamming",
+ "Blackman",
+ "Blackman-Harris 4",
+ "Blackman-Harris 7",
+ "Hann-Poisson",
+ "Youssef"
+ });
+ this.filterTypeComboBox.Location = new Point(56, 133);
+ this.filterTypeComboBox.Name = "filterTypeComboBox";
+ this.filterTypeComboBox.Size = new Size(166, 21);
+ this.filterTypeComboBox.TabIndex = 10;
+ this.filterTypeComboBox.SelectedIndexChanged += this.filterTypeComboBox_SelectedIndexChanged;
+ this.label16.Anchor = AnchorStyles.Left;
+ this.label16.AutoSize = true;
+ this.label16.Location = new Point(3, 137);
+ this.label16.Name = "label16";
+ this.label16.Size = new Size(29, 13);
+ this.label16.TabIndex = 9;
+ this.label16.Text = "Filter";
+ this.label16.TextAlign = ContentAlignment.MiddleLeft;
+ this.correctIQCheckBox.Anchor = AnchorStyles.Right;
+ this.correctIQCheckBox.AutoSize = true;
+ this.radioTableLayoutPanel.SetColumnSpan(this.correctIQCheckBox, 2);
+ this.correctIQCheckBox.Location = new Point(148, 298);
+ this.correctIQCheckBox.Name = "correctIQCheckBox";
+ this.correctIQCheckBox.RightToLeft = RightToLeft.Yes;
+ this.correctIQCheckBox.Size = new Size(74, 17);
+ this.correctIQCheckBox.TabIndex = 22;
+ this.correctIQCheckBox.Text = "Correct IQ";
+ this.correctIQCheckBox.UseVisualStyleBackColor = true;
+ this.correctIQCheckBox.CheckedChanged += this.autoCorrectIQCheckBox_CheckStateChanged;
+ this.snapFrequencyCheckBox.Anchor = AnchorStyles.Right;
+ this.snapFrequencyCheckBox.AutoSize = true;
+ this.radioTableLayoutPanel.SetColumnSpan(this.snapFrequencyCheckBox, 2);
+ this.snapFrequencyCheckBox.Location = new Point(23, 273);
+ this.snapFrequencyCheckBox.Name = "snapFrequencyCheckBox";
+ this.snapFrequencyCheckBox.RightToLeft = RightToLeft.Yes;
+ this.snapFrequencyCheckBox.Size = new Size(85, 17);
+ this.snapFrequencyCheckBox.TabIndex = 20;
+ this.snapFrequencyCheckBox.Text = "Snap to Grid";
+ this.snapFrequencyCheckBox.UseVisualStyleBackColor = true;
+ this.snapFrequencyCheckBox.CheckedChanged += this.stepSizeComboBox_SelectedIndexChanged;
+ this.lockCarrierCheckBox.Anchor = AnchorStyles.Right;
+ this.lockCarrierCheckBox.AutoSize = true;
+ this.lockCarrierCheckBox.CheckAlign = ContentAlignment.MiddleRight;
+ this.radioTableLayoutPanel.SetColumnSpan(this.lockCarrierCheckBox, 2);
+ this.lockCarrierCheckBox.Location = new Point(25, 298);
+ this.lockCarrierCheckBox.Name = "lockCarrierCheckBox";
+ this.lockCarrierCheckBox.Size = new Size(83, 17);
+ this.lockCarrierCheckBox.TabIndex = 33;
+ this.lockCarrierCheckBox.Text = "Lock Carrier";
+ this.lockCarrierCheckBox.UseVisualStyleBackColor = true;
+ this.lockCarrierCheckBox.CheckedChanged += this.lockCarrierCheckBox_CheckedChanged;
+ this.fmStereoCheckBox.Anchor = AnchorStyles.Right;
+ this.fmStereoCheckBox.AutoSize = true;
+ this.radioTableLayoutPanel.SetColumnSpan(this.fmStereoCheckBox, 2);
+ this.fmStereoCheckBox.Enabled = false;
+ this.fmStereoCheckBox.Location = new Point(33, 248);
+ this.fmStereoCheckBox.Name = "fmStereoCheckBox";
+ this.fmStereoCheckBox.RightToLeft = RightToLeft.Yes;
+ this.fmStereoCheckBox.Size = new Size(75, 17);
+ this.fmStereoCheckBox.TabIndex = 24;
+ this.fmStereoCheckBox.Text = "FM Stereo";
+ this.fmStereoCheckBox.UseVisualStyleBackColor = true;
+ this.fmStereoCheckBox.CheckedChanged += this.fmStereoCheckBox_CheckedChanged;
+ this.useAntiFadingCheckBox.Anchor = AnchorStyles.Right;
+ this.useAntiFadingCheckBox.AutoSize = true;
+ this.useAntiFadingCheckBox.CheckAlign = ContentAlignment.MiddleRight;
+ this.radioTableLayoutPanel.SetColumnSpan(this.useAntiFadingCheckBox, 2);
+ this.useAntiFadingCheckBox.Location = new Point(29, 321);
+ this.useAntiFadingCheckBox.Name = "useAntiFadingCheckBox";
+ this.useAntiFadingCheckBox.Size = new Size(79, 17);
+ this.useAntiFadingCheckBox.TabIndex = 34;
+ this.useAntiFadingCheckBox.Text = "Anti-Fading";
+ this.useAntiFadingCheckBox.UseVisualStyleBackColor = true;
+ this.useAntiFadingCheckBox.CheckedChanged += this.useAntiFadingCheckBox_CheckedChanged;
+ this.audioCollapsiblePanel.AutoHeight = false;
+ this.audioCollapsiblePanel.Content.Controls.Add(this.tableLayoutPanel1);
+ this.audioCollapsiblePanel.Content.Location = new Point(0, 24);
+ this.audioCollapsiblePanel.Content.Name = "Content";
+ this.audioCollapsiblePanel.Content.Size = new Size(224, 136);
+ this.audioCollapsiblePanel.Content.TabIndex = 1;
+ this.audioCollapsiblePanel.Location = new Point(0, 417);
+ this.audioCollapsiblePanel.Name = "audioCollapsiblePanel";
+ this.audioCollapsiblePanel.NextPanel = this.agcCollapsiblePanel;
+ this.audioCollapsiblePanel.PanelTitle = "Audio";
+ this.audioCollapsiblePanel.Size = new Size(224, 160);
+ this.audioCollapsiblePanel.TabIndex = 1;
+ this.tableLayoutPanel1.ColumnCount = 2;
+ this.tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 40f));
+ this.tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 60f));
+ this.tableLayoutPanel1.Controls.Add(this.label13, 0, 0);
+ this.tableLayoutPanel1.Controls.Add(this.sampleRateComboBox, 1, 0);
+ this.tableLayoutPanel1.Controls.Add(this.label11, 0, 1);
+ this.tableLayoutPanel1.Controls.Add(this.inputDeviceComboBox, 1, 1);
+ this.tableLayoutPanel1.Controls.Add(this.label12, 0, 2);
+ this.tableLayoutPanel1.Controls.Add(this.outputDeviceComboBox, 1, 2);
+ this.tableLayoutPanel1.Controls.Add(this.latencyNumericUpDown, 1, 3);
+ this.tableLayoutPanel1.Controls.Add(this.label6, 0, 3);
+ this.tableLayoutPanel1.Controls.Add(this.unityGainCheckBox, 0, 4);
+ this.tableLayoutPanel1.Controls.Add(this.filterAudioCheckBox, 1, 4);
+ this.tableLayoutPanel1.Dock = DockStyle.Fill;
+ this.tableLayoutPanel1.Location = new Point(0, 0);
+ this.tableLayoutPanel1.Name = "tableLayoutPanel1";
+ this.tableLayoutPanel1.RowCount = 5;
+ this.tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
+ this.tableLayoutPanel1.RowStyles.Add(new RowStyle());
+ this.tableLayoutPanel1.RowStyles.Add(new RowStyle());
+ this.tableLayoutPanel1.RowStyles.Add(new RowStyle());
+ this.tableLayoutPanel1.RowStyles.Add(new RowStyle());
+ this.tableLayoutPanel1.Size = new Size(224, 136);
+ this.tableLayoutPanel1.TabIndex = 31;
+ this.label13.Anchor = AnchorStyles.Left;
+ this.label13.AutoSize = true;
+ this.label13.Location = new Point(3, 10);
+ this.label13.Name = "label13";
+ this.label13.Size = new Size(60, 13);
+ this.label13.TabIndex = 28;
+ this.label13.Text = "Samplerate";
+ this.sampleRateComboBox.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.sampleRateComboBox.FormattingEnabled = true;
+ this.sampleRateComboBox.Items.AddRange(new object[14]
+ {
+ "8000 sample/sec",
+ "11025 sample/sec",
+ "16000 sample/sec",
+ "22050 sample/sec",
+ "24000 sample/sec",
+ "32000 sample/sec",
+ "44100 sample/sec",
+ "48000 sample/sec",
+ "80000 sample/sec",
+ "96000 sample/sec",
+ "120000 sample/sec",
+ "125000 sample/sec",
+ "150000 sample/sec",
+ "192000 sample/sec"
+ });
+ this.sampleRateComboBox.Location = new Point(92, 6);
+ this.sampleRateComboBox.Name = "sampleRateComboBox";
+ this.sampleRateComboBox.Size = new Size(129, 21);
+ this.sampleRateComboBox.TabIndex = 1;
+ this.label11.Anchor = AnchorStyles.Left;
+ this.label11.AutoSize = true;
+ this.label11.Location = new Point(3, 40);
+ this.label11.Name = "label11";
+ this.label11.Size = new Size(31, 13);
+ this.label11.TabIndex = 24;
+ this.label11.Text = "Input";
+ this.inputDeviceComboBox.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.inputDeviceComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
+ this.inputDeviceComboBox.DropDownWidth = 300;
+ this.inputDeviceComboBox.FormattingEnabled = true;
+ this.inputDeviceComboBox.Location = new Point(92, 36);
+ this.inputDeviceComboBox.Name = "inputDeviceComboBox";
+ this.inputDeviceComboBox.Size = new Size(129, 21);
+ this.inputDeviceComboBox.TabIndex = 2;
+ this.label12.Anchor = AnchorStyles.Left;
+ this.label12.AutoSize = true;
+ this.label12.Location = new Point(3, 67);
+ this.label12.Name = "label12";
+ this.label12.Size = new Size(39, 13);
+ this.label12.TabIndex = 26;
+ this.label12.Text = "Output";
+ this.outputDeviceComboBox.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.outputDeviceComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
+ this.outputDeviceComboBox.DropDownWidth = 300;
+ this.outputDeviceComboBox.FormattingEnabled = true;
+ this.outputDeviceComboBox.Location = new Point(92, 63);
+ this.outputDeviceComboBox.Name = "outputDeviceComboBox";
+ this.outputDeviceComboBox.Size = new Size(129, 21);
+ this.outputDeviceComboBox.TabIndex = 3;
+ this.latencyNumericUpDown.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.latencyNumericUpDown.Location = new Point(92, 90);
+ this.latencyNumericUpDown.Maximum = new decimal(new int[4]
+ {
+ 2000,
+ 0,
+ 0,
+ 0
+ });
+ this.latencyNumericUpDown.Minimum = new decimal(new int[4]
+ {
+ 1,
+ 0,
+ 0,
+ 0
+ });
+ this.latencyNumericUpDown.Name = "latencyNumericUpDown";
+ this.latencyNumericUpDown.Size = new Size(129, 20);
+ this.latencyNumericUpDown.TabIndex = 4;
+ this.latencyNumericUpDown.TextAlign = HorizontalAlignment.Right;
+ this.latencyNumericUpDown.Value = new decimal(new int[4]
+ {
+ 1,
+ 0,
+ 0,
+ 0
+ });
+ this.label6.Anchor = AnchorStyles.Left;
+ this.label6.AutoSize = true;
+ this.label6.Location = new Point(3, 93);
+ this.label6.Name = "label6";
+ this.label6.Size = new Size(67, 13);
+ this.label6.TabIndex = 30;
+ this.label6.Text = "Latency (ms)";
+ this.unityGainCheckBox.Anchor = AnchorStyles.Left;
+ this.unityGainCheckBox.AutoSize = true;
+ this.unityGainCheckBox.Location = new Point(3, 116);
+ this.unityGainCheckBox.Name = "unityGainCheckBox";
+ this.unityGainCheckBox.Size = new Size(75, 17);
+ this.unityGainCheckBox.TabIndex = 5;
+ this.unityGainCheckBox.Text = "Unity Gain";
+ this.unityGainCheckBox.UseVisualStyleBackColor = true;
+ this.unityGainCheckBox.CheckStateChanged += this.unityGainCheckBox_CheckStateChanged;
+ this.filterAudioCheckBox.Anchor = AnchorStyles.Left;
+ this.filterAudioCheckBox.AutoSize = true;
+ this.filterAudioCheckBox.Location = new Point(92, 116);
+ this.filterAudioCheckBox.Name = "filterAudioCheckBox";
+ this.filterAudioCheckBox.Size = new Size(78, 17);
+ this.filterAudioCheckBox.TabIndex = 6;
+ this.filterAudioCheckBox.Text = "Filter Audio";
+ this.filterAudioCheckBox.UseVisualStyleBackColor = true;
+ this.filterAudioCheckBox.CheckedChanged += this.filterAudioCheckBox_CheckStateChanged;
+ this.agcCollapsiblePanel.AutoHeight = false;
+ this.agcCollapsiblePanel.Content.Controls.Add(this.tableLayoutPanel2);
+ this.agcCollapsiblePanel.Content.Location = new Point(0, 24);
+ this.agcCollapsiblePanel.Content.Name = "Content";
+ this.agcCollapsiblePanel.Content.Size = new Size(224, 106);
+ this.agcCollapsiblePanel.Content.TabIndex = 1;
+ this.agcCollapsiblePanel.Location = new Point(0, 577);
+ this.agcCollapsiblePanel.Name = "agcCollapsiblePanel";
+ this.agcCollapsiblePanel.NextPanel = this.fftCollapsiblePanel;
+ this.agcCollapsiblePanel.PanelTitle = "AGC";
+ this.agcCollapsiblePanel.Size = new Size(224, 130);
+ this.agcCollapsiblePanel.TabIndex = 2;
+ this.tableLayoutPanel2.ColumnCount = 2;
+ this.tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 40f));
+ this.tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 60f));
+ this.tableLayoutPanel2.Controls.Add(this.agcCheckBox, 0, 0);
+ this.tableLayoutPanel2.Controls.Add(this.agcSlopeNumericUpDown, 1, 3);
+ this.tableLayoutPanel2.Controls.Add(this.agcUseHangCheckBox, 1, 0);
+ this.tableLayoutPanel2.Controls.Add(this.label22, 0, 3);
+ this.tableLayoutPanel2.Controls.Add(this.label4, 0, 1);
+ this.tableLayoutPanel2.Controls.Add(this.agcDecayNumericUpDown, 1, 2);
+ this.tableLayoutPanel2.Controls.Add(this.label10, 0, 2);
+ this.tableLayoutPanel2.Controls.Add(this.agcThresholdNumericUpDown, 1, 1);
+ this.tableLayoutPanel2.Dock = DockStyle.Fill;
+ this.tableLayoutPanel2.Location = new Point(0, 0);
+ this.tableLayoutPanel2.Name = "tableLayoutPanel2";
+ this.tableLayoutPanel2.RowCount = 4;
+ this.tableLayoutPanel2.RowStyles.Add(new RowStyle());
+ this.tableLayoutPanel2.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
+ this.tableLayoutPanel2.RowStyles.Add(new RowStyle());
+ this.tableLayoutPanel2.RowStyles.Add(new RowStyle());
+ this.tableLayoutPanel2.RowStyles.Add(new RowStyle(SizeType.Absolute, 20f));
+ this.tableLayoutPanel2.Size = new Size(224, 106);
+ this.tableLayoutPanel2.TabIndex = 14;
+ this.agcCheckBox.Anchor = AnchorStyles.Left;
+ this.agcCheckBox.AutoSize = true;
+ this.agcCheckBox.Location = new Point(3, 3);
+ this.agcCheckBox.Name = "agcCheckBox";
+ this.agcCheckBox.Size = new Size(70, 17);
+ this.agcCheckBox.TabIndex = 0;
+ this.agcCheckBox.Text = "Use AGC";
+ this.agcCheckBox.UseVisualStyleBackColor = true;
+ this.agcCheckBox.CheckedChanged += this.agcCheckBox_CheckedChanged;
+ this.agcSlopeNumericUpDown.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.agcSlopeNumericUpDown.Location = new Point(92, 83);
+ this.agcSlopeNumericUpDown.Maximum = new decimal(new int[4]
+ {
+ 10,
+ 0,
+ 0,
+ 0
+ });
+ this.agcSlopeNumericUpDown.Name = "agcSlopeNumericUpDown";
+ this.agcSlopeNumericUpDown.Size = new Size(129, 20);
+ this.agcSlopeNumericUpDown.TabIndex = 4;
+ this.agcSlopeNumericUpDown.TextAlign = HorizontalAlignment.Right;
+ this.agcSlopeNumericUpDown.Value = new decimal(new int[4]
+ {
+ 10,
+ 0,
+ 0,
+ 0
+ });
+ this.agcSlopeNumericUpDown.ValueChanged += this.agcSlopeNumericUpDown_ValueChanged;
+ this.agcUseHangCheckBox.Anchor = AnchorStyles.Left;
+ this.agcUseHangCheckBox.AutoSize = true;
+ this.agcUseHangCheckBox.Location = new Point(92, 3);
+ this.agcUseHangCheckBox.Name = "agcUseHangCheckBox";
+ this.agcUseHangCheckBox.Size = new Size(74, 17);
+ this.agcUseHangCheckBox.TabIndex = 1;
+ this.agcUseHangCheckBox.Text = "Use Hang";
+ this.agcUseHangCheckBox.UseVisualStyleBackColor = true;
+ this.agcUseHangCheckBox.CheckedChanged += this.agcUseHangCheckBox_CheckedChanged;
+ this.label22.Anchor = AnchorStyles.Left;
+ this.label22.AutoSize = true;
+ this.label22.Location = new Point(3, 86);
+ this.label22.Name = "label22";
+ this.label22.Size = new Size(56, 13);
+ this.label22.TabIndex = 13;
+ this.label22.Text = "Slope (dB)";
+ this.label4.Anchor = AnchorStyles.Left;
+ this.label4.AutoSize = true;
+ this.label4.Location = new Point(3, 32);
+ this.label4.Name = "label4";
+ this.label4.Size = new Size(76, 13);
+ this.label4.TabIndex = 7;
+ this.label4.Text = "Threshold (dB)";
+ this.agcDecayNumericUpDown.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.agcDecayNumericUpDown.Location = new Point(92, 57);
+ this.agcDecayNumericUpDown.Maximum = new decimal(new int[4]
+ {
+ 2000,
+ 0,
+ 0,
+ 0
+ });
+ this.agcDecayNumericUpDown.Minimum = new decimal(new int[4]
+ {
+ 10,
+ 0,
+ 0,
+ 0
+ });
+ this.agcDecayNumericUpDown.Name = "agcDecayNumericUpDown";
+ this.agcDecayNumericUpDown.Size = new Size(129, 20);
+ this.agcDecayNumericUpDown.TabIndex = 3;
+ this.agcDecayNumericUpDown.TextAlign = HorizontalAlignment.Right;
+ this.agcDecayNumericUpDown.Value = new decimal(new int[4]
+ {
+ 2000,
+ 0,
+ 0,
+ 0
+ });
+ this.agcDecayNumericUpDown.ValueChanged += this.agcDecayNumericUpDown_ValueChanged;
+ this.label10.Anchor = AnchorStyles.Left;
+ this.label10.AutoSize = true;
+ this.label10.Location = new Point(3, 60);
+ this.label10.Name = "label10";
+ this.label10.Size = new Size(60, 13);
+ this.label10.TabIndex = 11;
+ this.label10.Text = "Decay (ms)";
+ this.agcThresholdNumericUpDown.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.agcThresholdNumericUpDown.Font = new Font("Microsoft Sans Serif", 10f, FontStyle.Bold, GraphicsUnit.Point, 0);
+ this.agcThresholdNumericUpDown.Location = new Point(92, 27);
+ this.agcThresholdNumericUpDown.Maximum = new decimal(new int[4]);
+ this.agcThresholdNumericUpDown.Minimum = new decimal(new int[4]
+ {
+ 160,
+ 0,
+ 0,
+ -2147483648
+ });
+ this.agcThresholdNumericUpDown.Name = "agcThresholdNumericUpDown";
+ this.agcThresholdNumericUpDown.Size = new Size(129, 23);
+ this.agcThresholdNumericUpDown.TabIndex = 2;
+ this.agcThresholdNumericUpDown.TextAlign = HorizontalAlignment.Right;
+ this.agcThresholdNumericUpDown.ValueChanged += this.agcThresholdNumericUpDown_ValueChanged;
+ this.fftCollapsiblePanel.AutoHeight = false;
+ this.fftCollapsiblePanel.Content.Controls.Add(this.tableLayoutPanel3);
+ this.fftCollapsiblePanel.Content.Location = new Point(0, 24);
+ this.fftCollapsiblePanel.Content.Name = "Content";
+ this.fftCollapsiblePanel.Content.Size = new Size(224, 484);
+ this.fftCollapsiblePanel.Content.TabIndex = 1;
+ this.fftCollapsiblePanel.Location = new Point(0, 707);
+ this.fftCollapsiblePanel.Name = "fftCollapsiblePanel";
+ this.fftCollapsiblePanel.NextPanel = null;
+ this.fftCollapsiblePanel.PanelTitle = "FFT Display";
+ this.fftCollapsiblePanel.Size = new Size(224, 508);
+ this.fftCollapsiblePanel.TabIndex = 3;
+ this.tableLayoutPanel3.ColumnCount = 4;
+ this.tableLayoutPanel3.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 40f));
+ this.tableLayoutPanel3.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 10f));
+ this.tableLayoutPanel3.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 35f));
+ this.tableLayoutPanel3.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 15f));
+ this.tableLayoutPanel3.Controls.Add(this.label7, 0, 0);
+ this.tableLayoutPanel3.Controls.Add(this.viewComboBox, 1, 0);
+ this.tableLayoutPanel3.Controls.Add(this.label8, 0, 1);
+ this.tableLayoutPanel3.Controls.Add(this.fftWindowComboBox, 1, 1);
+ this.tableLayoutPanel3.Controls.Add(this.label21, 0, 2);
+ this.tableLayoutPanel3.Controls.Add(this.fftResolutionComboBox, 1, 2);
+ this.tableLayoutPanel3.Controls.Add(this.groupBox1, 0, 7);
+ this.tableLayoutPanel3.Controls.Add(this.smoothingGroupBox, 0, 6);
+ this.tableLayoutPanel3.Controls.Add(this.markPeaksCheckBox, 0, 5);
+ this.tableLayoutPanel3.Controls.Add(this.useTimestampsCheckBox, 0, 4);
+ this.tableLayoutPanel3.Controls.Add(this.label14, 2, 4);
+ this.tableLayoutPanel3.Controls.Add(this.gradientButton, 3, 4);
+ this.tableLayoutPanel3.Controls.Add(this.label3, 0, 3);
+ this.tableLayoutPanel3.Controls.Add(this.spectrumStyleComboBox, 1, 3);
+ this.tableLayoutPanel3.Dock = DockStyle.Fill;
+ this.tableLayoutPanel3.Location = new Point(0, 0);
+ this.tableLayoutPanel3.Name = "tableLayoutPanel3";
+ this.tableLayoutPanel3.RowCount = 8;
+ this.tableLayoutPanel3.RowStyles.Add(new RowStyle());
+ this.tableLayoutPanel3.RowStyles.Add(new RowStyle());
+ this.tableLayoutPanel3.RowStyles.Add(new RowStyle());
+ this.tableLayoutPanel3.RowStyles.Add(new RowStyle());
+ this.tableLayoutPanel3.RowStyles.Add(new RowStyle());
+ this.tableLayoutPanel3.RowStyles.Add(new RowStyle());
+ this.tableLayoutPanel3.RowStyles.Add(new RowStyle(SizeType.Percent, 75f));
+ this.tableLayoutPanel3.RowStyles.Add(new RowStyle(SizeType.Percent, 25f));
+ this.tableLayoutPanel3.Size = new Size(224, 484);
+ this.tableLayoutPanel3.TabIndex = 33;
+ this.label7.Anchor = AnchorStyles.Left;
+ this.label7.AutoSize = true;
+ this.label7.Location = new Point(3, 7);
+ this.label7.Name = "label7";
+ this.label7.Size = new Size(30, 13);
+ this.label7.TabIndex = 12;
+ this.label7.Text = "View";
+ this.viewComboBox.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.tableLayoutPanel3.SetColumnSpan(this.viewComboBox, 3);
+ this.viewComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
+ this.viewComboBox.FormattingEnabled = true;
+ this.viewComboBox.Items.AddRange(new object[4]
+ {
+ "Spectrum Analyzer",
+ "Waterfall",
+ "Both",
+ "None"
+ });
+ this.viewComboBox.Location = new Point(92, 3);
+ this.viewComboBox.Name = "viewComboBox";
+ this.viewComboBox.Size = new Size(129, 21);
+ this.viewComboBox.TabIndex = 0;
+ this.viewComboBox.SelectedIndexChanged += this.viewComboBox_SelectedIndexChanged;
+ this.label8.Anchor = AnchorStyles.Left;
+ this.label8.AutoSize = true;
+ this.label8.Location = new Point(3, 34);
+ this.label8.Name = "label8";
+ this.label8.Size = new Size(46, 13);
+ this.label8.TabIndex = 14;
+ this.label8.Text = "Window";
+ this.fftWindowComboBox.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.tableLayoutPanel3.SetColumnSpan(this.fftWindowComboBox, 3);
+ this.fftWindowComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
+ this.fftWindowComboBox.FormattingEnabled = true;
+ this.fftWindowComboBox.Items.AddRange(new object[7]
+ {
+ "None",
+ "Hamming",
+ "Blackman",
+ "Blackman-Harris 4",
+ "Blackman-Harris 7",
+ "Hann-Poisson",
+ "Youssef"
+ });
+ this.fftWindowComboBox.Location = new Point(92, 30);
+ this.fftWindowComboBox.Name = "fftWindowComboBox";
+ this.fftWindowComboBox.Size = new Size(129, 21);
+ this.fftWindowComboBox.TabIndex = 1;
+ this.fftWindowComboBox.SelectedIndexChanged += this.fftWindowComboBox_SelectedIndexChanged;
+ this.label21.Anchor = AnchorStyles.Left;
+ this.label21.AutoSize = true;
+ this.label21.Location = new Point(3, 61);
+ this.label21.Name = "label21";
+ this.label21.Size = new Size(57, 13);
+ this.label21.TabIndex = 18;
+ this.label21.Text = "Resolution";
+ this.fftResolutionComboBox.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.tableLayoutPanel3.SetColumnSpan(this.fftResolutionComboBox, 3);
+ this.fftResolutionComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
+ this.fftResolutionComboBox.FormattingEnabled = true;
+ this.fftResolutionComboBox.Items.AddRange(new object[14]
+ {
+ "512",
+ "1024",
+ "2048",
+ "4096",
+ "8192",
+ "16384",
+ "32768",
+ "65536",
+ "131072",
+ "262144",
+ "524288",
+ "1048576",
+ "2097152",
+ "4194304"
+ });
+ this.fftResolutionComboBox.Location = new Point(92, 57);
+ this.fftResolutionComboBox.Name = "fftResolutionComboBox";
+ this.fftResolutionComboBox.Size = new Size(129, 21);
+ this.fftResolutionComboBox.TabIndex = 2;
+ this.fftResolutionComboBox.SelectedIndexChanged += this.fftResolutionComboBox_SelectedIndexChanged;
+ this.tableLayoutPanel3.SetColumnSpan(this.groupBox1, 4);
+ this.groupBox1.Controls.Add(this.tableLayoutPanel5);
+ this.groupBox1.Dock = DockStyle.Fill;
+ this.groupBox1.Location = new Point(3, 406);
+ this.groupBox1.Name = "groupBox1";
+ this.groupBox1.Size = new Size(218, 75);
+ this.groupBox1.TabIndex = 32;
+ this.groupBox1.TabStop = false;
+ this.groupBox1.Text = "Spectrum";
+ this.tableLayoutPanel5.ColumnCount = 2;
+ this.tableLayoutPanel5.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 40f));
+ this.tableLayoutPanel5.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 60f));
+ this.tableLayoutPanel5.Controls.Add(this.label28, 0, 0);
+ this.tableLayoutPanel5.Controls.Add(this.fftSpeedTrackBar, 1, 0);
+ this.tableLayoutPanel5.Dock = DockStyle.Fill;
+ this.tableLayoutPanel5.Location = new Point(3, 16);
+ this.tableLayoutPanel5.Name = "tableLayoutPanel5";
+ this.tableLayoutPanel5.RowCount = 1;
+ this.tableLayoutPanel5.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
+ this.tableLayoutPanel5.RowStyles.Add(new RowStyle(SizeType.Absolute, 56f));
+ this.tableLayoutPanel5.Size = new Size(212, 56);
+ this.tableLayoutPanel5.TabIndex = 31;
+ this.label28.Anchor = AnchorStyles.Left;
+ this.label28.AutoSize = true;
+ this.label28.Location = new Point(3, 21);
+ this.label28.Name = "label28";
+ this.label28.Size = new Size(38, 13);
+ this.label28.TabIndex = 29;
+ this.label28.Text = "Speed";
+ this.fftSpeedTrackBar.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.fftSpeedTrackBar.Location = new Point(87, 5);
+ this.fftSpeedTrackBar.Maximum = 100;
+ this.fftSpeedTrackBar.Minimum = 1;
+ this.fftSpeedTrackBar.Name = "fftSpeedTrackBar";
+ this.fftSpeedTrackBar.RightToLeftLayout = true;
+ this.fftSpeedTrackBar.Size = new Size(122, 45);
+ this.fftSpeedTrackBar.TabIndex = 2;
+ this.fftSpeedTrackBar.TickFrequency = 10;
+ this.fftSpeedTrackBar.TickStyle = TickStyle.Both;
+ this.fftSpeedTrackBar.Value = 50;
+ this.fftSpeedTrackBar.ValueChanged += this.fftSpeedTrackBar_ValueChanged;
+ this.tableLayoutPanel3.SetColumnSpan(this.smoothingGroupBox, 4);
+ this.smoothingGroupBox.Controls.Add(this.tableLayoutPanel4);
+ this.smoothingGroupBox.Dock = DockStyle.Fill;
+ this.smoothingGroupBox.Location = new Point(3, 163);
+ this.smoothingGroupBox.Name = "smoothingGroupBox";
+ this.smoothingGroupBox.Size = new Size(218, 237);
+ this.smoothingGroupBox.TabIndex = 31;
+ this.smoothingGroupBox.TabStop = false;
+ this.smoothingGroupBox.Text = "Smoothing";
+ this.tableLayoutPanel4.ColumnCount = 2;
+ this.tableLayoutPanel4.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 40f));
+ this.tableLayoutPanel4.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 60f));
+ this.tableLayoutPanel4.Controls.Add(this.label23, 0, 0);
+ this.tableLayoutPanel4.Controls.Add(this.wDecayTrackBar, 1, 3);
+ this.tableLayoutPanel4.Controls.Add(this.wAttackTrackBar, 1, 2);
+ this.tableLayoutPanel4.Controls.Add(this.label25, 0, 3);
+ this.tableLayoutPanel4.Controls.Add(this.sDecayTrackBar, 1, 1);
+ this.tableLayoutPanel4.Controls.Add(this.sAttackTrackBar, 1, 0);
+ this.tableLayoutPanel4.Controls.Add(this.label24, 0, 1);
+ this.tableLayoutPanel4.Controls.Add(this.label26, 0, 2);
+ this.tableLayoutPanel4.Dock = DockStyle.Fill;
+ this.tableLayoutPanel4.Location = new Point(3, 16);
+ this.tableLayoutPanel4.Name = "tableLayoutPanel4";
+ this.tableLayoutPanel4.RowCount = 4;
+ this.tableLayoutPanel4.RowStyles.Add(new RowStyle(SizeType.Percent, 25f));
+ this.tableLayoutPanel4.RowStyles.Add(new RowStyle(SizeType.Percent, 25f));
+ this.tableLayoutPanel4.RowStyles.Add(new RowStyle(SizeType.Percent, 25f));
+ this.tableLayoutPanel4.RowStyles.Add(new RowStyle(SizeType.Percent, 25f));
+ this.tableLayoutPanel4.Size = new Size(212, 218);
+ this.tableLayoutPanel4.TabIndex = 27;
+ this.label23.Anchor = AnchorStyles.Left;
+ this.label23.AutoSize = true;
+ this.label23.Location = new Point(3, 20);
+ this.label23.Name = "label23";
+ this.label23.Size = new Size(48, 13);
+ this.label23.TabIndex = 23;
+ this.label23.Text = "S-Attack";
+ this.wDecayTrackBar.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.wDecayTrackBar.Location = new Point(87, 167);
+ this.wDecayTrackBar.Maximum = 50;
+ this.wDecayTrackBar.Name = "wDecayTrackBar";
+ this.wDecayTrackBar.Size = new Size(122, 45);
+ this.wDecayTrackBar.TabIndex = 8;
+ this.wDecayTrackBar.TickFrequency = 5;
+ this.wDecayTrackBar.TickStyle = TickStyle.Both;
+ this.wDecayTrackBar.ValueChanged += this.wDecayTrackBar_ValueChanged;
+ this.wAttackTrackBar.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.wAttackTrackBar.Location = new Point(87, 112);
+ this.wAttackTrackBar.Maximum = 50;
+ this.wAttackTrackBar.Name = "wAttackTrackBar";
+ this.wAttackTrackBar.Size = new Size(122, 45);
+ this.wAttackTrackBar.TabIndex = 7;
+ this.wAttackTrackBar.TickFrequency = 5;
+ this.wAttackTrackBar.TickStyle = TickStyle.Both;
+ this.wAttackTrackBar.ValueChanged += this.wAttackTrackBar_ValueChanged;
+ this.label25.Anchor = AnchorStyles.Left;
+ this.label25.AutoSize = true;
+ this.label25.Location = new Point(3, 183);
+ this.label25.Name = "label25";
+ this.label25.Size = new Size(52, 13);
+ this.label25.TabIndex = 26;
+ this.label25.Text = "W-Decay";
+ this.sDecayTrackBar.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.sDecayTrackBar.Location = new Point(87, 58);
+ this.sDecayTrackBar.Maximum = 50;
+ this.sDecayTrackBar.Name = "sDecayTrackBar";
+ this.sDecayTrackBar.Size = new Size(122, 45);
+ this.sDecayTrackBar.TabIndex = 6;
+ this.sDecayTrackBar.TickFrequency = 5;
+ this.sDecayTrackBar.TickStyle = TickStyle.Both;
+ this.sDecayTrackBar.ValueChanged += this.sDecayTrackBar_ValueChanged;
+ this.sAttackTrackBar.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.sAttackTrackBar.Location = new Point(87, 4);
+ this.sAttackTrackBar.Maximum = 50;
+ this.sAttackTrackBar.Name = "sAttackTrackBar";
+ this.sAttackTrackBar.Size = new Size(122, 45);
+ this.sAttackTrackBar.TabIndex = 5;
+ this.sAttackTrackBar.TickFrequency = 5;
+ this.sAttackTrackBar.TickStyle = TickStyle.Both;
+ this.sAttackTrackBar.ValueChanged += this.sAttackTrackBar_ValueChanged;
+ this.label24.Anchor = AnchorStyles.Left;
+ this.label24.AutoSize = true;
+ this.label24.Location = new Point(3, 74);
+ this.label24.Name = "label24";
+ this.label24.Size = new Size(48, 13);
+ this.label24.TabIndex = 24;
+ this.label24.Text = "S-Decay";
+ this.label26.Anchor = AnchorStyles.Left;
+ this.label26.AutoSize = true;
+ this.label26.Location = new Point(3, 128);
+ this.label26.Name = "label26";
+ this.label26.Size = new Size(52, 13);
+ this.label26.TabIndex = 25;
+ this.label26.Text = "W-Attack";
+ this.markPeaksCheckBox.Anchor = AnchorStyles.Right;
+ this.markPeaksCheckBox.AutoSize = true;
+ this.markPeaksCheckBox.Location = new Point(3, 140);
+ this.markPeaksCheckBox.Name = "markPeaksCheckBox";
+ this.markPeaksCheckBox.Size = new Size(83, 17);
+ this.markPeaksCheckBox.TabIndex = 23;
+ this.markPeaksCheckBox.Text = "Mark Peaks";
+ this.markPeaksCheckBox.UseVisualStyleBackColor = true;
+ this.markPeaksCheckBox.CheckedChanged += this.markPeaksCheckBox_CheckedChanged;
+ this.useTimestampsCheckBox.Anchor = AnchorStyles.Left;
+ this.useTimestampsCheckBox.AutoSize = true;
+ this.tableLayoutPanel3.SetColumnSpan(this.useTimestampsCheckBox, 2);
+ this.useTimestampsCheckBox.Location = new Point(3, 114);
+ this.useTimestampsCheckBox.Name = "useTimestampsCheckBox";
+ this.useTimestampsCheckBox.Size = new Size(90, 17);
+ this.useTimestampsCheckBox.TabIndex = 3;
+ this.useTimestampsCheckBox.Text = "Time Markers";
+ this.useTimestampsCheckBox.UseVisualStyleBackColor = true;
+ this.useTimestampsCheckBox.CheckedChanged += this.useTimestampCheckBox_CheckedChanged;
+ this.label14.Anchor = AnchorStyles.Right;
+ this.label14.AutoSize = true;
+ this.label14.Location = new Point(139, 116);
+ this.label14.Name = "label14";
+ this.label14.Size = new Size(47, 13);
+ this.label14.TabIndex = 16;
+ this.label14.Text = "Gradient";
+ this.gradientButton.Anchor = AnchorStyles.Right;
+ this.gradientButton.Location = new Point(197, 111);
+ this.gradientButton.Name = "gradientButton";
+ this.gradientButton.Size = new Size(24, 23);
+ this.gradientButton.TabIndex = 4;
+ this.gradientButton.Text = "...";
+ this.gradientButton.UseVisualStyleBackColor = true;
+ this.gradientButton.Click += this.gradientButton_Click;
+ this.label3.Anchor = AnchorStyles.Left;
+ this.label3.AutoSize = true;
+ this.label3.Location = new Point(3, 88);
+ this.label3.Name = "label3";
+ this.label3.Size = new Size(78, 13);
+ this.label3.TabIndex = 33;
+ this.label3.Text = "Spectrum Style";
+ this.spectrumStyleComboBox.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.tableLayoutPanel3.SetColumnSpan(this.spectrumStyleComboBox, 3);
+ this.spectrumStyleComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
+ this.spectrumStyleComboBox.FormattingEnabled = true;
+ this.spectrumStyleComboBox.Items.AddRange(new object[6]
+ {
+ "Dots",
+ "Simple Curve",
+ "Solid Fill",
+ "Static Gradient",
+ "Dynamic Gradient",
+ "Min Max"
+ });
+ this.spectrumStyleComboBox.Location = new Point(92, 84);
+ this.spectrumStyleComboBox.Name = "spectrumStyleComboBox";
+ this.spectrumStyleComboBox.Size = new Size(129, 21);
+ this.spectrumStyleComboBox.TabIndex = 34;
+ this.spectrumStyleComboBox.SelectedIndexChanged += this.spectrumStyleComboBox_SelectedIndexChanged;
+ this.fftOffsetTrackBar.Anchor = (AnchorStyles.Top | AnchorStyles.Bottom);
+ this.fftOffsetTrackBar.Location = new Point(3, 533);
+ this.fftOffsetTrackBar.Maximum = 15;
+ this.fftOffsetTrackBar.Name = "fftOffsetTrackBar";
+ this.fftOffsetTrackBar.Orientation = Orientation.Vertical;
+ this.fftOffsetTrackBar.Size = new Size(45, 154);
+ this.fftOffsetTrackBar.TabIndex = 27;
+ this.fftOffsetTrackBar.TickStyle = TickStyle.Both;
+ this.fftOffsetTrackBar.Scroll += this.fftOffsetTrackBar_Scroll;
+ this.fftRangeTrackBar.Anchor = (AnchorStyles.Top | AnchorStyles.Bottom);
+ this.fftRangeTrackBar.LargeChange = 10;
+ this.fftRangeTrackBar.Location = new Point(3, 356);
+ this.fftRangeTrackBar.Maximum = 15;
+ this.fftRangeTrackBar.Minimum = 1;
+ this.fftRangeTrackBar.Name = "fftRangeTrackBar";
+ this.fftRangeTrackBar.Orientation = Orientation.Vertical;
+ this.fftRangeTrackBar.Size = new Size(45, 151);
+ this.fftRangeTrackBar.TabIndex = 28;
+ this.fftRangeTrackBar.TickStyle = TickStyle.Both;
+ this.fftRangeTrackBar.Value = 13;
+ this.fftRangeTrackBar.Scroll += this.fftRangeTrackBar_Scroll;
+ this.audioGainTrackBar.Anchor = (AnchorStyles.Left | AnchorStyles.Right);
+ this.audioGainTrackBar.Location = new Point(147, 3);
+ this.audioGainTrackBar.Maximum = 60;
+ this.audioGainTrackBar.Minimum = 25;
+ this.audioGainTrackBar.Name = "audioGainTrackBar";
+ this.audioGainTrackBar.Size = new Size(143, 45);
+ this.audioGainTrackBar.TabIndex = 0;
+ this.audioGainTrackBar.TickFrequency = 5;
+ this.audioGainTrackBar.TickStyle = TickStyle.Both;
+ this.audioGainTrackBar.Value = 40;
+ this.audioGainTrackBar.ValueChanged += this.audioGainTrackBar_ValueChanged;
+ this.label17.Anchor = AnchorStyles.None;
+ this.label17.AutoSize = true;
+ this.label17.Location = new Point(6, 340);
+ this.label17.Name = "label17";
+ this.label17.Size = new Size(39, 13);
+ this.label17.TabIndex = 27;
+ this.label17.Text = "Range";
+ this.label17.TextAlign = ContentAlignment.MiddleCenter;
+ this.scrollPanel.AutoScroll = true;
+ this.scrollPanel.Controls.Add(this.controlPanel);
+ this.scrollPanel.Dock = DockStyle.Left;
+ this.scrollPanel.Location = new Point(10, 61);
+ this.scrollPanel.Margin = new Padding(3, 0, 3, 0);
+ this.scrollPanel.Name = "scrollPanel";
+ this.scrollPanel.Size = new Size(246, 690);
+ this.scrollPanel.TabIndex = 28;
+ this.rightTableLayoutPanel.AutoSize = true;
+ this.rightTableLayoutPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
+ this.rightTableLayoutPanel.ColumnCount = 1;
+ this.rightTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
+ this.rightTableLayoutPanel.Controls.Add(this.label2, 0, 6);
+ this.rightTableLayoutPanel.Controls.Add(this.label19, 0, 0);
+ this.rightTableLayoutPanel.Controls.Add(this.label20, 0, 2);
+ this.rightTableLayoutPanel.Controls.Add(this.label17, 0, 4);
+ this.rightTableLayoutPanel.Controls.Add(this.fftZoomTrackBar, 0, 1);
+ this.rightTableLayoutPanel.Controls.Add(this.fftContrastTrackBar, 0, 3);
+ this.rightTableLayoutPanel.Controls.Add(this.fftOffsetTrackBar, 0, 7);
+ this.rightTableLayoutPanel.Controls.Add(this.fftRangeTrackBar, 0, 5);
+ this.rightTableLayoutPanel.Dock = DockStyle.Right;
+ this.rightTableLayoutPanel.Location = new Point(922, 61);
+ this.rightTableLayoutPanel.Margin = new Padding(0);
+ this.rightTableLayoutPanel.Name = "rightTableLayoutPanel";
+ this.rightTableLayoutPanel.RowCount = 8;
+ this.rightTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.rightTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 25f));
+ this.rightTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.rightTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 25f));
+ this.rightTableLayoutPanel.RowStyles.Add(new RowStyle());
+ this.rightTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 25f));
+ this.rightTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Absolute, 20f));
+ this.rightTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 25f));
+ this.rightTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Absolute, 20f));
+ this.rightTableLayoutPanel.Size = new Size(52, 690);
+ this.rightTableLayoutPanel.TabIndex = 33;
+ this.label2.Anchor = AnchorStyles.None;
+ this.label2.AutoSize = true;
+ this.label2.Location = new Point(8, 513);
+ this.label2.Name = "label2";
+ this.label2.Size = new Size(35, 13);
+ this.label2.TabIndex = 28;
+ this.label2.Text = "Offset";
+ this.label2.TextAlign = ContentAlignment.MiddleCenter;
+ this.centerPanel.BackColor = Color.Black;
+ this.centerPanel.Controls.Add(this.spectrumPanel);
+ this.centerPanel.Controls.Add(this.bottomSplitter);
+ this.centerPanel.Controls.Add(this.bottomPluginPanel);
+ this.centerPanel.Controls.Add(this.topSplitter);
+ this.centerPanel.Controls.Add(this.topPluginPanel);
+ this.centerPanel.Controls.Add(this.rightSplitter);
+ this.centerPanel.Controls.Add(this.leftSplitter);
+ this.centerPanel.Controls.Add(this.leftPluginPanel);
+ this.centerPanel.Controls.Add(this.rightPluginPanel);
+ this.centerPanel.Dock = DockStyle.Fill;
+ this.centerPanel.Location = new Point(256, 61);
+ this.centerPanel.Margin = new Padding(0);
+ this.centerPanel.Name = "centerPanel";
+ this.centerPanel.Size = new Size(666, 690);
+ this.centerPanel.TabIndex = 4;
+ this.spectrumPanel.Controls.Add(this.spectrumSplitter);
+ this.spectrumPanel.Controls.Add(this.waterfall);
+ this.spectrumPanel.Controls.Add(this.spectrumAnalyzer);
+ this.spectrumPanel.Dock = DockStyle.Fill;
+ this.spectrumPanel.Location = new Point(4, 4);
+ this.spectrumPanel.Name = "spectrumPanel";
+ this.spectrumPanel.Size = new Size(658, 682);
+ this.spectrumPanel.TabIndex = 38;
+ this.spectrumSplitter.BackColor = SystemColors.Control;
+ this.spectrumSplitter.Dock = DockStyle.Top;
+ this.spectrumSplitter.Location = new Point(0, 324);
+ this.spectrumSplitter.MinExtra = 0;
+ this.spectrumSplitter.MinSize = 0;
+ this.spectrumSplitter.Name = "spectrumSplitter";
+ this.spectrumSplitter.Size = new Size(658, 4);
+ this.spectrumSplitter.TabIndex = 1;
+ this.spectrumSplitter.TabStop = false;
+ this.waterfall.Attack = 0.9f;
+ this.waterfall.BandType = BandType.Center;
+ this.waterfall.CenterFrequency = 0L;
+ this.waterfall.Contrast = 0;
+ this.waterfall.Decay = 0.5f;
+ this.waterfall.DisplayOffset = 0;
+ this.waterfall.DisplayRange = 130;
+ this.waterfall.Dock = DockStyle.Fill;
+ this.waterfall.EnableFilter = true;
+ this.waterfall.EnableFilterMove = true;
+ this.waterfall.EnableFrequencyMarker = true;
+ this.waterfall.EnableHotTracking = true;
+ this.waterfall.EnableSideFilterResize = false;
+ this.waterfall.FilterBandwidth = 10000;
+ this.waterfall.FilterOffset = 0;
+ this.waterfall.Frequency = 0L;
+ this.waterfall.Location = new Point(0, 324);
+ this.waterfall.Name = "waterfall";
+ this.waterfall.Size = new Size(658, 358);
+ this.waterfall.SpectrumWidth = 48000;
+ this.waterfall.StepSize = 0;
+ this.waterfall.TabIndex = 0;
+ this.waterfall.TimestampInterval = 100;
+ this.waterfall.UseSmoothing = true;
+ this.waterfall.UseSnap = false;
+ this.waterfall.UseTimestamps = false;
+ this.waterfall.Zoom = 0;
+ this.waterfall.FrequencyChanged += this.panview_FrequencyChanged;
+ this.waterfall.CenterFrequencyChanged += this.panview_CenterFrequencyChanged;
+ this.waterfall.BandwidthChanged += this.panview_BandwidthChanged;
+ this.waterfall.CustomPaint += this.waterfall_CustomPaint;
+ this.spectrumAnalyzer.Attack = 0.9f;
+ this.spectrumAnalyzer.BandType = BandType.Center;
+ this.spectrumAnalyzer.CenterFrequency = 0L;
+ this.spectrumAnalyzer.Contrast = 0;
+ this.spectrumAnalyzer.Decay = 0.3f;
+ this.spectrumAnalyzer.DisplayOffset = 0;
+ this.spectrumAnalyzer.DisplayRange = 130;
+ this.spectrumAnalyzer.Dock = DockStyle.Top;
+ this.spectrumAnalyzer.EnableFilter = true;
+ this.spectrumAnalyzer.EnableFilterMove = true;
+ this.spectrumAnalyzer.EnableFrequencyMarker = true;
+ this.spectrumAnalyzer.EnableHotTracking = true;
+ this.spectrumAnalyzer.EnableSideFilterResize = false;
+ this.spectrumAnalyzer.EnableSNR = true;
+ this.spectrumAnalyzer.FilterBandwidth = 10000;
+ this.spectrumAnalyzer.FilterOffset = 100;
+ this.spectrumAnalyzer.Frequency = 0L;
+ this.spectrumAnalyzer.Location = new Point(0, 0);
+ this.spectrumAnalyzer.MarkPeaks = false;
+ this.spectrumAnalyzer.Name = "spectrumAnalyzer";
+ this.spectrumAnalyzer.Size = new Size(658, 324);
+ this.spectrumAnalyzer.SpectrumStyle = SpectrumStyle.StaticGradient;
+ this.spectrumAnalyzer.SpectrumWidth = 48000;
+ this.spectrumAnalyzer.StatusText = null;
+ this.spectrumAnalyzer.StepSize = 1000;
+ this.spectrumAnalyzer.TabIndex = 0;
+ this.spectrumAnalyzer.UseSmoothing = true;
+ this.spectrumAnalyzer.UseSnap = false;
+ this.spectrumAnalyzer.UseStepSizeForDisplay = false;
+ this.spectrumAnalyzer.Zoom = 0;
+ this.spectrumAnalyzer.FrequencyChanged += this.panview_FrequencyChanged;
+ this.spectrumAnalyzer.CenterFrequencyChanged += this.panview_CenterFrequencyChanged;
+ this.spectrumAnalyzer.BandwidthChanged += this.panview_BandwidthChanged;
+ this.spectrumAnalyzer.CustomPaint += this.spectrumAnalyzer_CustomPaint;
+ this.spectrumAnalyzer.BackgroundCustomPaint += this.spectrumAnalyzer_BackgroundCustomPaint;
+ this.bottomSplitter.BackColor = SystemColors.Control;
+ this.bottomSplitter.Dock = DockStyle.Bottom;
+ this.bottomSplitter.Location = new Point(4, 686);
+ this.bottomSplitter.MinSize = 0;
+ this.bottomSplitter.Name = "bottomSplitter";
+ this.bottomSplitter.Size = new Size(658, 4);
+ this.bottomSplitter.TabIndex = 42;
+ this.bottomSplitter.TabStop = false;
+ this.bottomSplitter.SplitterMoved += this.pluginSplitter_SplitterMoved;
+ this.bottomPluginPanel.BackColor = SystemColors.Control;
+ this.bottomPluginPanel.ColumnCount = 1;
+ this.bottomPluginPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
+ this.bottomPluginPanel.Dock = DockStyle.Bottom;
+ this.bottomPluginPanel.Location = new Point(4, 690);
+ this.bottomPluginPanel.Margin = new Padding(0);
+ this.bottomPluginPanel.Name = "bottomPluginPanel";
+ this.bottomPluginPanel.RowCount = 1;
+ this.bottomPluginPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
+ this.bottomPluginPanel.Size = new Size(658, 0);
+ this.bottomPluginPanel.TabIndex = 41;
+ this.topSplitter.BackColor = SystemColors.Control;
+ this.topSplitter.Dock = DockStyle.Top;
+ this.topSplitter.Location = new Point(4, 0);
+ this.topSplitter.MinSize = 0;
+ this.topSplitter.Name = "topSplitter";
+ this.topSplitter.Size = new Size(658, 4);
+ this.topSplitter.TabIndex = 40;
+ this.topSplitter.TabStop = false;
+ this.topSplitter.SplitterMoved += this.pluginSplitter_SplitterMoved;
+ this.topPluginPanel.BackColor = SystemColors.Control;
+ this.topPluginPanel.ColumnCount = 1;
+ this.topPluginPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
+ this.topPluginPanel.Dock = DockStyle.Top;
+ this.topPluginPanel.Location = new Point(4, 0);
+ this.topPluginPanel.Margin = new Padding(0);
+ this.topPluginPanel.Name = "topPluginPanel";
+ this.topPluginPanel.RowCount = 1;
+ this.topPluginPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
+ this.topPluginPanel.Size = new Size(658, 0);
+ this.topPluginPanel.TabIndex = 39;
+ this.rightSplitter.BackColor = SystemColors.Control;
+ this.rightSplitter.Dock = DockStyle.Right;
+ this.rightSplitter.Location = new Point(662, 0);
+ this.rightSplitter.MinSize = 0;
+ this.rightSplitter.Name = "rightSplitter";
+ this.rightSplitter.Size = new Size(4, 690);
+ this.rightSplitter.TabIndex = 37;
+ this.rightSplitter.TabStop = false;
+ this.rightSplitter.SplitterMoved += this.pluginSplitter_SplitterMoved;
+ this.leftSplitter.BackColor = SystemColors.Control;
+ this.leftSplitter.Location = new Point(0, 0);
+ this.leftSplitter.MinSize = 0;
+ this.leftSplitter.Name = "leftSplitter";
+ this.leftSplitter.Size = new Size(4, 690);
+ this.leftSplitter.TabIndex = 36;
+ this.leftSplitter.TabStop = false;
+ this.leftSplitter.SplitterMoved += this.pluginSplitter_SplitterMoved;
+ this.leftPluginPanel.BackColor = SystemColors.Control;
+ this.leftPluginPanel.ColumnCount = 1;
+ this.leftPluginPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
+ this.leftPluginPanel.Dock = DockStyle.Left;
+ this.leftPluginPanel.Location = new Point(0, 0);
+ this.leftPluginPanel.Margin = new Padding(0);
+ this.leftPluginPanel.Name = "leftPluginPanel";
+ this.leftPluginPanel.RowCount = 1;
+ this.leftPluginPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
+ this.leftPluginPanel.Size = new Size(0, 690);
+ this.leftPluginPanel.TabIndex = 1;
+ this.rightPluginPanel.BackColor = SystemColors.Control;
+ this.rightPluginPanel.ColumnCount = 1;
+ this.rightPluginPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
+ this.rightPluginPanel.Dock = DockStyle.Right;
+ this.rightPluginPanel.Location = new Point(666, 0);
+ this.rightPluginPanel.Margin = new Padding(0);
+ this.rightPluginPanel.Name = "rightPluginPanel";
+ this.rightPluginPanel.RowCount = 1;
+ this.rightPluginPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
+ this.rightPluginPanel.Size = new Size(0, 690);
+ this.rightPluginPanel.TabIndex = 3;
+ this.settingsTableLayoutPanel.ColumnCount = 8;
+ this.settingsTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
+ this.settingsTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
+ this.settingsTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
+ this.settingsTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
+ this.settingsTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
+ this.settingsTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
+ this.settingsTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
+ this.settingsTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
+ this.settingsTableLayoutPanel.Controls.Add(this.playStopButton, 1, 0);
+ this.settingsTableLayoutPanel.Controls.Add(this.configureSourceButton, 2, 0);
+ this.settingsTableLayoutPanel.Controls.Add(this.toggleMenuButton, 0, 0);
+ this.settingsTableLayoutPanel.Controls.Add(this.muteButton, 3, 0);
+ this.settingsTableLayoutPanel.Controls.Add(this.audioGainTrackBar, 4, 0);
+ this.settingsTableLayoutPanel.Controls.Add(this.vfoFrequencyEdit, 5, 0);
+ this.settingsTableLayoutPanel.Controls.Add(this.tuningStyleButton, 6, 0);
+ this.settingsTableLayoutPanel.Controls.Add(this.logoPictureBox, 7, 0);
+ this.settingsTableLayoutPanel.Dock = DockStyle.Top;
+ this.settingsTableLayoutPanel.Location = new Point(10, 10);
+ this.settingsTableLayoutPanel.Margin = new Padding(0);
+ this.settingsTableLayoutPanel.Name = "settingsTableLayoutPanel";
+ this.settingsTableLayoutPanel.RowCount = 1;
+ this.settingsTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
+ this.settingsTableLayoutPanel.Size = new Size(964, 51);
+ this.settingsTableLayoutPanel.TabIndex = 33;
+ this.playStopButton.Anchor = AnchorStyles.None;
+ this.playStopButton.FlatAppearance.BorderSize = 0;
+ this.playStopButton.FlatStyle = FlatStyle.Flat;
+ this.playStopButton.Image = Resources.sdr_start;
+ this.playStopButton.Location = new Point(39, 10);
+ this.playStopButton.Name = "playStopButton";
+ this.playStopButton.Size = new Size(30, 30);
+ this.playStopButton.TabIndex = 0;
+ this.playStopButton.Click += this.playStopButton_Click;
+ this.configureSourceButton.Anchor = AnchorStyles.None;
+ this.configureSourceButton.FlatAppearance.BorderSize = 0;
+ this.configureSourceButton.FlatStyle = FlatStyle.Flat;
+ this.configureSourceButton.Image = Resources.config_gear;
+ this.configureSourceButton.Location = new Point(75, 12);
+ this.configureSourceButton.Name = "configureSourceButton";
+ this.configureSourceButton.Size = new Size(30, 26);
+ this.configureSourceButton.TabIndex = 0;
+ this.configureSourceButton.UseVisualStyleBackColor = true;
+ this.configureSourceButton.Click += this.frontendGuiButton_Click;
+ this.toggleMenuButton.Anchor = AnchorStyles.None;
+ this.toggleMenuButton.FlatAppearance.BorderSize = 0;
+ this.toggleMenuButton.FlatStyle = FlatStyle.Flat;
+ this.toggleMenuButton.Image = Resources.toggle_menu;
+ this.toggleMenuButton.Location = new Point(3, 10);
+ this.toggleMenuButton.Name = "toggleMenuButton";
+ this.toggleMenuButton.Size = new Size(30, 30);
+ this.toggleMenuButton.TabIndex = 0;
+ this.toggleMenuButton.Click += this.toggleMenuButton_Click;
+ this.muteButton.Anchor = AnchorStyles.None;
+ this.muteButton.FlatAppearance.BorderSize = 0;
+ this.muteButton.FlatStyle = FlatStyle.Flat;
+ this.muteButton.Image = Resources.audio_unmuted;
+ this.muteButton.Location = new Point(111, 10);
+ this.muteButton.Name = "muteButton";
+ this.muteButton.Size = new Size(30, 30);
+ this.muteButton.TabIndex = 1;
+ this.muteButton.UseVisualStyleBackColor = true;
+ this.muteButton.Click += this.muteButton_Click;
+ this.vfoFrequencyEdit.Anchor = AnchorStyles.Left;
+ this.vfoFrequencyEdit.AutoSize = true;
+ this.vfoFrequencyEdit.AutoSizeMode = AutoSizeMode.GrowAndShrink;
+ this.vfoFrequencyEdit.BackColor = Color.Transparent;
+ this.vfoFrequencyEdit.DisableFrequencyEvents = false;
+ this.vfoFrequencyEdit.Frequency = 0L;
+ this.vfoFrequencyEdit.Location = new Point(296, 13);
+ this.vfoFrequencyEdit.Name = "vfoFrequencyEdit";
+ this.vfoFrequencyEdit.Size = new Size(274, 25);
+ this.vfoFrequencyEdit.StepSize = 0;
+ this.vfoFrequencyEdit.TabIndex = 1;
+ this.vfoFrequencyEdit.FrequencyChanged += this.vfoFrequencyEdit_FrequencyChanged;
+ this.vfoFrequencyEdit.FrequencyChanging += this.vfoFrequencyEdit_FrequencyChanging;
+ this.tuningStyleButton.Anchor = AnchorStyles.Left;
+ this.tuningStyleButton.FlatAppearance.BorderSize = 0;
+ this.tuningStyleButton.FlatStyle = FlatStyle.Flat;
+ this.tuningStyleButton.Image = Resources.free_tuning;
+ this.tuningStyleButton.Location = new Point(576, 10);
+ this.tuningStyleButton.Name = "tuningStyleButton";
+ this.tuningStyleButton.Size = new Size(30, 30);
+ this.tuningStyleButton.TabIndex = 2;
+ this.tuningStyleButton.Click += this.centerButton_Click;
+ this.logoPictureBox.Anchor = AnchorStyles.Right;
+ this.logoPictureBox.Cursor = Cursors.Hand;
+ //this.logoPictureBox.Image = (Image)componentResourceManager.GetObject("logoPictureBox.Image");
+ this.logoPictureBox.Location = new Point(842, 3);
+ this.logoPictureBox.Name = "logoPictureBox";
+ this.logoPictureBox.Size = new Size(119, 45);
+ this.logoPictureBox.SizeMode = PictureBoxSizeMode.Zoom;
+ this.logoPictureBox.TabIndex = 3;
+ this.logoPictureBox.TabStop = false;
+ this.logoPictureBox.Click += this.logoPictureBox_Click;
+ this.menuSpacerPanel.Dock = DockStyle.Left;
+ this.menuSpacerPanel.Location = new Point(256, 61);
+ this.menuSpacerPanel.Name = "menuSpacerPanel";
+ this.menuSpacerPanel.Size = new Size(3, 690);
+ this.menuSpacerPanel.TabIndex = 34;
+ base.AutoScaleDimensions = new SizeF(96f, 96f);
+ base.AutoScaleMode = AutoScaleMode.Dpi;
+ base.ClientSize = new Size(984, 761);
+ base.Controls.Add(this.menuSpacerPanel);
+ base.Controls.Add(this.centerPanel);
+ base.Controls.Add(this.rightTableLayoutPanel);
+ base.Controls.Add(this.scrollPanel);
+ base.Controls.Add(this.settingsTableLayoutPanel);
+ base.KeyPreview = true;
+ base.Name = "MainForm";
+ base.Padding = new Padding(10);
+ base.SizeGripStyle = SizeGripStyle.Show;
+ base.StartPosition = FormStartPosition.Manual;
+ this.Text = "SDR#";
+ base.Closing += this.MainForm_Closing;
+ base.Load += this.MainForm_Load;
+ base.Move += this.MainForm_Move;
+ base.Resize += this.MainForm_Resize;
+ ((ISupportInitialize)this.fftContrastTrackBar).EndInit();
+ ((ISupportInitialize)this.fftZoomTrackBar).EndInit();
+ this.controlPanel.ResumeLayout(false);
+ this.sourceCollapsiblePanel.Content.ResumeLayout(false);
+ this.sourceCollapsiblePanel.Content.PerformLayout();
+ this.sourceCollapsiblePanel.ResumeLayout(false);
+ this.sourceTableLayoutPanel.ResumeLayout(false);
+ this.radioCollapsiblePanel.Content.ResumeLayout(false);
+ this.radioCollapsiblePanel.ResumeLayout(false);
+ this.radioTableLayoutPanel.ResumeLayout(false);
+ this.radioTableLayoutPanel.PerformLayout();
+ this.tableLayoutPanel7.ResumeLayout(false);
+ this.tableLayoutPanel7.PerformLayout();
+ ((ISupportInitialize)this.cwShiftNumericUpDown).EndInit();
+ ((ISupportInitialize)this.squelchNumericUpDown).EndInit();
+ ((ISupportInitialize)this.filterBandwidthNumericUpDown).EndInit();
+ ((ISupportInitialize)this.filterOrderNumericUpDown).EndInit();
+ ((ISupportInitialize)this.frequencyShiftNumericUpDown).EndInit();
+ this.audioCollapsiblePanel.Content.ResumeLayout(false);
+ this.audioCollapsiblePanel.ResumeLayout(false);
+ this.tableLayoutPanel1.ResumeLayout(false);
+ this.tableLayoutPanel1.PerformLayout();
+ ((ISupportInitialize)this.latencyNumericUpDown).EndInit();
+ this.agcCollapsiblePanel.Content.ResumeLayout(false);
+ this.agcCollapsiblePanel.ResumeLayout(false);
+ this.tableLayoutPanel2.ResumeLayout(false);
+ this.tableLayoutPanel2.PerformLayout();
+ ((ISupportInitialize)this.agcSlopeNumericUpDown).EndInit();
+ ((ISupportInitialize)this.agcDecayNumericUpDown).EndInit();
+ ((ISupportInitialize)this.agcThresholdNumericUpDown).EndInit();
+ this.fftCollapsiblePanel.Content.ResumeLayout(false);
+ this.fftCollapsiblePanel.ResumeLayout(false);
+ this.tableLayoutPanel3.ResumeLayout(false);
+ this.tableLayoutPanel3.PerformLayout();
+ this.groupBox1.ResumeLayout(false);
+ this.tableLayoutPanel5.ResumeLayout(false);
+ this.tableLayoutPanel5.PerformLayout();
+ ((ISupportInitialize)this.fftSpeedTrackBar).EndInit();
+ this.smoothingGroupBox.ResumeLayout(false);
+ this.tableLayoutPanel4.ResumeLayout(false);
+ this.tableLayoutPanel4.PerformLayout();
+ ((ISupportInitialize)this.wDecayTrackBar).EndInit();
+ ((ISupportInitialize)this.wAttackTrackBar).EndInit();
+ ((ISupportInitialize)this.sDecayTrackBar).EndInit();
+ ((ISupportInitialize)this.sAttackTrackBar).EndInit();
+ ((ISupportInitialize)this.fftOffsetTrackBar).EndInit();
+ ((ISupportInitialize)this.fftRangeTrackBar).EndInit();
+ ((ISupportInitialize)this.audioGainTrackBar).EndInit();
+ this.scrollPanel.ResumeLayout(false);
+ this.scrollPanel.PerformLayout();
+ this.rightTableLayoutPanel.ResumeLayout(false);
+ this.rightTableLayoutPanel.PerformLayout();
+ this.centerPanel.ResumeLayout(false);
+ this.spectrumPanel.ResumeLayout(false);
+ this.settingsTableLayoutPanel.ResumeLayout(false);
+ this.settingsTableLayoutPanel.PerformLayout();
+ ((ISupportInitialize)this.logoPictureBox).EndInit();
+ base.ResumeLayout(false);
+ base.PerformLayout();
+ }
+
+ public MainForm()
+ {
+ this._hookManager = new HookManager();
+ this._hookManager.RegisterStreamHook(this._iqBalancerProcessor, ProcessorType.RawIQ);
+ this._streamControl = new StreamControl(this._hookManager);
+ this._vfo = new Vfo(this._hookManager);
+ this._sharpControlProxy = new SharpControlProxy(this);
+ this.InitializeComponent();
+ this.InitializeGUI();
+ }
+
+ private unsafe void InitializeGUI()
+ {
+ this._initializing = true;
+ base.Icon = Resources.mainicon;
+ this._sourcePanelHeight = this.sourceCollapsiblePanel.Height;
+ this._modeStates[DetectorType.WFM] = Utils.GetIntArraySetting("wfmState", MainForm._defaultWFMState);
+ this._modeStates[DetectorType.NFM] = Utils.GetIntArraySetting("nfmState", MainForm._defaultNFMState);
+ this._modeStates[DetectorType.AM] = Utils.GetIntArraySetting("amState", MainForm._defaultAMState);
+ this._modeStates[DetectorType.LSB] = Utils.GetIntArraySetting("lsbState", MainForm._defaultSSBState);
+ this._modeStates[DetectorType.USB] = Utils.GetIntArraySetting("usbState", MainForm._defaultSSBState);
+ this._modeStates[DetectorType.DSB] = Utils.GetIntArraySetting("dsbState", MainForm._defaultDSBState);
+ this._modeStates[DetectorType.CW] = Utils.GetIntArraySetting("cwState", MainForm._defaultCWState);
+ this._modeStates[DetectorType.RAW] = Utils.GetIntArraySetting("rawState", MainForm._defaultRAWState);
+ ThreadPool.QueueUserWorkItem(this.TuneThreadProc);
+ string stringSetting = Utils.GetStringSetting("stepSizes", "1 Hz,10 Hz,100 Hz,500 Hz,1 kHz,2.5 kHz,5 kHz,6.25 kHz,7.5 kHz,8.33 kHz,9 kHz,10 kHz,12.5 kHz,15 kHz,20 kHz,25 kHz,30 kHz,50 kHz,100 kHz,150 kHz,200 kHz,250 kHz,300 kHz,350 kHz,400 kHz,450 kHz,500 kHz");
+ this.stepSizeComboBox.Items.AddRange(stringSetting.Split(','));
+ this._tuningStyle = (TuningStyle)Utils.GetIntSetting("tuningStyle", 0);
+ int num = 0;
+ int num2 = -1;
+ List devices = AudioDevice.GetDevices(DeviceDirection.Input);
+ string stringSetting2 = Utils.GetStringSetting("inputDevice", string.Empty);
+ for (int i = 0; i < devices.Count; i++)
+ {
+ this.inputDeviceComboBox.Items.Add(devices[i]);
+ if (devices[i].IsDefault)
+ {
+ num = i;
+ }
+ if (devices[i].ToString() == stringSetting2)
+ {
+ num2 = i;
+ }
+ }
+ if (this.inputDeviceComboBox.Items.Count > 0)
+ {
+ this.inputDeviceComboBox.SelectedIndex = ((num2 >= 0) ? num2 : num);
+ }
+ num = 0;
+ devices = AudioDevice.GetDevices(DeviceDirection.Output);
+ stringSetting2 = Utils.GetStringSetting("outputDevice", string.Empty);
+ for (int j = 0; j < devices.Count; j++)
+ {
+ this.outputDeviceComboBox.Items.Add(devices[j]);
+ if (devices[j].IsDefault)
+ {
+ num = j;
+ }
+ if (devices[j].ToString() == stringSetting2)
+ {
+ num2 = j;
+ }
+ }
+ if (this.outputDeviceComboBox.Items.Count > 0)
+ {
+ this.outputDeviceComboBox.SelectedIndex = ((num2 >= 0) ? num2 : num);
+ }
+ this._streamControl.BufferNeeded += this.ProcessBuffer;
+ this.DetectorType = (DetectorType)Utils.GetIntSetting("detectorType", 2);
+ this.modeRadioButton_CheckStateChanged(null, null);
+ this.filterBandwidthNumericUpDown_ValueChanged(null, null);
+ this.filterOrderNumericUpDown_ValueChanged(null, null);
+ this.filterTypeComboBox_SelectedIndexChanged(null, null);
+ this.cwShiftNumericUpDown_ValueChanged(null, null);
+ this.agcCheckBox.Checked = Utils.GetBooleanSetting("useAGC");
+ this.agcCheckBox_CheckedChanged(null, null);
+ this.agcThresholdNumericUpDown.Value = Utils.GetIntSetting("agcThreshold", -100);
+ this.agcThresholdNumericUpDown_ValueChanged(null, null);
+ this.agcDecayNumericUpDown.Value = Utils.GetIntSetting("agcDecay", 100);
+ this.agcDecayNumericUpDown_ValueChanged(null, null);
+ this.agcSlopeNumericUpDown.Value = Utils.GetIntSetting("agcSlope", 0);
+ this.agcSlopeNumericUpDown_ValueChanged(null, null);
+ this.agcUseHangCheckBox.Checked = Utils.GetBooleanSetting("agcHang");
+ this.agcUseHangCheckBox_CheckedChanged(null, null);
+ this.ResetFrequency(0L);
+ this.frequencyShiftNumericUpDown.Value = Utils.GetLongSetting("frequencyShift", 0L);
+ this.frequencyShiftNumericUpDown_ValueChanged(null, null);
+ this.frequencyShiftCheckBox.Checked = Utils.GetBooleanSetting("frequencyShiftEnabled");
+ this.frequencyShiftCheckBox_CheckStateChanged(null, null);
+ this.swapIQCheckBox.Checked = Utils.GetBooleanSetting("swapIQ");
+ this.swapIQCheckBox_CheckedChanged(null, null);
+ this.correctIQCheckBox.Checked = Utils.GetBooleanSetting("correctIQ");
+ this.autoCorrectIQCheckBox_CheckStateChanged(null, null);
+ this.markPeaksCheckBox.Checked = Utils.GetBooleanSetting("markPeaks");
+ this.markPeaksCheckBox_CheckedChanged(null, null);
+ this.fmStereoCheckBox.Checked = Utils.GetBooleanSetting("fmStereo");
+ this.fmStereoCheckBox_CheckedChanged(null, null);
+ this.filterAudioCheckBox.Checked = Utils.GetBooleanSetting("filterAudio");
+ this.filterAudioCheckBox_CheckStateChanged(null, null);
+ this.unityGainCheckBox.Checked = Utils.GetBooleanSetting("unityGain");
+ this.unityGainCheckBox_CheckStateChanged(null, null);
+ this.audioGainTrackBar.Value = Utils.GetIntSetting("audioGain", 50);
+ this.audioGainTrackBar_ValueChanged(null, null);
+ this._vfo.Muted = Utils.GetBooleanSetting("AudioIsMuted");
+ this.UpdateMuteButton();
+ this.latencyNumericUpDown.Value = Utils.GetIntSetting("latency", 100);
+ this.sampleRateComboBox.Text = Utils.GetStringSetting("sampleRate", "48000 sample/sec");
+ base.WindowState = (FormWindowState)Utils.GetIntSetting("windowState", 0);
+ this._usableSpectrumWidth = Utils.GetIntSetting("spectrumWidth", 48000);
+ this.spectrumAnalyzer.SpectrumWidth = this._usableSpectrumWidth;
+ this.waterfall.SpectrumWidth = this._usableSpectrumWidth;
+ this.lockCarrierCheckBox.Checked = Utils.GetBooleanSetting("lockCarrier");
+ this.lockCarrierCheckBox_CheckedChanged(null, null);
+ this.useAntiFadingCheckBox.Checked = Utils.GetBooleanSetting("useAntiFading");
+ this.useAntiFadingCheckBox_CheckedChanged(null, null);
+ int[] intArraySetting = Utils.GetIntArraySetting("windowPosition", null);
+ if (intArraySetting != null)
+ {
+ this._lastLocation.X = intArraySetting[0];
+ this._lastLocation.Y = intArraySetting[1];
+ base.Location = this._lastLocation;
+ }
+ else
+ {
+ this._lastLocation = base.Location;
+ }
+ int[] intArraySetting2 = Utils.GetIntArraySetting("windowSize", null);
+ if (intArraySetting2 != null)
+ {
+ this._lastSize.Width = intArraySetting2[0];
+ this._lastSize.Height = intArraySetting2[1];
+ base.Size = this._lastSize;
+ }
+ else
+ {
+ this._lastSize = base.Size;
+ }
+ this.spectrumSplitter.SplitPosition = Utils.GetIntSetting("splitterPosition", this.spectrumSplitter.SplitPosition);
+ this._waterfallTimer = new System.Windows.Forms.Timer(this.components);
+ this._waterfallTimer.Tick += this.waterfallTimer_Tick;
+ this._waterfallTimer.Enabled = true;
+ this._spectrumAnalyzerTimer = new System.Windows.Forms.Timer(this.components);
+ this._spectrumAnalyzerTimer.Tick += this.spectrumAnalyzerTimer_Tick;
+ this._spectrumAnalyzerTimer.Interval = 20;
+ this._spectrumAnalyzerTimer.Enabled = true;
+ this.viewComboBox.SelectedIndex = Utils.GetIntSetting("fftView", 2);
+ this.fftResolutionComboBox.SelectedIndex = Utils.GetIntSetting("fftResolution", 6);
+ this.fftWindowComboBox.SelectedIndex = Utils.GetIntSetting("fftWindowType", 3);
+ this.spectrumStyleComboBox.SelectedIndex = Utils.GetIntSetting("spectrumStyle", 3);
+ this.spectrumStyleComboBox_SelectedIndexChanged(null, null);
+ this.fftSpeedTrackBar.Value = Utils.GetIntSetting("fftSpeed", 40);
+ this.fftSpeedTrackBar_ValueChanged(null, null);
+ this.fftContrastTrackBar.Value = Utils.GetIntSetting("fftContrast", 0);
+ this.fftContrastTrackBar_Changed(null, null);
+ this.spectrumAnalyzer.Attack = (float)Utils.GetDoubleSetting("spectrumAnalyzer.attack", 0.5);
+ this.sAttackTrackBar.Value = (int)(this.spectrumAnalyzer.Attack * (float)this.sAttackTrackBar.Maximum);
+ this.spectrumAnalyzer.Decay = (float)Utils.GetDoubleSetting("spectrumAnalyzer.decay", 0.45);
+ this.sDecayTrackBar.Value = (int)(this.spectrumAnalyzer.Decay * (float)this.sDecayTrackBar.Maximum);
+ this.waterfall.Attack = (float)Utils.GetDoubleSetting("waterfall.attack", 0.9);
+ this.wAttackTrackBar.Value = (int)(this.waterfall.Attack * (float)this.wAttackTrackBar.Maximum);
+ this.waterfall.Decay = (float)Utils.GetDoubleSetting("waterfall.decay", 0.5);
+ this.wDecayTrackBar.Value = (int)(this.waterfall.Decay * (float)this.wDecayTrackBar.Maximum);
+ this.useTimestampsCheckBox.Checked = Utils.GetBooleanSetting("useTimeMarkers");
+ this.useTimestampCheckBox_CheckedChanged(null, null);
+ this.fftOffsetTrackBar.Value = Utils.GetIntSetting("fftDisplayOffset", 0);
+ this.fftOffsetTrackBar_Scroll(null, null);
+ this.fftRangeTrackBar.Value = Utils.GetIntSetting("fftDisplayRange", 13);
+ this.fftRangeTrackBar_Scroll(null, null);
+ this.LoadSource("AIRSPY", new AirspyIO(), 2147483647);
+ this.LoadSource("AIRSPY HF+", new AirspyHFIO(), 2147483647);
+ this.LoadSource("Spy Server", new SpyServerIO(), 2147483647);
+ this.LoadSource("UHD / USRP", "SDRSharp.USRP.UsrpIO,SDRSharp.USRP", 10);
+ this.LoadSource("HackRF", "SDRSharp.HackRF.HackRFIO,SDRSharp.HackRF", 10);
+ this.LoadSource("RTL-SDR (R820T)", "SDRSharp.R820T.RtlSdrIO,SDRSharp.R820T", 10);
+ this.LoadSource("RTL-SDR (USB)", "SDRSharp.RTLSDR.RtlSdrIO,SDRSharp.RTLSDR", 10);
+ this.LoadSource("RTL-SDR (TCP)", "SDRSharp.RTLTCP.RtlTcpIO,SDRSharp.RTLTCP", 10);
+ this.LoadSource("FUNcube Dongle Pro", "SDRSharp.FUNcube.FunCubeIO,SDRSharp.FUNcube", 10);
+ this.LoadSource("FUNcube Dongle Pro+", "SDRSharp.FUNcubeProPlus.FunCubeProPlusIO,SDRSharp.FUNcubeProPlus", 10);
+ this.LoadSource("SoftRock (Si570)", "SDRSharp.SoftRock.SoftRockIO,SDRSharp.SoftRock", 10);
+ this.LoadSource("RFSPACE SDR-IQ (USB)", "SDRSharp.SDRIQ.SdrIqIO,SDRSharp.SDRIQ", 10);
+ this.LoadSource("RFSPACE Networked Radios", "SDRSharp.SDRIP.SdrIpIO,SDRSharp.SDRIP", 10);
+ this.LoadSource("AFEDRI Networked Radios", "SDRSharp.AfedriSDRNet.AfedriSdrNetIO,SDRSharp.AfedriSDRNet", 10);
+ this.LoadSource("File Player", "SDRSharp.WAVPlayer.WAVFileIO,SDRSharp.WAVPlayer", 10);
+ NameValueCollection nameValueCollection = (NameValueCollection)ConfigurationManager.GetSection("frontendPlugins");
+ foreach (string key in nameValueCollection.Keys)
+ {
+ string text = nameValueCollection[key];
+ if (!this.IsFrontEndTypeLoaded(text))
+ {
+ this.LoadSource(key, text, 0);
+ }
+ }
+ this.iqSourceComboBox.Items.Add("IQ File (*.wav)");
+ this.iqSourceComboBox.Items.Add("IQ from Sound Card");
+ this._waveFile = Utils.GetStringSetting("waveFile", string.Empty);
+ int intSetting = Utils.GetIntSetting("iqSource", this.iqSourceComboBox.Items.Count - 1);
+ this.iqSourceComboBox.SelectedIndex = ((intSetting < this.iqSourceComboBox.Items.Count) ? intSetting : (this.iqSourceComboBox.Items.Count - 1));
+ this.ResetFrequency(Utils.GetLongSetting("centerFrequency", this._centerFrequency));
+ long longSetting = Utils.GetLongSetting("vfo", this._centerFrequency);
+ this.vfoFrequencyEdit.Frequency = longSetting;
+ this._tooltip.SetToolTip(this.playStopButton, "Start");
+ this._tooltip.SetToolTip(this.logoPictureBox, "Visit our website and check our high performance radios!");
+ this.UpdateTuningStyle();
+ bool visible = !Utils.GetBooleanSetting("menuIsHidden");
+ this.scrollPanel.Visible = visible;
+ this.menuSpacerPanel.Visible = visible;
+ this.rightTableLayoutPanel.Visible = visible;
+ this._tooltip.SetToolTip(this.toggleMenuButton, "Menu");
+ this._initializing = false;
+ }
+
+ private void LoadSource(string name, string fqdn, int access)
+ {
+ try
+ {
+ IFrontendController controller = (IFrontendController)this.LoadExtension(fqdn);
+ this.LoadSource(name, controller, access);
+ }
+ catch (Exception)
+ {
+ }
+ }
+
+ private void LoadSource(string name, IFrontendController controller, int access)
+ {
+ if (access > 0)
+ {
+ ISampleRateChangeSource sampleRateChangeSource = controller as ISampleRateChangeSource;
+ if (sampleRateChangeSource != null)
+ {
+ sampleRateChangeSource.SampleRateChanged += this.frontendController_SampleRateChanged;
+ }
+ IFFTSource iFFTSource = controller as IFFTSource;
+ if (iFFTSource != null)
+ {
+ iFFTSource.FFTAvailable += this.frontendController_FFTAvailable;
+ }
+ IControlAwareObject controlAwareObject = controller as IControlAwareObject;
+ if (controlAwareObject != null)
+ {
+ controlAwareObject.SetControl(this._sharpControlProxy);
+ }
+ this._builtinControllers.Add(controller);
+ }
+ this._frontendControllers.Add(name, controller);
+ this.iqSourceComboBox.Items.Add(name);
+ }
+
+ private void MainForm_Load(object sender, EventArgs e)
+ {
+ this.InitialiseSharpPlugins();
+ this.scrollPanel.VerticalScroll.Value = Utils.GetIntSetting("menuPosition", 0);
+ }
+
+ private bool IsFrontEndTypeLoaded(string fqtn)
+ {
+ fqtn = fqtn.Replace(" ", string.Empty);
+ foreach (IFrontendController value in this._frontendControllers.Values)
+ {
+ if (value.GetType().AssemblyQualifiedName.Replace(" ", string.Empty).StartsWith(fqtn))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private object LoadExtension(string fqtn)
+ {
+ string[] array = fqtn.Split(',');
+ string typeName = array[0].Trim();
+ string assemblyString = array[1].Trim();
+ Assembly assembly = Assembly.Load(assemblyString);
+ return assembly.CreateInstance(typeName);
+ }
+
+ private void frontendController_SampleRateChanged(object sender, EventArgs e)
+ {
+ if (base.InvokeRequired)
+ {
+ base.BeginInvoke((Action)delegate
+ {
+ this.frontendController_SampleRateChanged(sender, e);
+ });
+ }
+ else
+ {
+ if (this._streamControl.IsPlaying)
+ {
+ this._changingSampleRate = true;
+ try
+ {
+ this.StopRadio();
+ this.StartRadio();
+ }
+ catch (ApplicationException ex)
+ {
+ this.StopRadio();
+ MessageBox.Show(this, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
+ }
+ finally
+ {
+ this._changingSampleRate = false;
+ }
+ }
+ this.ResetFrequency(this._centerFrequency);
+ this.fftZoomTrackBar.Value = 0;
+ }
+ }
+
+ private void MainForm_Closing(object sender, CancelEventArgs e)
+ {
+ this._terminated = true;
+ this._streamControl.Stop();
+ this._fftEvent.Set();
+ if (this._frontendController != null)
+ {
+ this._frontendController.Close();
+ }
+ foreach (ISharpPlugin value in this._sharpPlugins.Values)
+ {
+ value.Close();
+ }
+ this._modeStates[this._vfo.DetectorType] = this.GetModeState();
+ Utils.SaveSetting("spectrumAnalyzer.attack", (float)this.sAttackTrackBar.Value / (float)this.sAttackTrackBar.Maximum);
+ Utils.SaveSetting("spectrumAnalyzer.decay", (float)this.sDecayTrackBar.Value / (float)this.sDecayTrackBar.Maximum);
+ Utils.SaveSetting("waterfall.Attack", (float)this.wAttackTrackBar.Value / (float)this.wAttackTrackBar.Maximum);
+ Utils.SaveSetting("waterfall.decay", (float)this.wDecayTrackBar.Value / (float)this.wDecayTrackBar.Maximum);
+ Utils.SaveSetting("useTimeMarkers", this.useTimestampsCheckBox.Checked);
+ Utils.SaveSetting("fftSpeed", this.fftSpeedTrackBar.Value);
+ Utils.SaveSetting("fftContrast", this.fftContrastTrackBar.Value);
+ Utils.SaveSetting("fftWindowType", this.fftWindowComboBox.SelectedIndex);
+ Utils.SaveSetting("spectrumStyle", this.spectrumStyleComboBox.SelectedIndex);
+ Utils.SaveSetting("fftView", this.viewComboBox.SelectedIndex);
+ Utils.SaveSetting("fftResolution", this.fftResolutionComboBox.SelectedIndex);
+ Utils.SaveSetting("detectorType", (int)this.DetectorType);
+ Utils.SaveSetting("useAGC", this.agcCheckBox.Checked);
+ Utils.SaveSetting("agcThreshold", (int)this.agcThresholdNumericUpDown.Value);
+ Utils.SaveSetting("agcDecay", (int)this.agcDecayNumericUpDown.Value);
+ Utils.SaveSetting("agcSlope", (int)this.agcSlopeNumericUpDown.Value);
+ Utils.SaveSetting("agcHang", this.agcUseHangCheckBox.Checked);
+ Utils.SaveSetting("frequencyShift", (long)this.frequencyShiftNumericUpDown.Value);
+ Utils.SaveSetting("frequencyShiftEnabled", this.frequencyShiftCheckBox.Checked);
+ Utils.SaveSetting("swapIQ", this.swapIQCheckBox.Checked);
+ Utils.SaveSetting("correctIQ", this.correctIQCheckBox.Checked);
+ Utils.SaveSetting("markPeaks", this.markPeaksCheckBox.Checked);
+ Utils.SaveSetting("fmStereo", this.fmStereoCheckBox.Checked);
+ Utils.SaveSetting("filterAudio", this.filterAudioCheckBox.Checked);
+ Utils.SaveSetting("unityGain", this.unityGainCheckBox.Checked);
+ Utils.SaveSetting("latency", (int)this.latencyNumericUpDown.Value);
+ Utils.SaveSetting("sampleRate", this.sampleRateComboBox.Text);
+ Utils.SaveSetting("audioGain", this.audioGainTrackBar.Value);
+ Utils.SaveSetting("AudioIsMuted", this._vfo.Muted);
+ Utils.SaveSetting("windowState", (int)base.WindowState);
+ Utils.SaveSetting("windowPosition", Utils.IntArrayToString(this._lastLocation.X, this._lastLocation.Y));
+ Utils.SaveSetting("windowSize", Utils.IntArrayToString(this._lastSize.Width, this._lastSize.Height));
+ Utils.SaveSetting("collapsiblePanelStates", Utils.IntArrayToString(this.GetCollapsiblePanelStates()));
+ Utils.SaveSetting("splitterPosition", this.spectrumSplitter.SplitPosition);
+ Utils.SaveSetting("iqSource", this.iqSourceComboBox.SelectedIndex);
+ Utils.SaveSetting("waveFile", this._waveFile ?? "");
+ Utils.SaveSetting("centerFrequency", this._centerFrequency);
+ Utils.SaveSetting("vfo", this.vfoFrequencyEdit.Frequency);
+ Utils.SaveSetting("tuningStyle", (int)this._tuningStyle);
+ Utils.SaveSetting("fftDisplayOffset", this.fftOffsetTrackBar.Value);
+ Utils.SaveSetting("fftDisplayRange", this.fftRangeTrackBar.Value);
+ Utils.SaveSetting("inputDevice", this.inputDeviceComboBox.SelectedItem);
+ Utils.SaveSetting("outputDevice", this.outputDeviceComboBox.SelectedItem);
+ Utils.SaveSetting("spectrumWidth", this.spectrumAnalyzer.SpectrumWidth);
+ Utils.SaveSetting("lockCarrier", this.lockCarrierCheckBox.Checked);
+ Utils.SaveSetting("useAntiFading", this.useAntiFadingCheckBox.Checked);
+ Utils.SaveSetting("menuPosition", this.scrollPanel.VerticalScroll.Value);
+ Utils.SaveSetting("wfmState", Utils.IntArrayToString(this._modeStates[DetectorType.WFM]));
+ Utils.SaveSetting("nfmState", Utils.IntArrayToString(this._modeStates[DetectorType.NFM]));
+ Utils.SaveSetting("amState", Utils.IntArrayToString(this._modeStates[DetectorType.AM]));
+ Utils.SaveSetting("lsbState", Utils.IntArrayToString(this._modeStates[DetectorType.LSB]));
+ Utils.SaveSetting("usbState", Utils.IntArrayToString(this._modeStates[DetectorType.USB]));
+ Utils.SaveSetting("dsbState", Utils.IntArrayToString(this._modeStates[DetectorType.DSB]));
+ Utils.SaveSetting("cwState", Utils.IntArrayToString(this._modeStates[DetectorType.CW]));
+ Utils.SaveSetting("rawState", Utils.IntArrayToString(this._modeStates[DetectorType.RAW]));
+ if (this._oldTopSplitterPosition > 0)
+ {
+ Utils.SaveSetting("topSplitter", this._oldTopSplitterPosition);
+ }
+ if (this._oldBottomSplitterPosition > 0)
+ {
+ Utils.SaveSetting("bottomSplitter", this._oldBottomSplitterPosition);
+ }
+ if (this._oldLeftSplitterPosition > 0)
+ {
+ Utils.SaveSetting("leftSplitter", this._oldLeftSplitterPosition);
+ }
+ if (this._oldRightSplitterPosition > 0)
+ {
+ Utils.SaveSetting("rightSplitter", this._oldRightSplitterPosition);
+ }
+ Utils.SaveSetting("menuIsHidden", !this.scrollPanel.Visible);
+ }
+
+ private void toggleMenuButton_Click(object sender, EventArgs e)
+ {
+ this.scrollPanel.Visible = !this.scrollPanel.Visible;
+ this.menuSpacerPanel.Visible = this.scrollPanel.Visible;
+ this.rightTableLayoutPanel.Visible = this.scrollPanel.Visible;
+ }
+
+ private unsafe void ProcessBuffer(Complex* iqBuffer, float* audioBuffer, int length)
+ {
+ if (!this.UseFFTSource && this.spectrumPanel.Visible)
+ {
+ this._inputBufferLength = length;
+ this._fftStream.Write(iqBuffer, length);
+ }
+ this._vfo.ProcessBuffer(iqBuffer, audioBuffer, length);
+ }
+
+ private unsafe void ProcessFFT(object parameter)
+ {
+ this._fftIsRunning = true;
+ while (this._streamControl.IsPlaying && this.spectrumPanel.Visible)
+ {
+ this._fftResolutionLock.AcquireReaderLock(300000);
+ double num = (double)this._fftBins / ((double)this._waterfallTimer.Interval * 0.001);
+ double num2 = this._streamControl.SampleRate / num;
+ int num3 = (int)((double)this._fftBins * num2);
+ int num4 = Math.Min(num3, this._fftBins);
+ int count = num3 - num4;
+ if (num4 < this._fftBins)
+ {
+ Utils.Memcpy(this._iqPtr, this._iqPtr + num4, (this._fftBins - num4) * sizeof(Complex));
+ }
+ int num5 = num4;
+ int num6 = 0;
+ while (this._streamControl.IsPlaying && !this._terminated && num6 < num5)
+ {
+ int count2 = num5 - num6;
+ num6 += this._fftStream.Read(this._iqPtr, this._fftBins - num5 + num6, count2);
+ }
+ if (this._streamControl.IsPlaying && !this._terminated)
+ {
+ this._fftStream.Advance(count);
+ float num7 = (float)(10.0 * Math.Log10((double)this._fftBins / 2.0));
+ float offset = 24f - num7 + this._fftOffset;
+ Utils.Memcpy(this._fftPtr, this._iqPtr, this._fftBins * sizeof(Complex));
+ Fourier.ApplyFFTWindow(this._fftPtr, this._fftWindowPtr, this._fftBins);
+ Fourier.ForwardTransform(this._fftPtr, this._fftBins, true);
+ Fourier.SpectrumPower(this._fftPtr, this._fftSpectrumPtr, this._fftBins, offset);
+ if (num4 < this._fftBins)
+ {
+ int num8 = this._fftBins - num4;
+ while (this._streamControl.IsPlaying && !this._terminated && this._fftStream.Length > this._inputBufferLength * 2 && this._fftStream.Length >= num8)
+ {
+ this._fftStream.Read(this._iqPtr + num4, num8);
+ }
+ }
+ else
+ {
+ while (this._streamControl.IsPlaying && !this._terminated && this._fftStream.Length > this._inputBufferLength * 2 && this._fftStream.Length >= this._fftBins)
+ {
+ this._fftStream.Advance(this._fftBins);
+ }
+ }
+ this._fftResolutionLock.ReleaseReaderLock();
+ if (this._streamControl.IsPlaying && !this._terminated)
+ {
+ this._fftEvent.WaitOne();
+ }
+ continue;
+ }
+ this._fftResolutionLock.ReleaseReaderLock();
+ break;
+ }
+ this._fftStream.Flush();
+ this._fftIsRunning = false;
+ }
+
+ private unsafe void frontendController_FFTAvailable(object sender, ByteSamplesEventArgs e)
+ {
+ IFFTSource iFFTSource = this._frontendController as IFFTSource;
+ if (this._fftFrames == null || this._fftFrames.BufferSize != e.Length)
+ {
+ this._fftFrames = new FloatCircularBuffer(e.Length, 3);
+ }
+ float* ptr = stackalloc float[e.Length];
+ for (int i = 0; i < e.Length; i++)
+ {
+ ptr[i] = (float)(e.Buffer[i] * iFFTSource.FFTRange) / 255f - (float)iFFTSource.FFTRange + (float)iFFTSource.FFTOffset;
+ }
+ if (!this._fftFrames.Write(ptr, e.Length, false) && this._fftcorrectionFPS < 10)
+ {
+ this._fftcorrectionFPS++;
+ }
+ Interlocked.Increment(ref this._fftFramesCount);
+ }
+
+ private unsafe void RenderFFT()
+ {
+ if (this.spectrumPanel.Visible)
+ {
+ if (this.UseFFTSource)
+ {
+ FloatCircularBuffer fftFrames = this._fftFrames;
+ if (fftFrames != null)
+ {
+ float* ptr = fftFrames.Acquire(false);
+ if (ptr != null)
+ {
+ Utils.Memcpy(this._fftDisplayPtr, ptr, fftFrames.BufferSize * 4);
+ fftFrames.Release();
+ }
+ }
+ }
+ if (this.waterfall.Visible)
+ {
+ this._fftResolutionLock.AcquireReaderLock(300000);
+ this.waterfall.Render(this._fftDisplayPtr, this._fftDisplaySize);
+ this._fftResolutionLock.ReleaseReaderLock();
+ }
+ }
+ }
+
+ private void waterfallTimer_Tick(object sender, EventArgs e)
+ {
+ if (this._streamControl.IsPlaying)
+ {
+ this.RenderFFT();
+ if (this.UseFFTSource)
+ {
+ DateTime now = DateTime.Now;
+ float num = (float)(now - this._lastFFTTick).TotalMilliseconds;
+ this._lastFFTTick = now;
+ int num2 = Interlocked.Exchange(ref this._fftFramesCount, 0);
+ if (num2 == 0 && this._fftcorrectionFPS > -10)
+ {
+ this._fftcorrectionFPS--;
+ }
+ float num3 = (float)num2 / (num * 0.001f);
+ this._fftAverageFPS += 0.1f * (num3 - this._fftAverageFPS);
+ int num4 = (int)this._fftAverageFPS + this._fftcorrectionFPS;
+ if (num4 > 0 && num4 <= 1000)
+ {
+ this._waterfallTimer.Interval = 1000 / num4;
+ }
+ }
+ else
+ {
+ this._fftEvent.Set();
+ }
+ }
+ }
+
+ private unsafe void spectrumAnalyzerTimer_Tick(object sender, EventArgs e)
+ {
+ if (this._streamControl.IsPlaying && this.spectrumAnalyzer.Visible && this.spectrumPanel.Visible)
+ {
+ this._fftResolutionLock.AcquireReaderLock(300000);
+ this.spectrumAnalyzer.Render(this._fftDisplayPtr, this._fftDisplaySize);
+ this._fftResolutionLock.ReleaseReaderLock();
+ }
+ }
+
+ private void iqTimer_Tick(object sender, EventArgs e)
+ {
+ if (this._vfo.DetectorType == DetectorType.WFM)
+ {
+ if (this._vfo.SignalIsStereo)
+ {
+ this.spectrumAnalyzer.StatusText = "((( " + this._vfo.RdsStationName + " )))";
+ }
+ else
+ {
+ this.spectrumAnalyzer.StatusText = this._vfo.RdsStationName;
+ }
+ if (this._vfo.RdsPICode != 0)
+ {
+ SpectrumAnalyzer obj = this.spectrumAnalyzer;
+ obj.StatusText = obj.StatusText + " - " + string.Format("{0:X4}", this._vfo.RdsPICode);
+ }
+ if (!string.IsNullOrEmpty(this._vfo.RdsStationText))
+ {
+ SpectrumAnalyzer obj2 = this.spectrumAnalyzer;
+ obj2.StatusText = obj2.StatusText + " - " + this._vfo.RdsStationText;
+ }
+ }
+ }
+
+ private unsafe void BuildFFTWindow()
+ {
+ if (!this.UseFFTSource)
+ {
+ float[] array = FilterBuilder.MakeWindow(this._fftWindowType, this._fftBins);
+ float[] array2 = array;
+ fixed (float* src = array2)
+ {
+ Utils.Memcpy(this._fftWindow, src, this._fftBins * 4);
+ }
+ }
+ }
+
+ private unsafe void InitFFTBuffers()
+ {
+ int length = this.UseFFTSource ? (this._frontendController as IFFTSource).DisplayPixels : this._fftBins;
+ this._iqBuffer = null;
+ this._fftBuffer = null;
+ this._fftWindow = null;
+ this._fftSpectrum = null;
+ this._scaledFFTSpectrum = null;
+ GC.Collect();
+ this._iqBuffer = UnsafeBuffer.Create(length, sizeof(Complex));
+ this._fftBuffer = UnsafeBuffer.Create(length, sizeof(Complex));
+ this._fftWindow = UnsafeBuffer.Create(length, 4);
+ this._fftSpectrum = UnsafeBuffer.Create(length, 4);
+ this._scaledFFTSpectrum = UnsafeBuffer.Create(length, 1);
+ this._iqPtr = (Complex*)(void*)this._iqBuffer;
+ this._fftPtr = (Complex*)(void*)this._fftBuffer;
+ this._fftWindowPtr = (float*)(void*)this._fftWindow;
+ this._fftSpectrumPtr = (float*)(void*)this._fftSpectrum;
+ this._scaledFFTSpectrumPtr = (byte*)(void*)this._scaledFFTSpectrum;
+ this.InitDisplayFFT();
+ }
+
+ private unsafe void InitDisplayFFT()
+ {
+ if (this.UseFFTSource)
+ {
+ this._fftDisplaySize = (this._frontendController as IFFTSource).DisplayPixels;
+ this._fftDisplayPtr = this._fftSpectrumPtr;
+ }
+ else
+ {
+ double num = 0.5 * (double)this._fftBins * (1.0 - (double)this._usableSpectrumWidth / this._vfo.SampleRate);
+ double num2 = (double)this._fftBins / this._vfo.SampleRate * (double)this._ifOffset;
+ double a = num - num2;
+ this._fftDisplaySize = (int)((double)this._fftBins - 2.0 * num);
+ this._fftDisplayPtr = this._fftSpectrumPtr + (int)Math.Ceiling(a);
+ }
+ }
+
+ private void iqSourceComboBox_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ this.sourceCollapsiblePanel.PanelTitle = "Source: " + this.iqSourceComboBox.SelectedItem;
+ this.Text = MainForm._baseTitle + " - " + this.iqSourceComboBox.SelectedItem;
+ if (this._streamControl.IsPlaying)
+ {
+ this.StopRadio();
+ }
+ try
+ {
+ this.Open(true);
+ }
+ catch (Exception ex)
+ {
+ if (this.SourceIsWaveFile && !this._initializing)
+ {
+ MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
+ }
+ }
+ this.NotifyPropertyChanged("SourceName");
+ }
+
+ private void SelectWaveFile()
+ {
+ if (this.openDlg.ShowDialog() == DialogResult.OK)
+ {
+ this.StopRadio();
+ this._waveFile = this.openDlg.FileName;
+ }
+ }
+
+ private void OpenWaveSource(bool refreshSource)
+ {
+ if (!this._initializing & refreshSource)
+ {
+ this.SelectWaveFile();
+ }
+ this._tooltip.SetToolTip(this.configureSourceButton, "Select File");
+ this.configureSourceButton.Enabled = true;
+ this.sampleRateComboBox.Enabled = false;
+ this.inputDeviceComboBox.Enabled = false;
+ this.outputDeviceComboBox.Enabled = true;
+ this.latencyNumericUpDown.Enabled = true;
+ this.frequencyShiftCheckBox.Enabled = false;
+ this.frequencyShiftCheckBox.Checked = false;
+ this.frequencyShiftNumericUpDown.Enabled = false;
+ this._tuningStyle = TuningStyle.Free;
+ this.UpdateTuningStyle();
+ StreamControl.ReducedBandwidth = false;
+ AudioDevice audioDevice = (AudioDevice)this.outputDeviceComboBox.SelectedItem;
+ if (audioDevice == null)
+ {
+ throw new ApplicationException("No audio playback device found.");
+ }
+ this._streamControl.OpenFile(this._waveFile, audioDevice.Index, (int)this.latencyNumericUpDown.Value);
+ string input = Path.GetFileName(this._waveFile) ?? "";
+ Match match = Regex.Match(input, "([0-9]+)kHz", RegexOptions.IgnoreCase);
+ long num;
+ if (match.Success)
+ {
+ num = int.Parse(match.Groups[1].Value) * 1000;
+ }
+ else
+ {
+ match = Regex.Match(input, "([\\-0-9]+)Hz", RegexOptions.IgnoreCase);
+ num = ((!match.Success) ? 0 : long.Parse(match.Groups[1].Value));
+ }
+ this._ifOffset = 0;
+ if (num > 0)
+ {
+ match = Regex.Match(input, "([\\-0-9]+)o", RegexOptions.IgnoreCase);
+ if (match.Success)
+ {
+ this._ifOffset = int.Parse(match.Groups[1].Value);
+ num -= this._ifOffset;
+ }
+ }
+ if (refreshSource)
+ {
+ this.ResetFrequency(num);
+ }
+ this._usableSpectrumRatio = 0.9f;
+ this._usableSpectrumWidth = (int)Math.Ceiling(this._streamControl.SampleRate * (double)this._usableSpectrumRatio);
+ this.NotifyPropertyChanged("TunableBandwidth");
+ this.InitDisplayFFT();
+ this.NotifyPropertyChanged("IFOffset");
+ }
+
+ private void OpenSoundCardSource()
+ {
+ this._tooltip.SetToolTip(this.configureSourceButton, string.Empty);
+ this.configureSourceButton.Enabled = false;
+ this.inputDeviceComboBox.Enabled = true;
+ this.outputDeviceComboBox.Enabled = true;
+ this.sampleRateComboBox.Enabled = true;
+ this.frequencyShiftCheckBox.Checked = false;
+ this.frequencyShiftCheckBox.Enabled = false;
+ this.frequencyShiftNumericUpDown.Enabled = false;
+ this.tuningStyleButton.Enabled = false;
+ this._tuningStyle = TuningStyle.Free;
+ this.UpdateTuningStyle();
+ StreamControl.ReducedBandwidth = false;
+ AudioDevice audioDevice = (AudioDevice)this.outputDeviceComboBox.SelectedItem;
+ if (audioDevice == null)
+ {
+ throw new ApplicationException("No audio playback device found.");
+ }
+ AudioDevice audioDevice2 = (AudioDevice)this.inputDeviceComboBox.SelectedItem;
+ if (audioDevice == null)
+ {
+ throw new ApplicationException("No audio recording device found.");
+ }
+ double audioInputSampleRate = this.GetAudioInputSampleRate();
+ this._streamControl.OpenSoundDevice(audioDevice2.Index, audioDevice.Index, audioInputSampleRate, (int)this.latencyNumericUpDown.Value);
+ this._usableSpectrumRatio = 0.9f;
+ this._usableSpectrumWidth = (int)Math.Ceiling(this._streamControl.SampleRate * (double)this._usableSpectrumRatio);
+ this.NotifyPropertyChanged("TunableBandwidth");
+ this._ifOffset = 0;
+ this.NotifyPropertyChanged("IFOffset");
+ this.ResetFrequency(0L);
+ }
+
+ private void OpenFrontEndSource(bool refreshSource, FrequencyInitType init)
+ {
+ AudioDevice audioDevice = (AudioDevice)this.outputDeviceComboBox.SelectedItem;
+ if (audioDevice == null)
+ {
+ throw new ApplicationException("No audio playback device found.");
+ }
+ string key = (string)this.iqSourceComboBox.SelectedItem;
+ this._frontendController = this._frontendControllers[key];
+ this.UpdateVFOSource();
+ if (refreshSource)
+ {
+ try
+ {
+ this._frontendController.Open();
+ }
+ catch (Exception)
+ {
+ }
+ if (this._builtinControllers.Contains(this._frontendController))
+ {
+ IConfigurationPanelProvider configurationPanelProvider = this._frontendController as IConfigurationPanelProvider;
+ if (configurationPanelProvider != null && configurationPanelProvider.Gui != null)
+ {
+ this.ShowControllerPanel(configurationPanelProvider.Gui);
+ }
+ }
+ }
+ bool flag = this._frontendController is ITunableSource && ((ITunableSource)this._frontendController).CanTune;
+ if (flag)
+ {
+ this.tuningStyleButton.Enabled = true;
+ }
+ else
+ {
+ this.frequencyShiftCheckBox.Checked = false;
+ this.tuningStyleButton.Enabled = false;
+ this._tuningStyle = TuningStyle.Free;
+ this.UpdateTuningStyle();
+ }
+ this._tooltip.SetToolTip(this.configureSourceButton, "Configure Source");
+ this.configureSourceButton.Enabled = true;
+ this.frequencyShiftCheckBox.Enabled = flag;
+ this.frequencyShiftNumericUpDown.Enabled = this.frequencyShiftCheckBox.Checked;
+ this.sampleRateComboBox.Enabled = (this._frontendController is ISoundcardController);
+ this.inputDeviceComboBox.Enabled = (this._frontendController is ISoundcardController);
+ this.outputDeviceComboBox.Enabled = true;
+ if (this._frontendController is ISpectrumProvider)
+ {
+ this._usableSpectrumRatio = ((ISpectrumProvider)this._frontendController).UsableSpectrumRatio;
+ }
+ else
+ {
+ this._usableSpectrumRatio = 0.9f;
+ }
+ if (this._frontendController is ISoundcardController)
+ {
+ string soundCardHint = ((ISoundcardController)this._frontendController).SoundCardHint;
+ if (!string.IsNullOrEmpty(soundCardHint))
+ {
+ Regex regex = new Regex(soundCardHint, RegexOptions.IgnoreCase);
+ for (int i = 0; i < this.inputDeviceComboBox.Items.Count; i++)
+ {
+ string input = this.inputDeviceComboBox.Items[i].ToString();
+ if (regex.IsMatch(input))
+ {
+ this.inputDeviceComboBox.SelectedIndex = i;
+ break;
+ }
+ }
+ }
+ AudioDevice audioDevice2 = (AudioDevice)this.inputDeviceComboBox.SelectedItem;
+ if (audioDevice == null)
+ {
+ throw new ApplicationException("No audio recording device found.");
+ }
+ double num;
+ if (refreshSource && !this._initializing)
+ {
+ num = ((ISoundcardController)this._frontendController).SampleRateHint;
+ this.sampleRateComboBox.Text = num + " sample/sec";
+ }
+ else
+ {
+ Match match = Regex.Match(this.sampleRateComboBox.Text, "([0-9\\.]+)", RegexOptions.IgnoreCase);
+ num = ((!match.Success) ? 48000.0 : double.Parse(match.Groups[1].Value));
+ }
+ this._streamControl.OpenSoundDevice(audioDevice2.Index, audioDevice.Index, num, (int)this.latencyNumericUpDown.Value);
+ }
+ else
+ {
+ this._streamControl.OpenPlugin(this._frontendController, audioDevice.Index, (int)this.latencyNumericUpDown.Value);
+ }
+ if (this.UseFFTSource)
+ {
+ this._usableSpectrumWidth = (this._frontendController as IFFTSource).DisplayBandwidth;
+ }
+ else
+ {
+ this._usableSpectrumWidth = (int)Math.Ceiling((double)this._usableSpectrumRatio * this._streamControl.SampleRate);
+ }
+ if (this._frontendController is IIQStreamController)
+ {
+ int num2 = (this._frontendController is IFrontendOffset) ? ((IFrontendOffset)this._frontendController).Offset : 0;
+ this._ifOffset = (((((IIQStreamController)this._frontendController).Samplerate - (double)this._usableSpectrumWidth) * 0.5 >= (double)Math.Abs(num2)) ? num2 : 0);
+ }
+ else
+ {
+ this._ifOffset = 0;
+ }
+ switch (init)
+ {
+ case FrequencyInitType.Vfo:
+ this.ResetFrequency(this.vfoFrequencyEdit.Frequency);
+ break;
+ case FrequencyInitType.Device:
+ if (this._frontendController is ITunableSource)
+ {
+ this.ResetFrequency(((ITunableSource)this._frontendController).Frequency - this._ifOffset);
+ }
+ break;
+ }
+ this.NotifyPropertyChanged("IFOffset");
+ this.NotifyPropertyChanged("TunableBandwidth");
+ }
+
+ public void RefreshSource(bool reload)
+ {
+ this.Open(reload);
+ }
+
+ private void Open(bool refreshSource)
+ {
+ bool flag = true;
+ if (refreshSource)
+ {
+ string text = (string)this.iqSourceComboBox.SelectedItem;
+ if (text != this._lastSourceName)
+ {
+ if (this._frontendController != null)
+ {
+ if (this._frontendController is IFloatingConfigDialogProvider)
+ {
+ ((IFloatingConfigDialogProvider)this._frontendController).HideSettingGUI();
+ }
+ this._frontendController.Close();
+ if (this._builtinControllers.Contains(this._frontendController) && this._frontendController is IConfigurationPanelProvider)
+ {
+ IConfigurationPanelProvider configurationPanelProvider = (IConfigurationPanelProvider)this._frontendController;
+ if (configurationPanelProvider.Gui != null)
+ {
+ this.HideControllerPanel(configurationPanelProvider.Gui);
+ }
+ }
+ this._frontendController = null;
+ }
+ this._lastSourceName = text;
+ flag = false;
+ }
+ }
+ if (this.SourceIsWaveFile)
+ {
+ this.OpenWaveSource(refreshSource);
+ }
+ else if (this.SourceIsSoundCard)
+ {
+ this.OpenSoundCardSource();
+ }
+ else
+ {
+ FrequencyInitType init = flag ? ((!(this._frontendController is IFFTSource) || (this._frontendController as IFFTSource).FFTEnabled) ? (this._changingSampleRate ? FrequencyInitType.Vfo : FrequencyInitType.None) : FrequencyInitType.None) : FrequencyInitType.Device;
+ this.OpenFrontEndSource(refreshSource, init);
+ }
+ if (this.UseFFTSource)
+ {
+ this.fftResolutionComboBox.Enabled = false;
+ this.fftWindowComboBox.Enabled = false;
+ this.fftSpeedTrackBar.Enabled = false;
+ this.sAttackTrackBar.Enabled = false;
+ this.sDecayTrackBar.Enabled = false;
+ this.wAttackTrackBar.Enabled = false;
+ this.wDecayTrackBar.Enabled = false;
+ this.spectrumAnalyzer.Attack = 0.25f;
+ this.spectrumAnalyzer.Decay = 0.15f;
+ this.waterfall.Attack = 0.95f;
+ this.waterfall.Decay = 0.95f;
+ this._fftAverageFPS = 50f;
+ this._fftcorrectionFPS = 0;
+ this._lastFFTTick = DateTime.Now;
+ }
+ else
+ {
+ this.fftResolutionComboBox.Enabled = true;
+ this.fftWindowComboBox.Enabled = true;
+ this.fftSpeedTrackBar.Enabled = true;
+ this.sAttackTrackBar.Enabled = true;
+ this.sDecayTrackBar.Enabled = true;
+ this.wAttackTrackBar.Enabled = true;
+ this.fftSpeedTrackBar_ValueChanged(null, null);
+ this.sAttackTrackBar_ValueChanged(null, null);
+ this.sDecayTrackBar_ValueChanged(null, null);
+ this.wAttackTrackBar_ValueChanged(null, null);
+ this.wDecayTrackBar.Enabled = true;
+ this.wDecayTrackBar_ValueChanged(null, null);
+ }
+ if (this._streamControl.SampleRate > 0.0)
+ {
+ this._vfo.SampleRate = this._streamControl.SampleRate;
+ this._vfo.DecimationStageCount = this._streamControl.DecimationStageCount;
+ this.spectrumAnalyzer.SpectrumWidth = this._usableSpectrumWidth;
+ this.waterfall.SpectrumWidth = this._usableSpectrumWidth;
+ this.UpdateFilterBandwidth();
+ }
+ if (refreshSource)
+ {
+ this.fftZoomTrackBar.Value = 0;
+ this.fftZoomTrackBar_ValueChanged(null, null);
+ }
+ this._frequencySet = 0L;
+ this.InitFFTBuffers();
+ this.BuildFFTWindow();
+ this.UpdateVfoFrequency();
+ this._sharpControlProxy.Enabled = (this.SourceIsSoundCard || this.SourceIsWaveFile || this._builtinControllers.Contains(this._frontendController));
+ this._iqBalancerProcessor.Engine.Enabled = (this.correctIQCheckBox.Checked && this._sharpControlProxy.Enabled);
+ this.spectrumAnalyzer.EnableSNR = this._sharpControlProxy.Enabled;
+ this._vfo.HookdEnabled = this._sharpControlProxy.Enabled;
+ if (this._vfo.SampleRate > 0.0)
+ {
+ this._vfo.Init();
+ }
+ this._vfo.RdsReset();
+ }
+
+ private double GetAudioInputSampleRate()
+ {
+ double result = 0.0;
+ Match match = Regex.Match(this.sampleRateComboBox.Text, "([0-9\\.]+)", RegexOptions.IgnoreCase);
+ if (match.Success)
+ {
+ result = double.Parse(match.Groups[1].Value);
+ }
+ return result;
+ }
+
+ private void audioGainTrackBar_ValueChanged(object sender, EventArgs e)
+ {
+ this._streamControl.AudioGain = (float)this.audioGainTrackBar.Value;
+ if (this.audioGainTrackBar.Value == this.audioGainTrackBar.Minimum)
+ {
+ this._vfo.Muted = true;
+ this._tooltip.SetToolTip(this.audioGainTrackBar, "Muted");
+ }
+ else
+ {
+ this._vfo.Muted = false;
+ this._tooltip.SetToolTip(this.audioGainTrackBar, this.audioGainTrackBar.Value + " dB");
+ }
+ this.UpdateMuteButton();
+ this.NotifyPropertyChanged("AudioGain");
+ }
+
+ private void filterAudioCheckBox_CheckStateChanged(object sender, EventArgs e)
+ {
+ this._vfo.FilterAudio = this.filterAudioCheckBox.Checked;
+ this.NotifyPropertyChanged("FilterAudio");
+ }
+
+ private void unityGainCheckBox_CheckStateChanged(object sender, EventArgs e)
+ {
+ this._streamControl.ScaleOutput = !this.unityGainCheckBox.Checked;
+ this.audioGainTrackBar.Enabled = !this.unityGainCheckBox.Checked;
+ this.NotifyPropertyChanged("UnityGain");
+ }
+
+ private void muteButton_Click(object sender, EventArgs e)
+ {
+ if (this._vfo.Muted && this.audioGainTrackBar.Value == this.audioGainTrackBar.Minimum)
+ {
+ this.audioGainTrackBar.Value = 30;
+ this._vfo.Muted = false;
+ }
+ else
+ {
+ this._vfo.Muted = !this._vfo.Muted;
+ }
+ this.UpdateMuteButton();
+ }
+
+ private void UpdateMuteButton()
+ {
+ if (this._vfo.Muted)
+ {
+ this.muteButton.Image = Resources.audio_muted;
+ this._tooltip.SetToolTip(this.muteButton, "Unmute");
+ }
+ else
+ {
+ this.muteButton.Image = Resources.audio_unmuted;
+ this._tooltip.SetToolTip(this.muteButton, "Mute");
+ }
+ }
+
+ private void playStopButton_Click(object sender, EventArgs e)
+ {
+ try
+ {
+ if (this._streamControl.IsPlaying)
+ {
+ this.StopRadio();
+ }
+ else
+ {
+ this.StartRadio();
+ }
+ }
+ catch (Exception ex)
+ {
+ this.StopRadio();
+ MessageBox.Show(this, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
+ }
+ }
+
+ private void centerButton_Click(object sender, EventArgs e)
+ {
+ int num = (int)(this._tuningStyle = (TuningStyle)((int)(this._tuningStyle + 1) % 3));
+ this.UpdateTuningStyle();
+ }
+
+ private void UpdateTuningStyle()
+ {
+ this.tuningStyleButton.Enabled = !this._tuningStyleFreezed;
+ switch (this._tuningStyle)
+ {
+ case TuningStyle.Free:
+ this.tuningStyleButton.Image = Resources.free_tuning;
+ this._tooltip.SetToolTip(this.tuningStyleButton, "Free tuning");
+ break;
+ case TuningStyle.Sticky:
+ this.tuningStyleButton.Image = Resources.sticky;
+ this._tooltip.SetToolTip(this.tuningStyleButton, "Sticky tuning");
+ break;
+ case TuningStyle.Center:
+ this.tuningStyleButton.Image = Resources.center_24;
+ this._tooltip.SetToolTip(this.tuningStyleButton, "Center tuning");
+ if (this.SourceIsTunable)
+ {
+ this.ResetFrequency(this.Frequency);
+ }
+ this.waterfall.CenterZoom();
+ this.spectrumAnalyzer.CenterZoom();
+ break;
+ }
+ this.NotifyPropertyChanged("TuningStyle");
+ }
+
+ private void UpdateVfoFrequency()
+ {
+ if (this.UseFFTSource && this._frontendController is IVFOSource)
+ {
+ IVFOSource iVFOSource = this._frontendController as IVFOSource;
+ iVFOSource.VFOFrequency = this.vfoFrequencyEdit.Frequency - this._frequencyShift;
+ this._vfo.Frequency = 0;
+ }
+ else
+ {
+ this._vfo.Frequency = (int)(this.vfoFrequencyEdit.Frequency - this._centerFrequency);
+ }
+ }
+
+ private void vfoFrequencyEdit_FrequencyChanged(object sender, EventArgs e)
+ {
+ this.waterfall.Frequency = this.vfoFrequencyEdit.Frequency;
+ this.spectrumAnalyzer.Frequency = this.vfoFrequencyEdit.Frequency;
+ if (this._tuningStyle == TuningStyle.Center)
+ {
+ this.waterfall.CenterZoom();
+ this.spectrumAnalyzer.CenterZoom();
+ }
+ this.UpdateVfoFrequency();
+ this._vfo.IFOffset = -this._ifOffset;
+ if (this._vfo.DetectorType == DetectorType.WFM)
+ {
+ this._vfo.RdsReset();
+ }
+ else
+ {
+ this._vfo.CarrierLockerReset();
+ }
+ this.NotifyPropertyChanged("Frequency");
+ }
+
+ private void vfoFrequencyEdit_FrequencyChanging(object sender, FrequencyChangingEventArgs e)
+ {
+ if (!this._initializing)
+ {
+ if (!this.SourceIsTunable)
+ {
+ float num = (float)this.spectrumAnalyzer.DisplayCenterFrequency - 0.5f * (float)this.spectrumAnalyzer.DisplayedBandwidth;
+ float num2 = (float)this.spectrumAnalyzer.DisplayCenterFrequency + 0.5f * (float)this.spectrumAnalyzer.DisplayedBandwidth;
+ if ((float)e.Frequency > num2)
+ {
+ e.Frequency = (long)num2;
+ }
+ else if ((float)e.Frequency < num)
+ {
+ e.Frequency = (long)num;
+ }
+ }
+ else
+ {
+ e.Frequency = this.ApplyFrequencyBoundaries(e.Frequency, this.spectrumAnalyzer.DisplayedBandwidth / 2);
+ long num3 = this._centerFrequency;
+ switch (this._tuningStyle)
+ {
+ case TuningStyle.Center:
+ if (!this._changingCenterSpot)
+ {
+ e.Accept = this.UpdateCenterFrequency(e.Frequency, false, true);
+ num3 = e.Frequency;
+ }
+ break;
+ case TuningStyle.Sticky:
+ if (!this._changingStickySpot)
+ {
+ long num4 = e.Frequency - this.vfoFrequencyEdit.Frequency;
+ num3 = this._centerFrequency + num4;
+ e.Accept = this.UpdateCenterFrequency(num3, false, false);
+ }
+ break;
+ case TuningStyle.Free:
+ {
+ float val = (float)this.spectrumAnalyzer.DisplayCenterFrequency - this._tuningLimit * (float)this.spectrumAnalyzer.DisplayedBandwidth;
+ val = Math.Max(val, (float)this._centerFrequency - this._tuningLimit * (float)this._usableSpectrumWidth);
+ float val2 = (float)this.spectrumAnalyzer.DisplayCenterFrequency + this._tuningLimit * (float)this.spectrumAnalyzer.DisplayedBandwidth;
+ val2 = Math.Min(val2, (float)this._centerFrequency + this._tuningLimit * (float)this._usableSpectrumWidth);
+ if (!((float)e.Frequency < val) && !((float)e.Frequency > val2))
+ {
+ break;
+ }
+ long num4 = e.Frequency - this.vfoFrequencyEdit.Frequency;
+ num3 = this._centerFrequency + num4;
+ e.Accept = this.UpdateCenterFrequency(num3, false, false);
+ break;
+ }
+ }
+ if (e.Accept && this._centerFrequency != num3)
+ {
+ e.Frequency = Math.Max(e.Frequency, 0L);
+ this.fftZoomTrackBar.Value = 0;
+ if (this._tuningStyle == TuningStyle.Center)
+ {
+ this._tuningStyle = TuningStyle.Free;
+ this.UpdateTuningStyle();
+ }
+ }
+ }
+ }
+ }
+
+ public void SetFrequency(long frequency, bool onlyMoveCenterFrequency)
+ {
+ if (onlyMoveCenterFrequency && this._tuningStyle == TuningStyle.Free)
+ {
+ long num = frequency - this.vfoFrequencyEdit.Frequency;
+ this.UpdateCenterFrequency(this._centerFrequency + num, false, false);
+ }
+ this.vfoFrequencyEdit.Frequency = frequency;
+ }
+
+ private void SetCenterFrequency(long newCenterFreq)
+ {
+ this.UpdateCenterFrequency(newCenterFreq, true, true);
+ }
+
+ private void panview_FrequencyChanged(object sender, FrequencyEventArgs e)
+ {
+ this._changingStickySpot = (e.Source != FrequencyChangeSource.Scroll);
+ this.vfoFrequencyEdit.Frequency = e.Frequency;
+ this._changingStickySpot = false;
+ if (this.vfoFrequencyEdit.Frequency != e.Frequency)
+ {
+ e.Cancel = true;
+ }
+ }
+
+ private void panview_CenterFrequencyChanged(object sender, FrequencyEventArgs e)
+ {
+ if (!this.SourceIsTunable)
+ {
+ e.Cancel = true;
+ }
+ else
+ {
+ e.Cancel = !this.UpdateCenterFrequency(e.Frequency, true, false);
+ e.Cancel |= (this._centerFrequency != e.Frequency);
+ }
+ }
+
+ public void ResetFrequency(long frequency, long centerFrequency)
+ {
+ if (this._tuningStyle == TuningStyle.Center && this.SourceIsTunable)
+ {
+ frequency = Math.Max(frequency, (long)((float)this._usableSpectrumWidth * this._tuningLimit));
+ }
+ if (this.SourceIsTunable)
+ {
+ this._centerFrequency = centerFrequency;
+ this.waterfall.CenterFrequency = centerFrequency;
+ this.spectrumAnalyzer.CenterFrequency = centerFrequency;
+ this.vfoFrequencyEdit.Frequency = frequency;
+ }
+ else
+ {
+ this._centerFrequency = centerFrequency;
+ this.waterfall.Frequency = frequency;
+ this.waterfall.CenterFrequency = centerFrequency;
+ this.spectrumAnalyzer.Frequency = frequency;
+ this.spectrumAnalyzer.CenterFrequency = centerFrequency;
+ this.vfoFrequencyEdit.DisableFrequencyEvents = true;
+ this.vfoFrequencyEdit.Frequency = frequency;
+ this.vfoFrequencyEdit.DisableFrequencyEvents = false;
+ this._vfo.Frequency = 0;
+ this._vfo.IFOffset = -this._ifOffset;
+ }
+ }
+
+ public void ResetFrequency(long frequency)
+ {
+ this.ResetFrequency(frequency, frequency);
+ }
+
+ private long ApplyFrequencyBoundaries(long frequency, long delta = 0L)
+ {
+ if (this.SourceIsTunable)
+ {
+ ITunableSource tunableSource = this._frontendController as ITunableSource;
+ long num = tunableSource.MinimumTunableFrequency - delta + this._frequencyShift;
+ long num2 = tunableSource.MaximumTunableFrequency + delta + this._frequencyShift;
+ if (num < 0)
+ {
+ num = 0L;
+ }
+ if (num2 < 0)
+ {
+ num2 = 0L;
+ }
+ if (frequency < num)
+ {
+ frequency = num;
+ }
+ else if (frequency > num2)
+ {
+ frequency = num2;
+ }
+ }
+ return frequency;
+ }
+
+ private bool UpdateCenterFrequency(long frequency, bool setVFO, bool centerZoom = true)
+ {
+ if (!this.SourceIsTunable)
+ {
+ return false;
+ }
+ frequency = this.ApplyFrequencyBoundaries(frequency, 0L);
+ frequency = Math.Max(frequency, (long)((float)this._usableSpectrumWidth * this._tuningLimit));
+ this.waterfall.CenterFrequency = frequency;
+ this.spectrumAnalyzer.CenterFrequency = frequency;
+ long num = frequency - this._centerFrequency;
+ Interlocked.Exchange(ref this._centerFrequency, frequency);
+ if (setVFO)
+ {
+ this._changingStickySpot = (this._tuningStyle == TuningStyle.Sticky);
+ this._changingCenterSpot = (this._tuningStyle == TuningStyle.Center);
+ this.vfoFrequencyEdit.Frequency += num;
+ this._changingStickySpot = false;
+ this._changingCenterSpot = false;
+ }
+ if (this._vfo.DetectorType == DetectorType.WFM)
+ {
+ this._vfo.RdsReset();
+ }
+ else
+ {
+ this._vfo.CarrierLockerReset();
+ }
+ if (centerZoom)
+ {
+ this.waterfall.CenterZoom();
+ this.spectrumAnalyzer.CenterZoom();
+ }
+ this.NotifyPropertyChanged("CenterFrequency");
+ return true;
+ }
+
+ private void UpdateTunableBandwidth()
+ {
+ this.ResetFrequency(this.vfoFrequencyEdit.Frequency);
+ this.NotifyPropertyChanged("TuningLimit");
+ this.NotifyPropertyChanged("TunableBandwidth");
+ }
+
+ private void TuneThreadProc(object state)
+ {
+ while (!this._terminated)
+ {
+ long num = Interlocked.Read(ref this._centerFrequency) + this._ifOffset;
+ long num2 = Interlocked.Read(ref this._frequencyShift);
+ long num3 = num - num2;
+ IFrontendController frontendController = this._frontendController;
+ ITunableSource tunableSource = frontendController as ITunableSource;
+ if (tunableSource != null && this._frequencySet != num3)
+ {
+ try
+ {
+ tunableSource.Frequency = num3;
+ this._frequencySet = num3;
+ }
+ catch
+ {
+ }
+ }
+ Thread.Sleep(1);
+ }
+ }
+
+ private void filterBandwidthNumericUpDown_ValueChanged(object sender, EventArgs e)
+ {
+ this._vfo.Bandwidth = (int)this.filterBandwidthNumericUpDown.Value;
+ this.waterfall.FilterBandwidth = this._vfo.Bandwidth;
+ this.spectrumAnalyzer.FilterBandwidth = this._vfo.Bandwidth;
+ this.NotifyPropertyChanged("FilterBandwidth");
+ }
+
+ private void filterOrderNumericUpDown_ValueChanged(object sender, EventArgs e)
+ {
+ this._vfo.FilterOrder = (int)this.filterOrderNumericUpDown.Value;
+ this.NotifyPropertyChanged("FilterOrder");
+ }
+
+ private void filterTypeComboBox_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ this._vfo.WindowType = (WindowType)(this.filterTypeComboBox.SelectedIndex + 1);
+ this.NotifyPropertyChanged("FilterType");
+ }
+
+ private void autoCorrectIQCheckBox_CheckStateChanged(object sender, EventArgs e)
+ {
+ this._iqBalancerProcessor.Engine.Enabled = (this.correctIQCheckBox.Checked && this._sharpControlProxy.Enabled);
+ this.NotifyPropertyChanged("CorrectIQ");
+ }
+
+ private void UpdateFrequencyShift()
+ {
+ long num = 0L;
+ if (this.frequencyShiftCheckBox.Checked)
+ {
+ num = (long)this.frequencyShiftNumericUpDown.Value;
+ }
+ long num2 = num - this._frequencyShift;
+ this._frequencyShift = num;
+ long frequency = Math.Max(this._usableSpectrumWidth / 2, this._centerFrequency + num2);
+ this.UpdateCenterFrequency(frequency, false, false);
+ if (Math.Abs(num2) > 10000)
+ {
+ long frequency2 = Math.Max(0L, this.vfoFrequencyEdit.Frequency + num2);
+ this.SetFrequency(frequency2, false);
+ }
+ this.UpdateVfoFrequency();
+ }
+
+ private void frequencyShiftCheckBox_CheckStateChanged(object sender, EventArgs e)
+ {
+ this.frequencyShiftNumericUpDown.Enabled = this.frequencyShiftCheckBox.Checked;
+ this.UpdateFrequencyShift();
+ this.NotifyPropertyChanged("FrequencyShiftEnabled");
+ }
+
+ private void frequencyShiftNumericUpDown_ValueChanged(object sender, EventArgs e)
+ {
+ this.UpdateFrequencyShift();
+ this.NotifyPropertyChanged("FrequencyShift");
+ }
+
+ private void modeRadioButton_CheckStateChanged(object sender, EventArgs e)
+ {
+ this.agcCheckBox.Enabled = (!this.wfmRadioButton.Checked && !this.rawRadioButton.Checked);
+ this.agcDecayNumericUpDown.Enabled = this.agcCheckBox.Enabled;
+ this.agcSlopeNumericUpDown.Enabled = this.agcCheckBox.Enabled;
+ this.agcThresholdNumericUpDown.Enabled = this.agcCheckBox.Enabled;
+ this.agcUseHangCheckBox.Enabled = this.agcCheckBox.Enabled;
+ this.fmStereoCheckBox.Enabled = this.wfmRadioButton.Checked;
+ this.useSquelchCheckBox.Enabled = (this.nfmRadioButton.Checked || this.amRadioButton.Checked);
+ this.squelchNumericUpDown.Enabled = (this.useSquelchCheckBox.Enabled && this.useSquelchCheckBox.Checked);
+ this.cwShiftNumericUpDown.Enabled = this.cwRadioButton.Checked;
+ this._streamControl.ScaleOutput = !this.unityGainCheckBox.Checked;
+ this.audioGainTrackBar.Enabled = !this.unityGainCheckBox.Checked;
+ this.filterAudioCheckBox.Enabled = !this.rawRadioButton.Checked;
+ this.lockCarrierCheckBox.Enabled = (this.dsbRadioButton.Checked || this.amRadioButton.Checked || this.usbRadioButton.Checked || this.lsbRadioButton.Checked);
+ this.useAntiFadingCheckBox.Enabled = (this.lockCarrierCheckBox.Checked && this.lockCarrierCheckBox.Enabled && (this.amRadioButton.Checked || this.dsbRadioButton.Checked));
+ this.spectrumAnalyzer.StatusText = string.Empty;
+ if (!this._initializing)
+ {
+ this._modeStates[this._vfo.DetectorType] = this.GetModeState();
+ }
+ if (this.wfmRadioButton.Checked)
+ {
+ this._vfo.DetectorType = DetectorType.WFM;
+ this.waterfall.BandType = BandType.Center;
+ this.spectrumAnalyzer.BandType = BandType.Center;
+ this.waterfall.FilterOffset = 0;
+ this.spectrumAnalyzer.FilterOffset = 0;
+ }
+ else if (this.nfmRadioButton.Checked)
+ {
+ this._vfo.DetectorType = DetectorType.NFM;
+ this.waterfall.BandType = BandType.Center;
+ this.spectrumAnalyzer.BandType = BandType.Center;
+ this.waterfall.FilterOffset = 0;
+ this.spectrumAnalyzer.FilterOffset = 0;
+ }
+ else if (this.amRadioButton.Checked)
+ {
+ this._vfo.DetectorType = DetectorType.AM;
+ this.waterfall.BandType = BandType.Center;
+ this.spectrumAnalyzer.BandType = BandType.Center;
+ this.waterfall.FilterOffset = 0;
+ this.spectrumAnalyzer.FilterOffset = 0;
+ }
+ else if (this.lsbRadioButton.Checked)
+ {
+ this._vfo.DetectorType = DetectorType.LSB;
+ this.waterfall.BandType = BandType.Lower;
+ this.spectrumAnalyzer.BandType = BandType.Lower;
+ this.waterfall.FilterOffset = -100;
+ this.spectrumAnalyzer.FilterOffset = -100;
+ }
+ else if (this.usbRadioButton.Checked)
+ {
+ this._vfo.DetectorType = DetectorType.USB;
+ this.waterfall.BandType = BandType.Upper;
+ this.spectrumAnalyzer.BandType = BandType.Upper;
+ this.waterfall.FilterOffset = 100;
+ this.spectrumAnalyzer.FilterOffset = 100;
+ }
+ else if (this.dsbRadioButton.Checked)
+ {
+ this._vfo.DetectorType = DetectorType.DSB;
+ this.waterfall.BandType = BandType.Center;
+ this.spectrumAnalyzer.BandType = BandType.Center;
+ this.waterfall.FilterOffset = 0;
+ this.spectrumAnalyzer.FilterOffset = 0;
+ }
+ else if (this.cwRadioButton.Checked)
+ {
+ this._vfo.DetectorType = DetectorType.CW;
+ this.waterfall.BandType = BandType.Center;
+ this.spectrumAnalyzer.BandType = BandType.Center;
+ this.waterfall.FilterOffset = 0;
+ this.spectrumAnalyzer.FilterOffset = 0;
+ }
+ else if (this.rawRadioButton.Checked)
+ {
+ this._vfo.DetectorType = DetectorType.RAW;
+ this.waterfall.BandType = BandType.Center;
+ this.spectrumAnalyzer.BandType = BandType.Center;
+ this.waterfall.FilterOffset = 0;
+ this.spectrumAnalyzer.FilterOffset = 0;
+ }
+ this._vfo.RdsReset();
+ this.UpdateVFOSource();
+ this.UpdateFilterBandwidth();
+ this.SetModeState(this._modeStates[this._vfo.DetectorType]);
+ this.NotifyPropertyChanged("DetectorType");
+ }
+
+ private void UpdateVFOSource()
+ {
+ if (this.UseFFTSource && this._frontendController is IVFOSource)
+ {
+ StreamControl.ReducedBandwidth = true;
+ IVFOSource iVFOSource = this._frontendController as IVFOSource;
+ int num = iVFOSource.VFOMinIQDecimation + StreamControl.GetDecimationStageCount(iVFOSource.VFOMaxSampleRate / (double)(1 << iVFOSource.VFOMinIQDecimation), this._vfo.DetectorType);
+ StreamControl.ReducedBandwidth = (this._vfo.DetectorType != DetectorType.WFM);
+ if (iVFOSource.VFODecimation != num)
+ {
+ bool isPlaying = this.IsPlaying;
+ if (isPlaying)
+ {
+ this.StopRadio();
+ }
+ iVFOSource.VFODecimation = num;
+ this._vfo.DecimationStageCount = iVFOSource.VFOMinIQDecimation + StreamControl.GetDecimationStageCount(iVFOSource.VFOMaxSampleRate / (double)(1 << iVFOSource.VFOMinIQDecimation), DetectorType.AM) - iVFOSource.VFODecimation;
+ this._vfo.SampleRate = iVFOSource.VFOMaxSampleRate / (double)(1 << iVFOSource.VFODecimation);
+ if (isPlaying)
+ {
+ this.StartRadio();
+ }
+ }
+ }
+ else
+ {
+ StreamControl.ReducedBandwidth = false;
+ }
+ }
+
+ private void UpdateFilterBandwidth()
+ {
+ switch (this._vfo.DetectorType)
+ {
+ case DetectorType.WFM:
+ this.filterBandwidthNumericUpDown.Maximum = ((this._streamControl.SampleRate == 0.0) ? 250000 : ((int)Math.Min(this._streamControl.SampleRate, 250000.0)));
+ break;
+ case DetectorType.NFM:
+ case DetectorType.AM:
+ case DetectorType.DSB:
+ case DetectorType.CW:
+ case DetectorType.RAW:
+ this.filterBandwidthNumericUpDown.Maximum = ((this._streamControl.AudioSampleRate == 0.0) ? this._minOutputSampleRate : ((int)Math.Min(this._streamControl.AudioSampleRate, (double)this._minOutputSampleRate)));
+ break;
+ case DetectorType.LSB:
+ case DetectorType.USB:
+ this.filterBandwidthNumericUpDown.Maximum = ((this._streamControl.AudioSampleRate == 0.0) ? (this._minOutputSampleRate / 2) : ((int)Math.Min(this._streamControl.AudioSampleRate, (double)this._minOutputSampleRate) / 2));
+ break;
+ }
+ }
+
+ private void fmStereoCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ this._vfo.FmStereo = this.fmStereoCheckBox.Checked;
+ this.NotifyPropertyChanged("FmStereo");
+ }
+
+ private void cwShiftNumericUpDown_ValueChanged(object sender, EventArgs e)
+ {
+ this._vfo.CWToneShift = (int)this.cwShiftNumericUpDown.Value;
+ this.NotifyPropertyChanged("CWShift");
+ }
+
+ private void squelchNumericUpDown_ValueChanged(object sender, EventArgs e)
+ {
+ if (!this._configuringSquelch)
+ {
+ this._vfo.SquelchThreshold = (int)this.squelchNumericUpDown.Value;
+ this.NotifyPropertyChanged("SquelchThreshold");
+ }
+ }
+
+ private void useSquelchCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ if (!this._configuringSquelch)
+ {
+ this.squelchNumericUpDown.Enabled = this.useSquelchCheckBox.Checked;
+ if (this.useSquelchCheckBox.Checked)
+ {
+ this._vfo.SquelchThreshold = (int)this.squelchNumericUpDown.Value;
+ }
+ else
+ {
+ this._vfo.SquelchThreshold = 0;
+ }
+ this.NotifyPropertyChanged("SquelchEnabled");
+ }
+ }
+
+ private static int ParseStepSize(string s)
+ {
+ int result = 0;
+ Match match = Regex.Match(s, "([0-9\\.]+) ([kMG]?)Hz", RegexOptions.IgnoreCase);
+ if (match.Success)
+ {
+ int num;
+ switch (match.Groups[2].Value.ToLower())
+ {
+ default:
+ num = 1;
+ break;
+ case "k":
+ num = 1000;
+ break;
+ case "m":
+ num = 1000000;
+ break;
+ case "g":
+ num = 1000000000;
+ break;
+ }
+ result = (int)(double.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture) * (double)num);
+ }
+ return result;
+ }
+
+ private bool SetStepSize(int stepSize)
+ {
+ for (int i = 0; i < this.stepSizeComboBox.Items.Count; i++)
+ {
+ int num = MainForm.ParseStepSize(this.stepSizeComboBox.Items[i].ToString());
+ if (stepSize == num)
+ {
+ this.stepSizeComboBox.SelectedIndex = i;
+ this.stepSizeComboBox_SelectedIndexChanged(null, null);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void stepSizeComboBox_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ if (!this._configuringSnap)
+ {
+ this.waterfall.UseSnap = this.snapFrequencyCheckBox.Checked;
+ this.spectrumAnalyzer.UseSnap = this.snapFrequencyCheckBox.Checked;
+ int num = MainForm.ParseStepSize(this.stepSizeComboBox.Text);
+ if (num > 0 && num != this._stepSize)
+ {
+ this.waterfall.StepSize = num;
+ this.spectrumAnalyzer.StepSize = num;
+ if (this.snapFrequencyCheckBox.Checked && this.SourceIsTunable)
+ {
+ long frequency = (this._centerFrequency + num / 2) / num * num;
+ this.UpdateCenterFrequency(frequency, false, false);
+ long num2 = (this.vfoFrequencyEdit.Frequency + num / 2) / num * num;
+ if (this.vfoFrequencyEdit.Frequency != num2)
+ {
+ this.vfoFrequencyEdit.Frequency = num2;
+ }
+ }
+ this._stepSize = num;
+ if (sender == this.snapFrequencyCheckBox)
+ {
+ this.NotifyPropertyChanged("SnapToGrid");
+ }
+ this.NotifyPropertyChanged("StepSize");
+ }
+ }
+ }
+
+ private void panview_BandwidthChanged(object sender, BandwidthEventArgs e)
+ {
+ if ((decimal)e.Bandwidth < this.filterBandwidthNumericUpDown.Minimum)
+ {
+ e.Bandwidth = (int)this.filterBandwidthNumericUpDown.Minimum;
+ }
+ else if ((decimal)e.Bandwidth > this.filterBandwidthNumericUpDown.Maximum)
+ {
+ e.Bandwidth = (int)this.filterBandwidthNumericUpDown.Maximum;
+ }
+ this.filterBandwidthNumericUpDown.Value = e.Bandwidth;
+ }
+
+ private void frontendGuiButton_Click(object sender, EventArgs e)
+ {
+ if (this.SourceIsWaveFile)
+ {
+ if (this._streamControl.IsPlaying)
+ {
+ this.StopRadio();
+ }
+ try
+ {
+ this.Open(true);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
+ }
+ }
+ else if (this.SourceIsFrontEnd)
+ {
+ if (this._frontendController is IConfigurationPanelProvider)
+ {
+ if (this.scrollPanel.Visible && this.sourceCollapsiblePanel.PanelState == PanelStateOptions.Expanded)
+ {
+ this.sourceCollapsiblePanel.PanelState = PanelStateOptions.Collapsed;
+ }
+ else
+ {
+ this.scrollPanel.Visible = true;
+ this.menuSpacerPanel.Visible = true;
+ this.rightTableLayoutPanel.Visible = true;
+ this.sourceCollapsiblePanel.PanelState = PanelStateOptions.Expanded;
+ }
+ this.scrollPanel.ScrollControlIntoView(this.sourceCollapsiblePanel);
+ }
+ else if (this._frontendController is IFloatingConfigDialogProvider)
+ {
+ ((IFloatingConfigDialogProvider)this._frontendController).ShowSettingGUI(this);
+ }
+ }
+ }
+
+ private int[] GetModeState()
+ {
+ return new int[12]
+ {
+ this.FilterBandwidth,
+ this.FilterOrder,
+ (int)this.FilterType,
+ this.SquelchThreshold,
+ this.SquelchEnabled ? 1 : 0,
+ this.CWShift,
+ this.SnapToGrid ? 1 : 0,
+ this.stepSizeComboBox.SelectedIndex,
+ this.unityGainCheckBox.Checked ? 1 : 0,
+ this.agcCheckBox.Checked ? 1 : 0,
+ this.lockCarrierCheckBox.Checked ? 1 : 0,
+ this.useAntiFadingCheckBox.Checked ? 1 : 0
+ };
+ }
+
+ private void SetModeState(int[] state)
+ {
+ this.FilterBandwidth = Math.Min(state[0], (int)this.filterBandwidthNumericUpDown.Maximum);
+ this.FilterOrder = state[1];
+ this.FilterType = (WindowType)state[2];
+ this._configuringSquelch = true;
+ this.SquelchThreshold = state[3];
+ this.SquelchEnabled = (state[4] == 1);
+ this._configuringSquelch = false;
+ this.useSquelchCheckBox_CheckedChanged(null, null);
+ this.CWShift = state[5];
+ this._configuringSnap = true;
+ this.SnapToGrid = (state[6] == 1);
+ this.stepSizeComboBox.SelectedIndex = Math.Min(this.stepSizeComboBox.Items.Count - 1, state[7]);
+ this._configuringSnap = false;
+ this.stepSizeComboBox_SelectedIndexChanged(null, null);
+ this.unityGainCheckBox.Checked = (state[8] == 1);
+ this.unityGainCheckBox_CheckStateChanged(null, null);
+ this.agcCheckBox.Checked = (state[9] == 1);
+ this.agcCheckBox_CheckedChanged(null, null);
+ this.lockCarrierCheckBox.Checked = (state[10] == 1);
+ this.lockCarrierCheckBox_CheckedChanged(null, null);
+ this.useAntiFadingCheckBox.Checked = (state[11] == 1);
+ this.useAntiFadingCheckBox_CheckedChanged(null, null);
+ }
+
+ private void lockCarrierCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ this._vfo.LockCarrier = this.lockCarrierCheckBox.Checked;
+ this.useAntiFadingCheckBox.Enabled = (this.lockCarrierCheckBox.Checked && this.lockCarrierCheckBox.Enabled && (this.amRadioButton.Checked || this.dsbRadioButton.Checked));
+ }
+
+ private void useAntiFadingCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ this._vfo.UseAntiFading = this.useAntiFadingCheckBox.Checked;
+ }
+
+ private void agcCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ this._vfo.UseAGC = this.agcCheckBox.Checked;
+ this.agcThresholdNumericUpDown.Enabled = (this.agcCheckBox.Checked && this.agcCheckBox.Enabled);
+ this.agcDecayNumericUpDown.Enabled = (this.agcCheckBox.Checked && this.agcCheckBox.Enabled);
+ this.agcSlopeNumericUpDown.Enabled = (this.agcCheckBox.Checked && this.agcCheckBox.Enabled);
+ this.agcUseHangCheckBox.Enabled = (this.agcCheckBox.Checked && this.agcCheckBox.Enabled);
+ this.NotifyPropertyChanged("UseAgc");
+ }
+
+ private void agcUseHangCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ this._vfo.AgcHang = this.agcUseHangCheckBox.Checked;
+ this.NotifyPropertyChanged("UseHang");
+ }
+
+ private void agcDecayNumericUpDown_ValueChanged(object sender, EventArgs e)
+ {
+ this._vfo.AgcDecay = (float)(int)this.agcDecayNumericUpDown.Value;
+ this.NotifyPropertyChanged("AgcDecay");
+ }
+
+ private void agcThresholdNumericUpDown_ValueChanged(object sender, EventArgs e)
+ {
+ this._vfo.AgcThreshold = (float)(int)this.agcThresholdNumericUpDown.Value;
+ this.NotifyPropertyChanged("AgcThreshold");
+ }
+
+ private void agcSlopeNumericUpDown_ValueChanged(object sender, EventArgs e)
+ {
+ this._vfo.AgcSlope = (float)(int)this.agcSlopeNumericUpDown.Value;
+ this.NotifyPropertyChanged("AgcSlope");
+ }
+
+ private void swapIQCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ this._streamControl.SwapIQ = this.swapIQCheckBox.Checked;
+ this.NotifyPropertyChanged("SwapIq");
+ }
+
+ private void viewComboBox_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ bool flag = false;
+ bool flag2 = false;
+ if (this._streamControl.IsPlaying)
+ {
+ if (this.viewComboBox.SelectedIndex < 3 && !this.spectrumPanel.Visible)
+ {
+ flag = true;
+ }
+ else if (this.viewComboBox.SelectedIndex == 3 && this.spectrumPanel.Visible)
+ {
+ flag2 = true;
+ }
+ }
+ switch (this.viewComboBox.SelectedIndex)
+ {
+ case 0:
+ this.spectrumPanel.Visible = true;
+ this.spectrumAnalyzer.Visible = true;
+ this.waterfall.Visible = false;
+ this.spectrumAnalyzer.Dock = DockStyle.Fill;
+ this.spectrumSplitter.Visible = false;
+ break;
+ case 1:
+ this.spectrumPanel.Visible = true;
+ this.spectrumAnalyzer.Visible = false;
+ this.waterfall.Visible = true;
+ this.spectrumAnalyzer.Dock = DockStyle.Top;
+ this.spectrumSplitter.Visible = false;
+ break;
+ case 2:
+ this.spectrumPanel.Visible = true;
+ this.spectrumAnalyzer.Visible = true;
+ this.waterfall.Visible = true;
+ this.spectrumAnalyzer.Dock = DockStyle.Top;
+ this.spectrumSplitter.Visible = true;
+ break;
+ case 3:
+ this.spectrumPanel.Visible = false;
+ break;
+ }
+ if (!this.UseFFTSource)
+ {
+ if (flag)
+ {
+ this._fftStream.Open();
+ ThreadPool.QueueUserWorkItem(this.ProcessFFT);
+ }
+ else if (flag2)
+ {
+ this._fftStream.Close();
+ this._fftEvent.Set();
+ }
+ }
+ }
+
+ private void fftResolutionComboBox_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ this._fftResolutionLock.AcquireWriterLock(300000);
+ this._fftBins = int.Parse(this.fftResolutionComboBox.SelectedItem.ToString());
+ this.InitFFTBuffers();
+ this.BuildFFTWindow();
+ this._fftResolutionLock.ReleaseWriterLock();
+ this.NotifyPropertyChanged("FFTResolution");
+ }
+
+ private void spectrumStyleComboBox_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ this.spectrumAnalyzer.SpectrumStyle = (SpectrumStyle)this.spectrumStyleComboBox.SelectedIndex;
+ this.NotifyPropertyChanged("FFTSpectrumStyle");
+ }
+
+ private void fftWindowComboBox_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ this._fftWindowType = (WindowType)this.fftWindowComboBox.SelectedIndex;
+ this.BuildFFTWindow();
+ }
+
+ private void gradientButton_Click(object sender, EventArgs e)
+ {
+ ColorBlend gradient = GradientDialog.GetGradient(this.waterfall.GradientColorBlend);
+ if (gradient != null && gradient.Positions.Length != 0)
+ {
+ this.waterfall.GradientColorBlend = gradient;
+ this.spectrumAnalyzer.VerticalLinesGradient = gradient;
+ Utils.SaveSetting("waterfall.gradient", MainForm.GradientToString(gradient.Colors));
+ this.NotifyPropertyChanged("Gradient");
+ }
+ }
+
+ private static string GradientToString(Color[] colors)
+ {
+ StringBuilder stringBuilder = new StringBuilder();
+ for (int i = 0; i < colors.Length; i++)
+ {
+ stringBuilder.AppendFormat(",{0:X2}{1:X2}{2:X2}", colors[i].R, colors[i].G, colors[i].B);
+ }
+ return stringBuilder.ToString().Substring(1);
+ }
+
+ private void fftContrastTrackBar_Changed(object sender, EventArgs e)
+ {
+ this.waterfall.Contrast = this.fftContrastTrackBar.Value * 100 / (this.fftContrastTrackBar.Maximum - this.fftContrastTrackBar.Minimum);
+ this.spectrumAnalyzer.Contrast = this.waterfall.Contrast;
+ this.NotifyPropertyChanged("FFTContrast");
+ }
+
+ private void sAttackTrackBar_ValueChanged(object sender, EventArgs e)
+ {
+ this.spectrumAnalyzer.Attack = (float)this.sAttackTrackBar.Value / (float)this.sAttackTrackBar.Maximum;
+ this.NotifyPropertyChanged("SAttack");
+ }
+
+ private void sDecayTrackBar_ValueChanged(object sender, EventArgs e)
+ {
+ this.spectrumAnalyzer.Decay = (float)this.sDecayTrackBar.Value / (float)this.sDecayTrackBar.Maximum;
+ this.NotifyPropertyChanged("SDecay");
+ }
+
+ private void wAttackTrackBar_ValueChanged(object sender, EventArgs e)
+ {
+ this.waterfall.Attack = (float)this.wAttackTrackBar.Value / (float)this.wAttackTrackBar.Maximum;
+ this.NotifyPropertyChanged("WAttack");
+ }
+
+ private void wDecayTrackBar_ValueChanged(object sender, EventArgs e)
+ {
+ this.waterfall.Decay = (float)this.wDecayTrackBar.Value / (float)this.wDecayTrackBar.Maximum;
+ this.NotifyPropertyChanged("WDecay");
+ }
+
+ private void markPeaksCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ this.spectrumAnalyzer.MarkPeaks = this.markPeaksCheckBox.Checked;
+ this.NotifyPropertyChanged("MarkPeaks");
+ }
+
+ private void useTimestampCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ this.waterfall.UseTimestamps = this.useTimestampsCheckBox.Checked;
+ this.NotifyPropertyChanged("UseTimeMarkers");
+ }
+
+ private void fftSpeedTrackBar_ValueChanged(object sender, EventArgs e)
+ {
+ this._waterfallTimer.Interval = (int)(1.0 / (double)this.fftSpeedTrackBar.Value * 1000.0);
+ }
+
+ private void fftZoomTrackBar_ValueChanged(object sender, EventArgs e)
+ {
+ this.spectrumAnalyzer.Zoom = this.fftZoomTrackBar.Value * 100 / this.fftZoomTrackBar.Maximum;
+ this.waterfall.Zoom = this.spectrumAnalyzer.Zoom;
+ this.NotifyPropertyChanged("Zoom");
+ }
+
+ private void MainForm_Move(object sender, EventArgs e)
+ {
+ if (base.WindowState == FormWindowState.Normal)
+ {
+ this._lastLocation = base.Location;
+ }
+ }
+
+ private void MainForm_Resize(object sender, EventArgs e)
+ {
+ if (base.WindowState == FormWindowState.Normal)
+ {
+ this._lastSize = base.Size;
+ }
+ }
+
+ private int[] GetCollapsiblePanelStates()
+ {
+ List list = new List();
+ for (SDRSharp.CollapsiblePanel.CollapsiblePanel nextPanel = this.sourceCollapsiblePanel; nextPanel != null; nextPanel = nextPanel.NextPanel)
+ {
+ list.Add((int)nextPanel.PanelState);
+ }
+ return list.ToArray();
+ }
+
+ private void fftOffsetTrackBar_Scroll(object sender, EventArgs e)
+ {
+ this.spectrumAnalyzer.DisplayOffset = -this.fftOffsetTrackBar.Value * 10;
+ this.waterfall.DisplayOffset = this.spectrumAnalyzer.DisplayOffset;
+ this.NotifyPropertyChanged("FFTOffset");
+ }
+
+ private void fftRangeTrackBar_Scroll(object sender, EventArgs e)
+ {
+ this.spectrumAnalyzer.DisplayRange = this.fftRangeTrackBar.Value * 10;
+ this.waterfall.DisplayRange = this.spectrumAnalyzer.DisplayRange;
+ this.NotifyPropertyChanged("FFTRange");
+ }
+
+ private void InitialiseSharpPlugins()
+ {
+ NameValueCollection nameValueCollection = (NameValueCollection)ConfigurationManager.GetSection("sharpPlugins");
+ if (nameValueCollection == null)
+ {
+ MessageBox.Show("Configuration section 'sharpPlugins' was not found. Please check 'SDRSharp.exe.config'.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Hand);
+ }
+ else
+ {
+ this._oldTopSplitterPosition = Utils.GetIntSetting("topSplitter", this._oldTopSplitterPosition);
+ this._oldBottomSplitterPosition = Utils.GetIntSetting("bottomSplitter", this._oldBottomSplitterPosition);
+ this._oldLeftSplitterPosition = Utils.GetIntSetting("leftSplitter", this._oldLeftSplitterPosition);
+ this._oldRightSplitterPosition = Utils.GetIntSetting("rightSplitter", this._oldRightSplitterPosition);
+ this.topSplitter.Visible = false;
+ this.bottomSplitter.Visible = false;
+ this.leftSplitter.Visible = false;
+ this.rightSplitter.Visible = false;
+ foreach (string key in nameValueCollection.Keys)
+ {
+ try
+ {
+ string fqtn = nameValueCollection[key];
+ ISharpPlugin sharpPlugin = (ISharpPlugin)this.LoadExtension(fqtn);
+ this._sharpPlugins.Add(key, sharpPlugin);
+ sharpPlugin.Initialize(this._sharpControlProxy);
+ if (sharpPlugin.Gui != null)
+ {
+ this.CreatePluginCollapsiblePanel(sharpPlugin);
+ }
+ }
+ catch (Exception)
+ {
+ }
+ }
+ this.sourceCollapsiblePanel.PanelState = PanelStateOptions.Collapsed;
+ int[] intArraySetting = Utils.GetIntArraySetting("collapsiblePanelStates", null);
+ if (intArraySetting != null)
+ {
+ SDRSharp.CollapsiblePanel.CollapsiblePanel nextPanel = this.sourceCollapsiblePanel;
+ for (int i = 0; i < intArraySetting.Length; i++)
+ {
+ if (nextPanel == null)
+ {
+ break;
+ }
+ nextPanel.PanelState = (PanelStateOptions)intArraySetting[i];
+ nextPanel = nextPanel.NextPanel;
+ }
+ }
+ else
+ {
+ this.sourceCollapsiblePanel.PanelState = PanelStateOptions.Expanded;
+ }
+ }
+ }
+
+ private void ShowControllerPanel(UserControl gui)
+ {
+ if (this.sourceTableLayoutPanel.Controls.Count == 1)
+ {
+ this.sourceCollapsiblePanel.Height = this._sourcePanelHeight + gui.Height;
+ this.sourceTableLayoutPanel.Controls.Add(gui, 0, 1);
+ gui.Dock = DockStyle.Fill;
+ }
+ }
+
+ private void HideControllerPanel(UserControl gui)
+ {
+ if (this.sourceTableLayoutPanel.Controls.Count > 1)
+ {
+ this.sourceTableLayoutPanel.Controls.Remove(gui);
+ this.sourceCollapsiblePanel.Height = this._sourcePanelHeight;
+ }
+ }
+
+ private void CreatePluginCollapsiblePanel(ISharpPlugin plugin)
+ {
+ UserControl gui = plugin.Gui;
+ if (gui != null)
+ {
+ SDRSharp.CollapsiblePanel.CollapsiblePanel collapsiblePanel = new SDRSharp.CollapsiblePanel.CollapsiblePanel();
+ collapsiblePanel.PanelTitle = plugin.DisplayName + " *";
+ collapsiblePanel.AutoHeight = true;
+ collapsiblePanel.Content.Controls.Add(gui);
+ collapsiblePanel.Height = gui.Height;
+ collapsiblePanel.Width = this.fftCollapsiblePanel.Width;
+ collapsiblePanel.PanelState = PanelStateOptions.Collapsed;
+ gui.Dock = DockStyle.Fill;
+ SDRSharp.CollapsiblePanel.CollapsiblePanel nextPanel = this.fftCollapsiblePanel;
+ while (nextPanel.NextPanel != null)
+ {
+ nextPanel = nextPanel.NextPanel;
+ }
+ nextPanel.NextPanel = collapsiblePanel;
+ this.controlPanel.Controls.Add(collapsiblePanel);
+ }
+ }
+
+ public void RegisterFrontControl(UserControl c, PluginPosition position)
+ {
+ SizeType sizeType = c.Visible ? SizeType.Absolute : SizeType.AutoSize;
+ SizeType sizeType2 = c.Visible ? SizeType.Percent : SizeType.AutoSize;
+ switch (position)
+ {
+ case PluginPosition.Top:
+ if (this.topPluginPanel.Controls.Count > 0)
+ {
+ this.topPluginPanel.ColumnCount++;
+ this.topPluginPanel.ColumnStyles.Add(new ColumnStyle(sizeType, 4f));
+ this.topPluginPanel.ColumnCount++;
+ this.topPluginPanel.ColumnStyles.Add(new ColumnStyle(sizeType2, 100f));
+ }
+ else
+ {
+ this.topPluginPanel.ColumnStyles[0].SizeType = sizeType2;
+ }
+ this._oldTopSplitterPosition = Math.Max(this._oldTopSplitterPosition, c.Height);
+ this.topPluginPanel.Controls.Add(c, this.topPluginPanel.ColumnCount - 1, 0);
+ break;
+ case PluginPosition.Bottom:
+ if (this.bottomPluginPanel.Controls.Count > 0)
+ {
+ this.bottomPluginPanel.ColumnCount++;
+ this.bottomPluginPanel.ColumnStyles.Add(new ColumnStyle(sizeType, 4f));
+ this.bottomPluginPanel.ColumnCount++;
+ this.bottomPluginPanel.ColumnStyles.Add(new ColumnStyle(sizeType2, 100f));
+ }
+ else
+ {
+ this.bottomPluginPanel.ColumnStyles[0].SizeType = sizeType2;
+ }
+ this._oldBottomSplitterPosition = Math.Max(this._oldBottomSplitterPosition, c.Height);
+ this.bottomPluginPanel.Controls.Add(c, this.bottomPluginPanel.ColumnCount - 1, 0);
+ break;
+ case PluginPosition.Left:
+ if (this.leftPluginPanel.Controls.Count > 0)
+ {
+ this.leftPluginPanel.RowCount++;
+ this.leftPluginPanel.RowStyles.Add(new RowStyle(sizeType, 4f));
+ this.leftPluginPanel.RowCount++;
+ this.leftPluginPanel.RowStyles.Add(new RowStyle(sizeType2, 100f));
+ }
+ else
+ {
+ this.leftPluginPanel.RowStyles[0].SizeType = sizeType2;
+ }
+ this._oldLeftSplitterPosition = Math.Max(this._oldLeftSplitterPosition, c.Width);
+ this.leftPluginPanel.Controls.Add(c, 0, this.leftPluginPanel.RowCount - 1);
+ break;
+ case PluginPosition.Right:
+ if (this.rightPluginPanel.Controls.Count > 0)
+ {
+ this.rightPluginPanel.RowCount++;
+ this.rightPluginPanel.RowStyles.Add(new RowStyle(sizeType, 4f));
+ this.rightPluginPanel.RowCount++;
+ this.rightPluginPanel.RowStyles.Add(new RowStyle(sizeType2, 100f));
+ }
+ else
+ {
+ this.rightPluginPanel.RowStyles[0].SizeType = sizeType2;
+ }
+ this._oldRightSplitterPosition = Math.Max(this._oldRightSplitterPosition, c.Width);
+ this.rightPluginPanel.Controls.Add(c, 0, this.rightPluginPanel.RowCount - 1);
+ break;
+ }
+ c.Margin = new Padding(0);
+ c.Dock = DockStyle.Fill;
+ c.VisibleChanged += this.plugin_VisibleChanged;
+ this.plugin_VisibleChanged(c, null);
+ }
+
+ private void plugin_VisibleChanged(object sender, EventArgs e)
+ {
+ UserControl userControl = (UserControl)sender;
+ TableLayoutPanel tableLayoutPanel = (TableLayoutPanel)userControl.Parent;
+ int index = tableLayoutPanel.Controls.IndexOf(userControl) * 2;
+ bool flag = tableLayoutPanel == this.leftPluginPanel || tableLayoutPanel == this.rightPluginPanel;
+ if (userControl.Visible)
+ {
+ if (flag)
+ {
+ tableLayoutPanel.RowStyles[index].SizeType = SizeType.Percent;
+ }
+ else
+ {
+ tableLayoutPanel.ColumnStyles[index].SizeType = SizeType.Percent;
+ }
+ }
+ else if (flag)
+ {
+ tableLayoutPanel.RowStyles[index].SizeType = SizeType.AutoSize;
+ }
+ else
+ {
+ tableLayoutPanel.ColumnStyles[index].SizeType = SizeType.AutoSize;
+ }
+ for (int i = 0; i < tableLayoutPanel.ColumnStyles.Count - 1; i += 2)
+ {
+ if (tableLayoutPanel.ColumnStyles[i].SizeType == SizeType.Percent)
+ {
+ SizeType sizeType = SizeType.AutoSize;
+ int num = i + 2;
+ while (num < tableLayoutPanel.ColumnStyles.Count)
+ {
+ if (tableLayoutPanel.ColumnStyles[num].SizeType != SizeType.Percent)
+ {
+ num += 2;
+ continue;
+ }
+ sizeType = SizeType.Absolute;
+ break;
+ }
+ tableLayoutPanel.ColumnStyles[i + 1].SizeType = sizeType;
+ }
+ else
+ {
+ tableLayoutPanel.ColumnStyles[i + 1].SizeType = SizeType.AutoSize;
+ }
+ }
+ for (int j = 0; j < tableLayoutPanel.RowStyles.Count - 1; j += 2)
+ {
+ if (tableLayoutPanel.RowStyles[j].SizeType == SizeType.Percent)
+ {
+ SizeType sizeType2 = SizeType.AutoSize;
+ int num2 = j + 2;
+ while (num2 < tableLayoutPanel.RowStyles.Count)
+ {
+ if (tableLayoutPanel.RowStyles[num2].SizeType != SizeType.Percent)
+ {
+ num2 += 2;
+ continue;
+ }
+ sizeType2 = SizeType.Absolute;
+ break;
+ }
+ tableLayoutPanel.RowStyles[j + 1].SizeType = sizeType2;
+ }
+ else
+ {
+ tableLayoutPanel.RowStyles[j + 1].SizeType = SizeType.AutoSize;
+ }
+ }
+ this.UpdatePluginPanel(tableLayoutPanel);
+ }
+
+ private void UpdatePluginPanel(TableLayoutPanel panel)
+ {
+ bool flag = true;
+ int num = 0;
+ while (num < panel.Controls.Count)
+ {
+ if (!((Control.ControlCollection)panel.Controls)[num].Visible)
+ {
+ num++;
+ continue;
+ }
+ flag = false;
+ break;
+ }
+ if (panel == this.topPluginPanel)
+ {
+ if (flag)
+ {
+ this.topSplitter.Visible = false;
+ this.topSplitter.SplitPosition = 0;
+ }
+ else
+ {
+ this.topSplitter.Visible = true;
+ this.topSplitter.SplitPosition = this._oldTopSplitterPosition;
+ }
+ }
+ else if (panel == this.bottomPluginPanel)
+ {
+ if (flag)
+ {
+ this.bottomSplitter.Visible = false;
+ this.bottomSplitter.SplitPosition = 0;
+ }
+ else
+ {
+ this.bottomSplitter.Visible = true;
+ this.bottomSplitter.SplitPosition = this._oldBottomSplitterPosition;
+ }
+ }
+ else if (panel == this.leftPluginPanel)
+ {
+ if (flag)
+ {
+ this.leftSplitter.Visible = false;
+ this.leftSplitter.SplitPosition = 0;
+ }
+ else
+ {
+ this.leftSplitter.Visible = true;
+ this.leftSplitter.SplitPosition = this._oldLeftSplitterPosition;
+ }
+ }
+ else if (panel == this.rightPluginPanel)
+ {
+ if (flag)
+ {
+ this.rightSplitter.Visible = false;
+ this.rightSplitter.SplitPosition = 0;
+ }
+ else
+ {
+ this.rightSplitter.Visible = true;
+ this.rightSplitter.SplitPosition = this._oldRightSplitterPosition;
+ }
+ }
+ }
+
+ private void pluginSplitter_SplitterMoved(object sender, SplitterEventArgs e)
+ {
+ if (this.topSplitter.Visible)
+ {
+ this._oldTopSplitterPosition = this.topSplitter.SplitPosition;
+ }
+ if (this.bottomSplitter.Visible)
+ {
+ this._oldBottomSplitterPosition = this.bottomSplitter.SplitPosition;
+ }
+ if (this.leftSplitter.Visible)
+ {
+ this._oldLeftSplitterPosition = this.leftSplitter.SplitPosition;
+ }
+ if (this.rightSplitter.Visible)
+ {
+ this._oldRightSplitterPosition = this.rightSplitter.SplitPosition;
+ }
+ }
+
+ private void ConnectSource()
+ {
+ if (this.SourceIsFrontEnd && this._frontendController is IConnectableSource)
+ {
+ IConnectableSource connectableSource = this._frontendController as IConnectableSource;
+ if (!connectableSource.Connected)
+ {
+ connectableSource.Connect();
+ }
+ }
+ }
+
+ public void StartRadio()
+ {
+ this.playStopButton.Image = Resources.sdr_stop;
+ this._tooltip.SetToolTip(this.playStopButton, "Stop");
+ try
+ {
+ this.ConnectSource();
+ this.Open(false);
+ this._streamControl.Play();
+ }
+ catch
+ {
+ this.ConnectSource();
+ this.Open(true);
+ this._streamControl.Play();
+ }
+ this._fftStream.Open();
+ if (!this.UseFFTSource)
+ {
+ ThreadPool.QueueUserWorkItem(this.ProcessFFT);
+ }
+ this.sampleRateComboBox.Enabled = false;
+ this.inputDeviceComboBox.Enabled = false;
+ this.outputDeviceComboBox.Enabled = false;
+ this.latencyNumericUpDown.Enabled = false;
+ this.NotifyPropertyChanged("StartRadio");
+ }
+
+ public void StopRadio()
+ {
+ this.playStopButton.Image = Resources.sdr_start;
+ this._tooltip.SetToolTip(this.playStopButton, "Start");
+ this._streamControl.Stop();
+ this._iqBalancerProcessor.Engine.Reset();
+ this._fftStream.Close();
+ if (!this.SourceIsWaveFile)
+ {
+ this.inputDeviceComboBox.Enabled = (this._frontendController == null || this._frontendController is ISoundcardController);
+ this.sampleRateComboBox.Enabled = (this._frontendController == null || this._frontendController is ISoundcardController);
+ }
+ this.outputDeviceComboBox.Enabled = true;
+ this.latencyNumericUpDown.Enabled = true;
+ this._fftEvent.Set();
+ while (this._fftIsRunning)
+ {
+ }
+ this.NotifyPropertyChanged("StopRadio");
+ GC.Collect();
+ }
+
+ public unsafe void GetSpectrumSnapshot(byte[] destArray)
+ {
+ this._fftResolutionLock.AcquireReaderLock(300000);
+ float[] array = new float[destArray.Length];
+ fixed (byte* dest = destArray)
+ {
+ float[] array2 = array;
+ fixed (float* ptr = array2)
+ {
+ Fourier.SmoothMaxCopy(this._fftDisplayPtr, ptr, this._fftDisplaySize, array.Length, 1f, 0f);
+ Fourier.ScaleFFT(ptr, dest, array.Length, -130f, 0f);
+ }
+ }
+ this._fftResolutionLock.ReleaseReaderLock();
+ }
+
+ public unsafe void GetSpectrumSnapshot(float[] destArray, float scale = 1f, float offset = 0f)
+ {
+ this._fftResolutionLock.AcquireReaderLock(300000);
+ fixed (float* dstPtr = destArray)
+ {
+ Fourier.SmoothMaxCopy(this._fftDisplayPtr, dstPtr, this._fftDisplaySize, destArray.Length, scale, offset);
+ }
+ this._fftResolutionLock.ReleaseReaderLock();
+ }
+
+ public void RegisterStreamHook(object streamHook, ProcessorType processorType)
+ {
+ this._hookManager.RegisterStreamHook(streamHook, processorType);
+ }
+
+ public void UnregisterStreamHook(object streamHook)
+ {
+ this._hookManager.UnregisterStreamHook(streamHook);
+ }
+
+ private void NotifyPropertyChanged(string property)
+ {
+ PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
+ if (propertyChanged != null)
+ {
+ this.PropertyChanged(this, new PropertyChangedEventArgs(property));
+ }
+ }
+
+ private void waterfall_CustomPaint(object sender, CustomPaintEventArgs e)
+ {
+ CustomPaintEventHandler waterfallCustomPaint = this.WaterfallCustomPaint;
+ if (waterfallCustomPaint != null)
+ {
+ waterfallCustomPaint(sender, e);
+ }
+ }
+
+ private void spectrumAnalyzer_CustomPaint(object sender, CustomPaintEventArgs e)
+ {
+ CustomPaintEventHandler spectrumAnalyzerCustomPaint = this.SpectrumAnalyzerCustomPaint;
+ if (spectrumAnalyzerCustomPaint != null)
+ {
+ spectrumAnalyzerCustomPaint(sender, e);
+ }
+ }
+
+ private void spectrumAnalyzer_BackgroundCustomPaint(object sender, CustomPaintEventArgs e)
+ {
+ CustomPaintEventHandler spectrumAnalyzerBackgroundCustomPaint = this.SpectrumAnalyzerBackgroundCustomPaint;
+ if (spectrumAnalyzerBackgroundCustomPaint != null)
+ {
+ spectrumAnalyzerBackgroundCustomPaint(sender, e);
+ }
+ }
+
+ public void Perform()
+ {
+ this.spectrumAnalyzer.Perform();
+ this.waterfall.Perform();
+ }
+
+ private void logoPictureBox_Click(object sender, EventArgs e)
+ {
+ Process.Start("http://airspy.com");
+ }
+ }
+}
diff --git a/SDRSharp/SDRSharp/MainForm.resource b/SDRSharp/SDRSharp/MainForm.resource
new file mode 100644
index 0000000..4b38d0b
--- /dev/null
+++ b/SDRSharp/SDRSharp/MainForm.resource
@@ -0,0 +1,283 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAAKYAAABECAYAAAALBE7+AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAu
+ IgAALiIBquLdkgAAJIBJREFUeF7tnYm3HVWVxit3eC8hMyEEQggBwiDBMIkCAQmTICgyhVEQBxAcEFG7
+ cYKogAOitO0sKDh2O7e6lqtH1+rV3f5Z9Pfb++yqXXXrvvcyPELiq7W+VbdOnXPq1Dlf7emcqlu98sor
+ S/gbgbZlPRgE9lTVsIt8fq/vJ+rou9aBojdxCUceWmTau7cmovYFu0faT4Gds3wdsi4aQXsTl3BkoUOg
+ TMgg33hHVc1ob9heVbMgjgvGVXWBoPy7naiJpLn+g0LQ3sQlHDnokAYSDQuxjJACpIOEy4UVgc1VdVQ+
+ FjhvZN1R7VCZQtJFImhv4hKODHSI4qR0tWyE3FZtCzJCwpXCqoLVCZHG+SBrTVJhToL2tWsh6E1cwpGB
+ RJCivhtSCpArCAkB11Tr1q3THqzvgLS1whqBvJmkEwQVDpicvYlLOPyRiSEUaWnqN5MSSQjZIN7R1erV
+ xyhlY7Vq1bE6dvjvjaLjMTq/Qb8hKiQNadol6EGRnr2JSzj8kQhRSFmNioOTSLkegq03QlYQcOVxOj6+
+ WrHiBO0zZHIqfaXOO1GVX0R2QkPsIOjy4jQdsPTsTVzC4Y9EhiAmZIE0KzY2khLpJ5Kt3KQ9hNwibNXv
+ k4RtwskF/CbtRAGiQlLKBEFDgqLiTXq6g7T/5OxNXMLhjyBBUaldaQmJ1pmkRApW1RYB8p0qnF7NzJyp
+ PTjDjmdnT9f+NIHzEBXyUsYJahLXSN6SnsJ+k7M3cQmHNxIBajUuQBKkGVINCXe0kcqJWVS7qWZUNZIR
+ Mr5eOFc4RxC3C1lnZ0+rli8/RTjJpGxVUYcEcUt6JnKabbtP5OxNXMLhjTT4hZhGDNR4Q0CXcscLx02p
+ g7IQGcLJdDSCXiDsFNPO0h5pSjoSFCJvLuodBwmCh2rfL3L2Ji7h8EYa+JCYmZgQBrW7MTk5kCecll6y
+ aFtRpCPS8w3CTuF1AgRFxWODnlAk8DGSm6HaEzkn1XrftUBv4hIOb8SgC5mYkAOSQBZX405KbEvUNlLw
+ bAM2pjtBSL5hp27qOqEaj8/XHgkaKh7pSV2u2letQtLuNzknEpZw+CMGXNDg10H1Qsz1ayUzNxTJBolO
+ EZB6IqXINh6/qRqNLhV2G6rqIgHHZ3XnGtRH+oWCS093kk4WqU+sjjrq+ETOjlqfn5ytgyUcGYjBFhj4
+ 7JFDDhHT7EuIiW2IGj5TUnKnSHlBIeYu4bJCzquFa5XnKsszSdBNKoNqxwZF6rbJ6SGlsDnDIdKDsntU
+ IgZLxPxbQQy2YMQUgpg4Pk5MJ81WSTlUMLYipEM1X5RIeVUhJrimGg7fansnX63itUE6EdPUe0POUOv+
+ IEDOCCWFPTtVatY/lnDkIAY6YpgCxMTDXlXmw8Mjx46EmISFIFVIyzcLIS3fIlwrXFf214qgN2i/S/nX
+ pGtCfMjdJqc7TOGtE0qCnLRlTpXeuqElHBmIQe4lJpLLbT+IiSeNjYkTdLJU9Q6p5TeKdFcUEl6ndIh5
+ nUnL4fD6IjUh5/XaXyPjoA43aUMS4rWfJyCFsUG3iZzUf2y1Zg1xzuwMjcsSvCViHumIARYY7PmIaVON
+ nfKoWKTqqSLeLhHwbcLbjZBOzBts7xLUCSuVncqjqpGaEBRyIpGRzOEMhUpfoRMQuVdq1g06HBANPxjo
+ q39f0a3zYKPvmhnd/B0UYtbBdYiJLQgxNsrGZGFGzIP3Xkub6pD37gS9SYCgNygdcgKkZ5BzcyqHJ463
+ TvgJj/8Uc4Y8EhAq3aSmz6lPSs2JxhxMxIWW8KqCAS6kNGk03rbN1GYTXGeFkKtXSIm6RaoxJ769pHc9
+ 72WqYZsIiMSEoJASSdqQs6rw3I9OZSQlx5DzrBJG4lqb7dpNCCkcoZbUtPJR0cECFU9BdNhU8BZeoO/8
+ YmLvXtv3tdtwKNq0H2BwA0ZKIaQlEmqNPGRJrJWbilOCxESi4bSIROb4YF9eaWEjzzNIY7tK54ht3twi
+ Z0jNUcW52ZKX9sRcOwF4wlKEpyakpjDhoU8Qa39RV9hgWoftO5p3VA4yrN7+a86LxWrTNHC9QN/5Ggwy
+ wK7MpAw1HqGiEyXJPIYZoaLx2ENFVQU5Idvby/GGNM4jpeGRZ3Jic7q3Ph6fm/LqeiY1mR1yqYlERmqu
+ XYvUbHnoOa7ZItf+oq6sTciaUBe4nWMowV5DWbNXn5uCOn8BnX0wkOtsXZP2BrrnhFzOsL3abm8VBpRm
+ 6B7vJyauF+C62teQrkT6BBjwo+RtMPhBSmZ8kJaoazxy1DihHZ//Ho0u0Z5Q0ZXCNYVsNxpmZ09L4z3U
+ OQgbah0EObE3j015kch46Y3UxNb01fB46P1xzahgf2GVtElZE7IQD3Dh6Kw+5A7NyHm4AcMmlwD7jVI+
+ 6ptoAwMcyOlCb3sWgv1sc29dPchlIGKQkYFHZfoqdZ8fx+khsA5JII3UraTaaHSxwFTk5cJVSn+Lkczt
+ SqTirUo7O407Y0oeiBvEdE/dCW4mgDZd32aGeAB4EHggkNjHEFPdWG2krfTtwSOmVdAlZUPIIGN0XO6w
+ LuJcF3GeDo5OdqwzCdAP1ISvbnHw21VH5Il6ot6+duQ2B7rtabdpPnTbbO3sIM553qbsensNormuctfn
+ 4iWy5p55f2eDz7hYiObYor6RlIRuiF2iWlGx2IGn2XSjB9ddUhJYd2I2tmRVQc4z0/hvVPo7BMgbxKQ8
+ ztDxiSOEjc7Rw8CDgF3r6ryy+6VfedDhDALtwIhphSdIaaqPCwQhYzDXWqcTYPUOa0AneuA1DVB0cJ2f
+ mYrmRSn37I41CZDhMwwO1EUXns55r4O4mtfdbke0JX57ureFvHV7VD7AIHn70J4FOg5029zXziYtt5Fy
+ zXUyuvV6+eMKCcHm4sDgdJxki3vdA0etQkpU7I40nowbEvXiQsYmdtmEiG6m/sSDc5X2jkJK4DNFvjTO
+ PWxvF7FNDx3ZVKW1l36FH3AFQQYx3c6MC+wrrHCHlMWegv1IGy7IgtQN1onecXT+8RZD8zgav+nAhjTR
+ wT5Qnt87i6fdO9mXZDlYRe3qwbEcDWyhiUn4OfJRlsGiPupNbUkkaQgj6Jy32dviA75QcC2hbne0d3ob
+ /b6indHWNogN+jmvs7l3pBIEQF1DRFQopEByQUicnfPM807OShrbgan6UNFBTt+/TcRjvhwBtKzatGll
+ IiWSFonJHPuVOm9TltogfATcF2Zndhu1EGjrkNKYTkODlGuKlOGpZmAZSDovdxrg98nWoc1ABGLgyNN0
+ sKsDwFPPqhhuNgNbRpBv5YNQYMecIw9lqeN0dQeDRv3Rnkxgb8Py5d7OPNhebqEgRpjbndoZba3bF21s
+ t9NR16lRjLRuneV+LbiNmoaEkIKwjaSW7D2mHd0DR3XjYUdIZ2NrnCGPkxHJ6QiHSPeT+LBTaYSNYsEH
+ Xv3V9Fs5D1doN+06tXABXkBMzBE407Yzo/J9gTYjZmculoq5AKtX3PtzacTgMph0Ip0XnQZiMPIgdAeQ
+ PK9XR0bnon5QC4Q3pC7MsKazL9TvC63TK+t4pMGb9NthvyudsxkJyrGSRnWNqXOnCM4gWntmqplMjhhs
+ tdfyaKCtLYKVdUjyeFoXdZ6m3a7mvK3Rzmiftb+0kfuzpWhWjvLN9QxjpB5kK3VaaIby3DPEw6G5RHAC
+ unMjb7pepAGBkG7uTUNCCCs7JI31lkI6VHQAyXiFzs2UPBtKGt48deJA7VZd56R60DRnmyReDGJq60pL
+ KgtSlgCu2RRblIqEgWQMLINEB/ug+MAAjulY7/yxOntyEFlYQCc3nesrYC7Xuejg6JT4Dej0PpCH8tRD
+ nXiRF+naEARyFPJGWwuBnOQMOIMNaIuXd9DGLnKbS7srBm6ynax5dIlDGvdFvmhngLo6WO594WWivqtV
+ X14dlIklyKP2BRrtcyH5ZIrVY86YBPGqivppO+XDwSF8xP03/Uqw3h+yIC8OGjyAE5gfi0ZMk5bFrsRG
+ WG3q2yUlNhBSEokjklmjYxmVbrAqMwW1sUynRqfngeRGeZpRJ8zT4gES2L1VuE1NuM3SqupG/ebc7cKd
+ +g3u0u+7tb/b9g7OE3uj/B7hFklKytEW2saARscHOGZArlI+t7EcBJ+5D8Il1MGeNsZ5gNPA/ZGPstF2
+ rn2HcKegds5E+0i/WW2O8+x1nxbMxrbzfqvUf821OXeLQF/crnOU83sfDu8ROOaa7OkP+obf5KUMnjZ1
+ 0C/0g3vjGofd1e5RGfM1OqYPIF08KEjM19W8mJGWsXFc4Q9oowWM4Now884oZhH84LXfsDEPjJjapklL
+ vG+eCGxK1DdPBaRE4qGyI0yBgR72WmC7biSIijeXn9zLSv7GtgubLewrH7z7hHt1jIoLE6ABy7nYQ7zB
+ 4P12HVuNjQRXRzuxGMxbdFuQy0lWVfwGDDwDKDVn7eHh2640DW69ONbVf2Pz0g481iAi5Un3dse72+Rn
+ UDnn7XhQ7btGg+oahT7kYfU23CtgQUGoO3We/m33R9QZ5gjtH48ftnuvdE3Ozcre9flr+jPs61PsuoyF
+ P4yMha0a0rZMdSA1IWRogkssTVwoebYoDSkZ7Q7taPPn2uAK18G08zlz90MO3PnR1pKWQkhLKof9eNA4
+ Dj4o3Iga31dXhvJBTKQHnQ+QGnepjov78mco7z0i2yPCB5R/ZV+egNpyrvI9qXxvaKVv2bLCBswl6v2S
+ YCFx2BcyVJtaZSDiYPAh7a/K6RnaVqvsu4R3C+/UcT3v3AfleavqfEL5tk+cQ+JABh4s6mS/kP5BbQ4G
+ j2t/a9/5gLb1qtc97tAWbtcTumHMtyntYu15GAqMmEVVWwhNRKxtbexx9seU+odmW66o1Tj5EWYpXJRe
+ 8c2NmwvapktLj/kRT+OiPMG8P4IthmrEFvHQwiSoa5ny7FanPKhq36k9RGD/gJXP+SYxrAbjh9Txnymd
+ z82S3s1Pe7kOZsLXBGzTOj3d40pd91bV9QHt3ys8aGg6N+6dsucp398Lf6ffu8v5OBfXP8bqgsBOJAYg
+ nw94OR6A0eg5/caTbtKF1MZdqutjwgcNPrDU162zSePBGAy+oN9IR9LqessxfXOJ8mGeIC1R15dqDCFg
+ xCLRhhxDuIywIWkH7Q7tgXBin+fZPfTnsVek5dqNG03b5gC7ty0KzQfLLIQnnmxLl5bE+DyAyypoprgu
+ 140S+7pDaSHO6w4ux6EGGGQ6+/0CRGP/qMpe31cuoG2gfI/pWl/W/ikdW7ijm598tvcH4EXtr8nprTyQ
+ czAWmcaPVoPR45Qp6fkdF/piRtf8oM4/qf2Tkrg7ox7Ol98bVccndB4Cf1THR0X5qCvK2N4fyhd0fH5O
+ L7+DSBDtParvcQHpuqubN5XxetEUo9E/qNzdJT3aF3ukJUFyzCh8gV0qhOOCRMR02arBhtROODdDInqC
+ vQjhEEqYBwimMBEA52Mig7gwZhDaNUvLYl/aVLY/KDRsIbDMXghiNtLSLxDSkoZJlJtkinnUB0gvdbQ6
+ T1t0zFZ18qc1iJDzMRvEwegzKntzzteFNohJvm/pes/r2BYPdPOTz/aoqeHwl7qFIHy3PZ4PT38w+py1
+ ye1jI0TKF+0+QXV+UXhGeT/PcUk3EmvbpHNPCJ8TPqXj+Yj5gPBPOn5jTp/Ih4ocjZ7WNfVQ6AHSeOR8
+ XXDerj8aPavf9iqEtvqetF1IX+s8Ds6lQpASiYiPELZx2KPuH4T97/PufDImx3/xKeAEEwFbTHC5pAxS
+ htNTVrInNU7b8g1Mg2V0UCgH02F82JY0Mt4ZuVrZMOBZsfIx4fJST7ejo2NY5/cpkeFJ7VHL+j14Wp11
+ R1+5AOnKrwEa/kj5v6fjutM7+YIoxOr+JLwjp6d83ik89RBuYGSK2Yt+MmGyDIff1vWfU36ITGfHfbH6
+ +xnhywLENRs4zge0BeEwIf6gvdmOkZ7yRb1rdb0g/FfVBosXapvop0hTPqIKP9TxTa10Fy545Tif/h65
+ f8wAQiIRIR2EaxPNH1gQs2EZzYyeS8iQmKjvFikFNC+kDDW+X8RUQWM2la20xQP+FDTS0j3IG/X7UmsU
+ hJODEvV06w6oMz5inTwafUGd/nntv6Zy99q5ng6PdOV9Vvl+Ibys43rhQCdfEPNt1XD0H8p7S05P+WLg
+ T9L1MQ/26ve6fC5Dmw+uO0ovCN/WfeCERX9tVj3PCc+rri/peG5iDgYfUR3/quOp6rmkEzf8uIC0/qrK
+ PVzS+9oY97RaD06YPOntRjkwbm4RLrtMx3jSSEpIiXSEkBBQRKunaiFbM5fvHPC5+6qex4eIsR4CHyQI
+ iTALUqJ1s20ZgmF+YtYZvdCwrBwKaXm0PRku0rE/sC311FmMjCdOzomcA2ysYiT31J8H97sq/xXDQIMs
+ Wyrn6UIbA4Tt9FsBFWjvnWjrDrwT0+2o/5bXvSenp3zRMWco37PCV6WmsJns/nPekj/6ZaR7xKSAnC/q
+ OIgvj3j4DZ37loBTYzMq2qYR8zGV/09d97KS3mpfQNsy5X1U+b4kPCXCUffWcq6vndHHRDxeUJmwm1fo
+ mDgp/YLTg9eNJ40NCSkROP6pQScbJGsvdgGTq7fWlRVPBM+DjEhmCIlNGZLSSbk7Pn5gf/PifMs30Ic6
+ YwxA4/Sstca6WMfYxe7BtiQgfreOXXrhzEAeETjq69TvnVZVOEsvWV4n24vCgzlPF9qGygOB/yj8Rsdh
+ 4/UT0xe2/q9we05P+bwtHsuTahaZwpzYs2cqScqeJWCo9O9Xw8FLOiZssk5pOjZ8Q8fzEfNjyvcXXXs+
+ YuJ44VR9QXn3CrKvh3eWcxN9pS3aeGy5r4+TJpwlCX+/rokaJ1KBtNyhh5HxhJTxDaKiguuldxCtC4jX
+ BUQMMgYh7avDRcCZpCwOdU1Ka2u+gT6QsbzvQuHG6fEnBJGOocsTRiAYchEk5ilcbuX9Kf0J50t9rY6L
+ Y22sZnlJ+K4A2X6iDvxAX5mANiQy+f8s/J6HpKRPIya21F912CKmNjokfq/WfXxWeFp4SngW476cm9aO
+ uIdzVP/PBB6qf9QxZg1kxQb+lo4XQMzRf+n4UjvuPAyUKzhOUvIJ5ZfDOKCtEBQVvTby5XI5Te25R3mf
+ EyFxbpg8uFvHTDfK6bE0VDhjmr89BBmDaEGyINpcgCt4ROxDQopDZg72khJYO3Pj+1AyBzGzGvcQkYt8
+ 1DZxy6t1owTG66CzOgDn4NfCbaW+ic4u+5U2eANJGyTMYPALXfVD5dx0Yg6HP6iWyS4bjv6o47mJ6Q/J
+ X4Ugpk25pXzLdY6ZF2w3wkA4FzguT+tcr8cf0BbEvlnlfql6IOd39ft72v9Y++/I6orpuW77gpgfVd5/
+ 0/V6bcw6n0UNiN3KsawIRZnHjTq3fo98GZFmnjM2PLFVm0wY3qqyLGNj8cbrXVpKC7rdCCkZ62wTQjCD
+ e9PGibkACQOQMezJIOQEKa2d8aMPqQCF22oc1eXrAU+3p88MZ5sDv7+amclL8LeqE34ufDzqjHMpj6Up
+ z16V/2ftXxB+KTxSzk90dEmHmLLrRv9eLVs2PzFdYv6fYGovzgncz05dD7uNMIxLIo8SoCoJyn9GeXrD
+ PQFtPvioysGA+3hR+JGuJ+k/JGowHzEf0X38Scctr1ybjUH5vU75CNoTYCdw/xEBR2iv7UUA8vVBm/cz
+ U7gDOU8eN71JZQkTMY14lvsL5lFjT5r3LFsNUkr9BtFqibcAWGwyiJglZIuQoNXWfNBFKqTKLhgXkbyy
+ o8ZfJ2Li9Fyhm0SNv0tpOdqPFHpZnfZT/Y5XO/uJY7Mto99r/0PhNyrzaDl/kIjJgocRNiamxk2qH8lB
+ aAqVjdNFROCTCZ+2NEI9w+E3dcwUJP0xrT3RX4S/sJN/LkBM1PsPlD4fMT+ofL/V8YX5fEAbM1PMkz9Q
+ Dc02fK/KMBnxYYEY8DPKUwf6e8oHuc9QXknnmXv0m9kelqgxq4MaxwtHWuK8oL4hZUi+Fsn6APECOg4C
+ ZkQf1ei209rYlxigULYvy5ODaMcY5qkqatymH6/RjbJqhUFvdYrSblRHPKL03tCLtpBoV0gt/4vyo85/
+ R+eV9GlEcFWO+sMBml+VIx3+R3tWJB2tvUwHswGJNaK6iaU+LhBJwLn4tPIwnSfpaXFVnJgwSaa1KQYf
+ mxlCSo0vmJjMg/9UJGFVDmRAELCnz5nLZzHIvbqRWH10lwjKApb3qSwS9LP6HZGMiQGPNG3rlfdh5SUS
+ 4otl3Fk7zZxZty2RlmhHtKR9Y6hLtsKNCaItFN32ZfQmBkoFQUw6CGKy5pIYFbGtrjd+n/ZvjrLd+qZB
+ WwzmKRpEqXAN5sKJ+T2R+c+SmPM7Py7R/yJEuEjXGxA7RWJCPkgpQjIDNf6ojp80SeITBV8Xvqi2MWXY
+ OzMT0ObX86D2r4SfCvMT0ySgnKbBiClMbMD3CawbYPkahGSFki+pY+9L/SCpry1wtc405YlTrmPHojlz
+ +HoIZlg4Y0vcdI55boLpm3UeNc7DENIyk3KCYPuC3J650JsYKJXRGEQ4DZy0L5m6YtLf1xvej/FsZf2v
+ h70jXMKyhO2UqDeukY+1rVCH4WWjAn+r3/Orco93MpuzkHCRvFALsN8ceYx4Fjc11Y2kZEr0UeERpaPK
+ 3UNGOjk5CSF9U+V7w18BbU42NwN+pXZ+R2lzE7Oq7lV+guC0g2A7U5SQjqVzvIUY79Ow+scXyPi6T9Q7
+ 9uKDKoctbO/xRL3pOtHPaAtIL77Z/DjxSxzYky0u7XFK1DgSGxU+Qcpc72KgNxGkRgQxEek+2+M2CLZI
+ EybydYx45HVIRIgOR639RZ32pTgX1wlEmnUswXKb016Q8/MN4XdWZr4Au7/pR2gppiTNUdDtMeuBfYmU
+ dFKaYyG1XlWsbudeZpUmiUQc0ObmIQCDVj+AGdrimkg17GXCR/3hIj3Etkc943D56qZ3m0RjEbSHc2Jx
+ Bd4z37FkTSQqGKkMOVmyx3I4HKB483EuYrIU7xbVcXUxHZggYeoR34ExxhNnzMOufFUIGehNBNEQoU1M
+ Gs1MgL88dmZRc8QvWQRwXbeOsscBIgSEOpvbAWLFuYWKBj8T5g8XoYqdyD/W8XxTksyV/0F4e6QLQQqW
+ u0lqjvF0AZ7vJ3R/F6V6UIEs7pBUM3szTIKJ9kWatjcp389FuK/r97Q4pvcTfWjEsjAOapapXV5zIMYY
+ CyuYmUHt8p4RnjRmlPLYgpk7VR4HzVaWa5tGTFYTIWGZ8WElOoF1hEw4PmjFo0o4iLGvpWWubzHRmwii
+ IcW2mE5Ml5iocuyfWExQ30D8VoexOENSsNiBRUqkfM1AeiyToPRD+VwXpCvPMyLJS9p/X8fTFnF43a76
+ iDHWq4vIWzCj9PtUDxIzlt49pvSwJ+M1gzNVD6r/GeVHpU8jQdzPG8q9zDVX7n1UVdcrH9ISxwZJiSTH
+ /vN1kDP1Kh/Ab+a0EQysLMfGJxz2Ps5bfZN9HNdhRgppfL1uC6kL4U8386zt+Mh8a5ai5boWG72JgIaA
+ qcT0p4vwwvnWMf502ypvbfVNaPMBwgNctuzXSNicnvJFp7Hw4XnlJzA931w5q4uekGf6HQ3o87SrpLc6
+ kXxlf6XqfFll3tJJj2uv1Xk83Ie0JxRDJKFuL7DfaAgPvLPcjRVIOAktiZLystTu29qzIGQ+Ykotj+/X
+ ntc6gpSs9NkhMGfPmgQEAiAiQv+LnLaii5VBaARWysdM1bTrcJ8E1rEvMVV2anTjE4S141OmDWv7Mte1
+ 2OhNBDQETBJzYkURXjlr+OrXOTv1xOAzA/SycENO70LbWIPNbAZxwFiVNC0vxCSYLQlmszPzLRRGskOS
+ K3J6Kw/RBnc6sNce0oPUWrSb9qh+AvBPKe+NJa2+rjbPh+T1JXQsZJm2HtMJ4w84q9ghJX0a7xPhLW8t
+ xIkPL7BGAaKeoTayqgubk0A5mmvbnNdBIvprFCKzmQNcwx0fd1Rbjk+EhXJdi43eREBDCmhUENO9chf3
+ dBIdhjfHU+fvh5SbSIDYy0z1u0fLS2NNuuePMr5Hurrqu6WkR94usDEfFpil+aSO269WEBnw6MCw7C9W
+ XiQd6svLN3XZcbl3bOf3qB3v0d7va8+ebntndZ4Yp69Qd8ch6gzynq1zePs8PB/WMf0XdcQ18/EbVecN
+ 6m5X3zMmKenj+N8clpjR93zZ5DjCY/qN5ESto7l2VbP2ZiZCY67rsB4AD59xO7eatY8xbC2a8JA7PqA3
+ EURjBG6GBka4CPsDcc/TxWBgZ2KYt17W6kIbJCLOZnbjXBAZLlQ+Fidc23c+QwP5TuXFLuT9l7lfRuPh
+ wdYdj21ByVzQtkN1M4tVf0SqC22rlOde1YnDYeqzPsdrJi55CeEQOEdFG2GngdczdM/SPDYLAymJE8fn
+ VHjoYmkZe8ZARJJd6KuByI+9f2WYNNOgbbny8aIgTg/Skk9RI2i4htmXh9LxAb2JIBojDMpnBSEmthRi
+ fn15ungy6RSMcFc17hShYkhH1fOOCDfPQMv+0WD59Bd/DedL92dmkLo7yp73k98s75jBvNHK+uu37Bu4
+ lDhLee5QffJix/dpf36d38rU4BrUcZXVa16sPVCANqb3VGZP1UAjpU5W3bz6ijpFSmG+ZLBoVukaXHck
+ aDuSh/YzPUv88a4SxOZty9utLS6d8jWReDzgJ+pafPCBh5x8zMJgx8eHASAktl8g/qiU8+SjvrNVntCP
+ OzLxLSNfL8tvxqakmwqn/+M6m1L8MgLrJv2F1w4xQWkQDaOB2Bs+V446Z/YAqek3T0eHnUO8LQK//vI+
+ L9n77AWxNkCogqk0fx98OOY4ZjiYJsMrpSyBYwY1g7ripX3PNxzepGPWWurYXr0F1EN9GZH/ttIu2WN2
+ HaYoicPS5ut1t/GdHoLY/q41x5zzWCh5Cc8Qo6QMaQTtqRuQzoQD4Fx8LY1z/npylPW1n1wH8yJLSiel
+ k4/wDYSh79Fa7J2cLuXQXpCOceABgXR8NIJAPPYq98CeYyQl5IWUp5fxOz5dZ6USDql9CXoTAzSoAGJm
+ qUmn9Hxk3qSSx9ciAOxEZXU0HcNL9O1PkjjoOO88pFruTP8kSRv+KeZAN509x1FPF9TLOZy15hMsHg+U
+ zWXtdmCTcuxpfGGCPCC+FALy1ylID8T5aXn4YADv10Ci89R3EAUJ7t/2waaELD6hASkhJIQBjAHHTk7s
+ TneIICekjlAS899IfKZVAWEh0jnPf44zbpRDI8R1QloeMjUOehMD0SihT2piiyD6UWveKW7r0Lk8tTgN
+ /GkmX2bQE2oLPWKAuyhEsIFy+IehHJQPmD0rddd8fMrh3xwirXxnSOWtnoS67qjHymJnIakYQA2cDR4D
+ 2gfOkYe8HVhaOS+4Wm6OJ2AfBiBQzsN8pqluf8C31DblJCkhzIw6GfuPYycnq73cKUJyYl6huqmPsXCT
+ x8Ex6ZgPkD9IGesuqY+6TVoKhw0xs9R0crpdwhNLp+Al8tRio/GV2ugYOl9kNakwDZA5gzI9MKkcHT0N
+ kb9bZyDOk5f2AdoKsAHDDnTM1p8QdHBfgZyey8wPCBI25klmE0IstJC/wBXq28iixtLvEIYxYM9xQ87a
+ 5jTbnw+2Uh9jAdnbNibnIT9mgC9hDFKW/91pf6i/jxuLjd7EjGicUMg55QOtGOge3ySMgXqHpNhJuXMY
+ hLlAnklgvDcGfANUUUb7/GQ9k4i8tDFAm+dG91ue7jww4HxUlj333oA0T+/mOcGllkjifbehEAVtRN/S
+ xyHB7KUt7fO/eGRyQuK1Jijc/ucrw+XrwgJ7JyPX4TwCBfLXpBSMlMIhlZagNzEjGleQVXq3U+hMCMon
+ obnx+Iow8A5yqdqPON/koxPbWAnUuQGvu0F9ruTv1pnRqrdTF+1eKGygLb7IgKMWm09RZ8S5nMe/97Oh
+ aB36jj6sVXcRAOprJ2SRYoOyB/l791lQxFhA8vhEd7zdSHoQ0sivJ/M1RUrQm9hFNJIgde4UIXdK/usO
+ Ooab9w/g00F0indMP+K85/EyU6E6qbcP/sH5BrneLnI+Q7cuG8T2R/wjrYX6VdWC9c3H/G1vv7ugjwD9
+ Rb9BEggZUtKJMrlANwRECAnGIQsKGwuB+mI8Ahyv0tMU0jiR/7VDStCb2IdobEFfp3CDuWOic6KDDgog
+ f3kAou4WyrkDRW/d8yDueV/B9I2RRAhCdolSk6WDrqDojkWMh2Ge68S16vr7ePBqoTdxGnKjhW6n5I6J
+ zglEJx0QUDkBHef6WyjnFwVzXb9In7j/fQH9VkjSVttC7vNpCEHRHYtxUfU91zFEmQny943/q4nexLmQ
+ G18QNxU3mTunwN6q2w9QLqNb73zY1+t2rzcFF7Dvu95+oe63HpVd4wDGYhosb/e9nb7rHAr0Ji4E+WYS
+ omNq7Kls8cNBA4Pn2GNQ2mDPHjtn14rzucxiwa5Vrn2gmPZiV1/fZ3TzJ0xcI6Ev/2uGlK+88kr1//6M
+ rGyUeAVvAAAAAElFTkSuQmCC
+
+
+
\ No newline at end of file
diff --git a/SDRSharp/SDRSharp/MainForm.resx b/SDRSharp/SDRSharp/MainForm.resx
new file mode 100644
index 0000000..4b38d0b
--- /dev/null
+++ b/SDRSharp/SDRSharp/MainForm.resx
@@ -0,0 +1,283 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAAKYAAABECAYAAAALBE7+AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAu
+ IgAALiIBquLdkgAAJIBJREFUeF7tnYm3HVWVxit3eC8hMyEEQggBwiDBMIkCAQmTICgyhVEQBxAcEFG7
+ cYKogAOitO0sKDh2O7e6lqtH1+rV3f5Z9Pfb++yqXXXrvvcyPELiq7W+VbdOnXPq1Dlf7emcqlu98sor
+ S/gbgbZlPRgE9lTVsIt8fq/vJ+rou9aBojdxCUceWmTau7cmovYFu0faT4Gds3wdsi4aQXsTl3BkoUOg
+ TMgg33hHVc1ob9heVbMgjgvGVXWBoPy7naiJpLn+g0LQ3sQlHDnokAYSDQuxjJACpIOEy4UVgc1VdVQ+
+ FjhvZN1R7VCZQtJFImhv4hKODHSI4qR0tWyE3FZtCzJCwpXCqoLVCZHG+SBrTVJhToL2tWsh6E1cwpGB
+ RJCivhtSCpArCAkB11Tr1q3THqzvgLS1whqBvJmkEwQVDpicvYlLOPyRiSEUaWnqN5MSSQjZIN7R1erV
+ xyhlY7Vq1bE6dvjvjaLjMTq/Qb8hKiQNadol6EGRnr2JSzj8kQhRSFmNioOTSLkegq03QlYQcOVxOj6+
+ WrHiBO0zZHIqfaXOO1GVX0R2QkPsIOjy4jQdsPTsTVzC4Y9EhiAmZIE0KzY2khLpJ5Kt3KQ9hNwibNXv
+ k4RtwskF/CbtRAGiQlLKBEFDgqLiTXq6g7T/5OxNXMLhjyBBUaldaQmJ1pmkRApW1RYB8p0qnF7NzJyp
+ PTjDjmdnT9f+NIHzEBXyUsYJahLXSN6SnsJ+k7M3cQmHNxIBajUuQBKkGVINCXe0kcqJWVS7qWZUNZIR
+ Mr5eOFc4RxC3C1lnZ0+rli8/RTjJpGxVUYcEcUt6JnKabbtP5OxNXMLhjTT4hZhGDNR4Q0CXcscLx02p
+ g7IQGcLJdDSCXiDsFNPO0h5pSjoSFCJvLuodBwmCh2rfL3L2Ji7h8EYa+JCYmZgQBrW7MTk5kCecll6y
+ aFtRpCPS8w3CTuF1AgRFxWODnlAk8DGSm6HaEzkn1XrftUBv4hIOb8SgC5mYkAOSQBZX405KbEvUNlLw
+ bAM2pjtBSL5hp27qOqEaj8/XHgkaKh7pSV2u2letQtLuNzknEpZw+CMGXNDg10H1Qsz1ayUzNxTJBolO
+ EZB6IqXINh6/qRqNLhV2G6rqIgHHZ3XnGtRH+oWCS093kk4WqU+sjjrq+ETOjlqfn5ytgyUcGYjBFhj4
+ 7JFDDhHT7EuIiW2IGj5TUnKnSHlBIeYu4bJCzquFa5XnKsszSdBNKoNqxwZF6rbJ6SGlsDnDIdKDsntU
+ IgZLxPxbQQy2YMQUgpg4Pk5MJ81WSTlUMLYipEM1X5RIeVUhJrimGg7fansnX63itUE6EdPUe0POUOv+
+ IEDOCCWFPTtVatY/lnDkIAY6YpgCxMTDXlXmw8Mjx46EmISFIFVIyzcLIS3fIlwrXFf214qgN2i/S/nX
+ pGtCfMjdJqc7TOGtE0qCnLRlTpXeuqElHBmIQe4lJpLLbT+IiSeNjYkTdLJU9Q6p5TeKdFcUEl6ndIh5
+ nUnL4fD6IjUh5/XaXyPjoA43aUMS4rWfJyCFsUG3iZzUf2y1Zg1xzuwMjcsSvCViHumIARYY7PmIaVON
+ nfKoWKTqqSLeLhHwbcLbjZBOzBts7xLUCSuVncqjqpGaEBRyIpGRzOEMhUpfoRMQuVdq1g06HBANPxjo
+ q39f0a3zYKPvmhnd/B0UYtbBdYiJLQgxNsrGZGFGzIP3Xkub6pD37gS9SYCgNygdcgKkZ5BzcyqHJ463
+ TvgJj/8Uc4Y8EhAq3aSmz6lPSs2JxhxMxIWW8KqCAS6kNGk03rbN1GYTXGeFkKtXSIm6RaoxJ769pHc9
+ 72WqYZsIiMSEoJASSdqQs6rw3I9OZSQlx5DzrBJG4lqb7dpNCCkcoZbUtPJR0cECFU9BdNhU8BZeoO/8
+ YmLvXtv3tdtwKNq0H2BwA0ZKIaQlEmqNPGRJrJWbilOCxESi4bSIROb4YF9eaWEjzzNIY7tK54ht3twi
+ Z0jNUcW52ZKX9sRcOwF4wlKEpyakpjDhoU8Qa39RV9hgWoftO5p3VA4yrN7+a86LxWrTNHC9QN/5Ggwy
+ wK7MpAw1HqGiEyXJPIYZoaLx2ENFVQU5Idvby/GGNM4jpeGRZ3Jic7q3Ph6fm/LqeiY1mR1yqYlERmqu
+ XYvUbHnoOa7ZItf+oq6sTciaUBe4nWMowV5DWbNXn5uCOn8BnX0wkOtsXZP2BrrnhFzOsL3abm8VBpRm
+ 6B7vJyauF+C62teQrkT6BBjwo+RtMPhBSmZ8kJaoazxy1DihHZ//Ho0u0Z5Q0ZXCNYVsNxpmZ09L4z3U
+ OQgbah0EObE3j015kch46Y3UxNb01fB46P1xzahgf2GVtElZE7IQD3Dh6Kw+5A7NyHm4AcMmlwD7jVI+
+ 6ptoAwMcyOlCb3sWgv1sc29dPchlIGKQkYFHZfoqdZ8fx+khsA5JII3UraTaaHSxwFTk5cJVSn+Lkczt
+ SqTirUo7O407Y0oeiBvEdE/dCW4mgDZd32aGeAB4EHggkNjHEFPdWG2krfTtwSOmVdAlZUPIIGN0XO6w
+ LuJcF3GeDo5OdqwzCdAP1ISvbnHw21VH5Il6ot6+duQ2B7rtabdpPnTbbO3sIM553qbsensNormuctfn
+ 4iWy5p55f2eDz7hYiObYor6RlIRuiF2iWlGx2IGn2XSjB9ddUhJYd2I2tmRVQc4z0/hvVPo7BMgbxKQ8
+ ztDxiSOEjc7Rw8CDgF3r6ryy+6VfedDhDALtwIhphSdIaaqPCwQhYzDXWqcTYPUOa0AneuA1DVB0cJ2f
+ mYrmRSn37I41CZDhMwwO1EUXns55r4O4mtfdbke0JX57ureFvHV7VD7AIHn70J4FOg5029zXziYtt5Fy
+ zXUyuvV6+eMKCcHm4sDgdJxki3vdA0etQkpU7I40nowbEvXiQsYmdtmEiG6m/sSDc5X2jkJK4DNFvjTO
+ PWxvF7FNDx3ZVKW1l36FH3AFQQYx3c6MC+wrrHCHlMWegv1IGy7IgtQN1onecXT+8RZD8zgav+nAhjTR
+ wT5Qnt87i6fdO9mXZDlYRe3qwbEcDWyhiUn4OfJRlsGiPupNbUkkaQgj6Jy32dviA75QcC2hbne0d3ob
+ /b6indHWNogN+jmvs7l3pBIEQF1DRFQopEByQUicnfPM807OShrbgan6UNFBTt+/TcRjvhwBtKzatGll
+ IiWSFonJHPuVOm9TltogfATcF2Zndhu1EGjrkNKYTkODlGuKlOGpZmAZSDovdxrg98nWoc1ABGLgyNN0
+ sKsDwFPPqhhuNgNbRpBv5YNQYMecIw9lqeN0dQeDRv3Rnkxgb8Py5d7OPNhebqEgRpjbndoZba3bF21s
+ t9NR16lRjLRuneV+LbiNmoaEkIKwjaSW7D2mHd0DR3XjYUdIZ2NrnCGPkxHJ6QiHSPeT+LBTaYSNYsEH
+ Xv3V9Fs5D1doN+06tXABXkBMzBE407Yzo/J9gTYjZmculoq5AKtX3PtzacTgMph0Ip0XnQZiMPIgdAeQ
+ PK9XR0bnon5QC4Q3pC7MsKazL9TvC63TK+t4pMGb9NthvyudsxkJyrGSRnWNqXOnCM4gWntmqplMjhhs
+ tdfyaKCtLYKVdUjyeFoXdZ6m3a7mvK3Rzmiftb+0kfuzpWhWjvLN9QxjpB5kK3VaaIby3DPEw6G5RHAC
+ unMjb7pepAGBkG7uTUNCCCs7JI31lkI6VHQAyXiFzs2UPBtKGt48deJA7VZd56R60DRnmyReDGJq60pL
+ KgtSlgCu2RRblIqEgWQMLINEB/ug+MAAjulY7/yxOntyEFlYQCc3nesrYC7Xuejg6JT4Dej0PpCH8tRD
+ nXiRF+naEARyFPJGWwuBnOQMOIMNaIuXd9DGLnKbS7srBm6ynax5dIlDGvdFvmhngLo6WO594WWivqtV
+ X14dlIklyKP2BRrtcyH5ZIrVY86YBPGqivppO+XDwSF8xP03/Uqw3h+yIC8OGjyAE5gfi0ZMk5bFrsRG
+ WG3q2yUlNhBSEokjklmjYxmVbrAqMwW1sUynRqfngeRGeZpRJ8zT4gES2L1VuE1NuM3SqupG/ebc7cKd
+ +g3u0u+7tb/b9g7OE3uj/B7hFklKytEW2saARscHOGZArlI+t7EcBJ+5D8Il1MGeNsZ5gNPA/ZGPstF2
+ rn2HcKegds5E+0i/WW2O8+x1nxbMxrbzfqvUf821OXeLQF/crnOU83sfDu8ROOaa7OkP+obf5KUMnjZ1
+ 0C/0g3vjGofd1e5RGfM1OqYPIF08KEjM19W8mJGWsXFc4Q9oowWM4Now884oZhH84LXfsDEPjJjapklL
+ vG+eCGxK1DdPBaRE4qGyI0yBgR72WmC7biSIijeXn9zLSv7GtgubLewrH7z7hHt1jIoLE6ABy7nYQ7zB
+ 4P12HVuNjQRXRzuxGMxbdFuQy0lWVfwGDDwDKDVn7eHh2640DW69ONbVf2Pz0g481iAi5Un3dse72+Rn
+ UDnn7XhQ7btGg+oahT7kYfU23CtgQUGoO3We/m33R9QZ5gjtH48ftnuvdE3Ozcre9flr+jPs61PsuoyF
+ P4yMha0a0rZMdSA1IWRogkssTVwoebYoDSkZ7Q7taPPn2uAK18G08zlz90MO3PnR1pKWQkhLKof9eNA4
+ Dj4o3Iga31dXhvJBTKQHnQ+QGnepjov78mco7z0i2yPCB5R/ZV+egNpyrvI9qXxvaKVv2bLCBswl6v2S
+ YCFx2BcyVJtaZSDiYPAh7a/K6RnaVqvsu4R3C+/UcT3v3AfleavqfEL5tk+cQ+JABh4s6mS/kP5BbQ4G
+ j2t/a9/5gLb1qtc97tAWbtcTumHMtyntYu15GAqMmEVVWwhNRKxtbexx9seU+odmW66o1Tj5EWYpXJRe
+ 8c2NmwvapktLj/kRT+OiPMG8P4IthmrEFvHQwiSoa5ny7FanPKhq36k9RGD/gJXP+SYxrAbjh9Txnymd
+ z82S3s1Pe7kOZsLXBGzTOj3d40pd91bV9QHt3ys8aGg6N+6dsucp398Lf6ffu8v5OBfXP8bqgsBOJAYg
+ nw94OR6A0eg5/caTbtKF1MZdqutjwgcNPrDU162zSePBGAy+oN9IR9LqessxfXOJ8mGeIC1R15dqDCFg
+ xCLRhhxDuIywIWkH7Q7tgXBin+fZPfTnsVek5dqNG03b5gC7ty0KzQfLLIQnnmxLl5bE+DyAyypoprgu
+ 140S+7pDaSHO6w4ux6EGGGQ6+/0CRGP/qMpe31cuoG2gfI/pWl/W/ikdW7ijm598tvcH4EXtr8nprTyQ
+ czAWmcaPVoPR45Qp6fkdF/piRtf8oM4/qf2Tkrg7ox7Ol98bVccndB4Cf1THR0X5qCvK2N4fyhd0fH5O
+ L7+DSBDtParvcQHpuqubN5XxetEUo9E/qNzdJT3aF3ukJUFyzCh8gV0qhOOCRMR02arBhtROODdDInqC
+ vQjhEEqYBwimMBEA52Mig7gwZhDaNUvLYl/aVLY/KDRsIbDMXghiNtLSLxDSkoZJlJtkinnUB0gvdbQ6
+ T1t0zFZ18qc1iJDzMRvEwegzKntzzteFNohJvm/pes/r2BYPdPOTz/aoqeHwl7qFIHy3PZ4PT38w+py1
+ ye1jI0TKF+0+QXV+UXhGeT/PcUk3EmvbpHNPCJ8TPqXj+Yj5gPBPOn5jTp/Ih4ocjZ7WNfVQ6AHSeOR8
+ XXDerj8aPavf9iqEtvqetF1IX+s8Ds6lQpASiYiPELZx2KPuH4T97/PufDImx3/xKeAEEwFbTHC5pAxS
+ htNTVrInNU7b8g1Mg2V0UCgH02F82JY0Mt4ZuVrZMOBZsfIx4fJST7ejo2NY5/cpkeFJ7VHL+j14Wp11
+ R1+5AOnKrwEa/kj5v6fjutM7+YIoxOr+JLwjp6d83ik89RBuYGSK2Yt+MmGyDIff1vWfU36ITGfHfbH6
+ +xnhywLENRs4zge0BeEwIf6gvdmOkZ7yRb1rdb0g/FfVBosXapvop0hTPqIKP9TxTa10Fy545Tif/h65
+ f8wAQiIRIR2EaxPNH1gQs2EZzYyeS8iQmKjvFikFNC+kDDW+X8RUQWM2la20xQP+FDTS0j3IG/X7UmsU
+ hJODEvV06w6oMz5inTwafUGd/nntv6Zy99q5ng6PdOV9Vvl+Ibys43rhQCdfEPNt1XD0H8p7S05P+WLg
+ T9L1MQ/26ve6fC5Dmw+uO0ovCN/WfeCERX9tVj3PCc+rri/peG5iDgYfUR3/quOp6rmkEzf8uIC0/qrK
+ PVzS+9oY97RaD06YPOntRjkwbm4RLrtMx3jSSEpIiXSEkBBQRKunaiFbM5fvHPC5+6qex4eIsR4CHyQI
+ iTALUqJ1s20ZgmF+YtYZvdCwrBwKaXm0PRku0rE/sC311FmMjCdOzomcA2ysYiT31J8H97sq/xXDQIMs
+ Wyrn6UIbA4Tt9FsBFWjvnWjrDrwT0+2o/5bXvSenp3zRMWco37PCV6WmsJns/nPekj/6ZaR7xKSAnC/q
+ OIgvj3j4DZ37loBTYzMq2qYR8zGV/09d97KS3mpfQNsy5X1U+b4kPCXCUffWcq6vndHHRDxeUJmwm1fo
+ mDgp/YLTg9eNJ40NCSkROP6pQScbJGsvdgGTq7fWlRVPBM+DjEhmCIlNGZLSSbk7Pn5gf/PifMs30Ic6
+ YwxA4/Sstca6WMfYxe7BtiQgfreOXXrhzEAeETjq69TvnVZVOEsvWV4n24vCgzlPF9qGygOB/yj8Rsdh
+ 4/UT0xe2/q9we05P+bwtHsuTahaZwpzYs2cqScqeJWCo9O9Xw8FLOiZssk5pOjZ8Q8fzEfNjyvcXXXs+
+ YuJ44VR9QXn3CrKvh3eWcxN9pS3aeGy5r4+TJpwlCX+/rokaJ1KBtNyhh5HxhJTxDaKiguuldxCtC4jX
+ BUQMMgYh7avDRcCZpCwOdU1Ka2u+gT6QsbzvQuHG6fEnBJGOocsTRiAYchEk5ilcbuX9Kf0J50t9rY6L
+ Y22sZnlJ+K4A2X6iDvxAX5mANiQy+f8s/J6HpKRPIya21F912CKmNjokfq/WfXxWeFp4SngW476cm9aO
+ uIdzVP/PBB6qf9QxZg1kxQb+lo4XQMzRf+n4UjvuPAyUKzhOUvIJ5ZfDOKCtEBQVvTby5XI5Te25R3mf
+ EyFxbpg8uFvHTDfK6bE0VDhjmr89BBmDaEGyINpcgCt4ROxDQopDZg72khJYO3Pj+1AyBzGzGvcQkYt8
+ 1DZxy6t1owTG66CzOgDn4NfCbaW+ic4u+5U2eANJGyTMYPALXfVD5dx0Yg6HP6iWyS4bjv6o47mJ6Q/J
+ X4Ugpk25pXzLdY6ZF2w3wkA4FzguT+tcr8cf0BbEvlnlfql6IOd39ft72v9Y++/I6orpuW77gpgfVd5/
+ 0/V6bcw6n0UNiN3KsawIRZnHjTq3fo98GZFmnjM2PLFVm0wY3qqyLGNj8cbrXVpKC7rdCCkZ62wTQjCD
+ e9PGibkACQOQMezJIOQEKa2d8aMPqQCF22oc1eXrAU+3p88MZ5sDv7+amclL8LeqE34ufDzqjHMpj6Up
+ z16V/2ftXxB+KTxSzk90dEmHmLLrRv9eLVs2PzFdYv6fYGovzgncz05dD7uNMIxLIo8SoCoJyn9GeXrD
+ PQFtPvioysGA+3hR+JGuJ+k/JGowHzEf0X38Scctr1ybjUH5vU75CNoTYCdw/xEBR2iv7UUA8vVBm/cz
+ U7gDOU8eN71JZQkTMY14lvsL5lFjT5r3LFsNUkr9BtFqibcAWGwyiJglZIuQoNXWfNBFKqTKLhgXkbyy
+ o8ZfJ2Li9Fyhm0SNv0tpOdqPFHpZnfZT/Y5XO/uJY7Mto99r/0PhNyrzaDl/kIjJgocRNiamxk2qH8lB
+ aAqVjdNFROCTCZ+2NEI9w+E3dcwUJP0xrT3RX4S/sJN/LkBM1PsPlD4fMT+ofL/V8YX5fEAbM1PMkz9Q
+ Dc02fK/KMBnxYYEY8DPKUwf6e8oHuc9QXknnmXv0m9kelqgxq4MaxwtHWuK8oL4hZUi+Fsn6APECOg4C
+ ZkQf1ei209rYlxigULYvy5ODaMcY5qkqatymH6/RjbJqhUFvdYrSblRHPKL03tCLtpBoV0gt/4vyo85/
+ R+eV9GlEcFWO+sMBml+VIx3+R3tWJB2tvUwHswGJNaK6iaU+LhBJwLn4tPIwnSfpaXFVnJgwSaa1KQYf
+ mxlCSo0vmJjMg/9UJGFVDmRAELCnz5nLZzHIvbqRWH10lwjKApb3qSwS9LP6HZGMiQGPNG3rlfdh5SUS
+ 4otl3Fk7zZxZty2RlmhHtKR9Y6hLtsKNCaItFN32ZfQmBkoFQUw6CGKy5pIYFbGtrjd+n/ZvjrLd+qZB
+ WwzmKRpEqXAN5sKJ+T2R+c+SmPM7Py7R/yJEuEjXGxA7RWJCPkgpQjIDNf6ojp80SeITBV8Xvqi2MWXY
+ OzMT0ObX86D2r4SfCvMT0ySgnKbBiClMbMD3CawbYPkahGSFki+pY+9L/SCpry1wtc405YlTrmPHojlz
+ +HoIZlg4Y0vcdI55boLpm3UeNc7DENIyk3KCYPuC3J650JsYKJXRGEQ4DZy0L5m6YtLf1xvej/FsZf2v
+ h70jXMKyhO2UqDeukY+1rVCH4WWjAn+r3/Orco93MpuzkHCRvFALsN8ceYx4Fjc11Y2kZEr0UeERpaPK
+ 3UNGOjk5CSF9U+V7w18BbU42NwN+pXZ+R2lzE7Oq7lV+guC0g2A7U5SQjqVzvIUY79Ow+scXyPi6T9Q7
+ 9uKDKoctbO/xRL3pOtHPaAtIL77Z/DjxSxzYky0u7XFK1DgSGxU+Qcpc72KgNxGkRgQxEek+2+M2CLZI
+ EybydYx45HVIRIgOR639RZ32pTgX1wlEmnUswXKb016Q8/MN4XdWZr4Au7/pR2gppiTNUdDtMeuBfYmU
+ dFKaYyG1XlWsbudeZpUmiUQc0ObmIQCDVj+AGdrimkg17GXCR/3hIj3Etkc943D56qZ3m0RjEbSHc2Jx
+ Bd4z37FkTSQqGKkMOVmyx3I4HKB483EuYrIU7xbVcXUxHZggYeoR34ExxhNnzMOufFUIGehNBNEQoU1M
+ Gs1MgL88dmZRc8QvWQRwXbeOsscBIgSEOpvbAWLFuYWKBj8T5g8XoYqdyD/W8XxTksyV/0F4e6QLQQqW
+ u0lqjvF0AZ7vJ3R/F6V6UIEs7pBUM3szTIKJ9kWatjcp389FuK/r97Q4pvcTfWjEsjAOapapXV5zIMYY
+ CyuYmUHt8p4RnjRmlPLYgpk7VR4HzVaWa5tGTFYTIWGZ8WElOoF1hEw4PmjFo0o4iLGvpWWubzHRmwii
+ IcW2mE5Ml5iocuyfWExQ30D8VoexOENSsNiBRUqkfM1AeiyToPRD+VwXpCvPMyLJS9p/X8fTFnF43a76
+ iDHWq4vIWzCj9PtUDxIzlt49pvSwJ+M1gzNVD6r/GeVHpU8jQdzPG8q9zDVX7n1UVdcrH9ISxwZJiSTH
+ /vN1kDP1Kh/Ab+a0EQysLMfGJxz2Ps5bfZN9HNdhRgppfL1uC6kL4U8386zt+Mh8a5ai5boWG72JgIaA
+ qcT0p4vwwvnWMf502ypvbfVNaPMBwgNctuzXSNicnvJFp7Hw4XnlJzA931w5q4uekGf6HQ3o87SrpLc6
+ kXxlf6XqfFll3tJJj2uv1Xk83Ie0JxRDJKFuL7DfaAgPvLPcjRVIOAktiZLystTu29qzIGQ+Ykotj+/X
+ ntc6gpSs9NkhMGfPmgQEAiAiQv+LnLaii5VBaARWysdM1bTrcJ8E1rEvMVV2anTjE4S141OmDWv7Mte1
+ 2OhNBDQETBJzYkURXjlr+OrXOTv1xOAzA/SycENO70LbWIPNbAZxwFiVNC0vxCSYLQlmszPzLRRGskOS
+ K3J6Kw/RBnc6sNce0oPUWrSb9qh+AvBPKe+NJa2+rjbPh+T1JXQsZJm2HtMJ4w84q9ghJX0a7xPhLW8t
+ xIkPL7BGAaKeoTayqgubk0A5mmvbnNdBIvprFCKzmQNcwx0fd1Rbjk+EhXJdi43eREBDCmhUENO9chf3
+ dBIdhjfHU+fvh5SbSIDYy0z1u0fLS2NNuuePMr5Hurrqu6WkR94usDEfFpil+aSO269WEBnw6MCw7C9W
+ XiQd6svLN3XZcbl3bOf3qB3v0d7va8+ebntndZ4Yp69Qd8ch6gzynq1zePs8PB/WMf0XdcQ18/EbVecN
+ 6m5X3zMmKenj+N8clpjR93zZ5DjCY/qN5ESto7l2VbP2ZiZCY67rsB4AD59xO7eatY8xbC2a8JA7PqA3
+ EURjBG6GBka4CPsDcc/TxWBgZ2KYt17W6kIbJCLOZnbjXBAZLlQ+Fidc23c+QwP5TuXFLuT9l7lfRuPh
+ wdYdj21ByVzQtkN1M4tVf0SqC22rlOde1YnDYeqzPsdrJi55CeEQOEdFG2GngdczdM/SPDYLAymJE8fn
+ VHjoYmkZe8ZARJJd6KuByI+9f2WYNNOgbbny8aIgTg/Skk9RI2i4htmXh9LxAb2JIBojDMpnBSEmthRi
+ fn15ungy6RSMcFc17hShYkhH1fOOCDfPQMv+0WD59Bd/DedL92dmkLo7yp73k98s75jBvNHK+uu37Bu4
+ lDhLee5QffJix/dpf36d38rU4BrUcZXVa16sPVCANqb3VGZP1UAjpU5W3bz6ijpFSmG+ZLBoVukaXHck
+ aDuSh/YzPUv88a4SxOZty9utLS6d8jWReDzgJ+pafPCBh5x8zMJgx8eHASAktl8g/qiU8+SjvrNVntCP
+ OzLxLSNfL8tvxqakmwqn/+M6m1L8MgLrJv2F1w4xQWkQDaOB2Bs+V446Z/YAqek3T0eHnUO8LQK//vI+
+ L9n77AWxNkCogqk0fx98OOY4ZjiYJsMrpSyBYwY1g7ripX3PNxzepGPWWurYXr0F1EN9GZH/ttIu2WN2
+ HaYoicPS5ut1t/GdHoLY/q41x5zzWCh5Cc8Qo6QMaQTtqRuQzoQD4Fx8LY1z/npylPW1n1wH8yJLSiel
+ k4/wDYSh79Fa7J2cLuXQXpCOceABgXR8NIJAPPYq98CeYyQl5IWUp5fxOz5dZ6USDql9CXoTAzSoAGJm
+ qUmn9Hxk3qSSx9ciAOxEZXU0HcNL9O1PkjjoOO88pFruTP8kSRv+KeZAN509x1FPF9TLOZy15hMsHg+U
+ zWXtdmCTcuxpfGGCPCC+FALy1ylID8T5aXn4YADv10Ci89R3EAUJ7t/2waaELD6hASkhJIQBjAHHTk7s
+ TneIICekjlAS899IfKZVAWEh0jnPf44zbpRDI8R1QloeMjUOehMD0SihT2piiyD6UWveKW7r0Lk8tTgN
+ /GkmX2bQE2oLPWKAuyhEsIFy+IehHJQPmD0rddd8fMrh3xwirXxnSOWtnoS67qjHymJnIakYQA2cDR4D
+ 2gfOkYe8HVhaOS+4Wm6OJ2AfBiBQzsN8pqluf8C31DblJCkhzIw6GfuPYycnq73cKUJyYl6huqmPsXCT
+ x8Ex6ZgPkD9IGesuqY+6TVoKhw0xs9R0crpdwhNLp+Al8tRio/GV2ugYOl9kNakwDZA5gzI9MKkcHT0N
+ kb9bZyDOk5f2AdoKsAHDDnTM1p8QdHBfgZyey8wPCBI25klmE0IstJC/wBXq28iixtLvEIYxYM9xQ87a
+ 5jTbnw+2Uh9jAdnbNibnIT9mgC9hDFKW/91pf6i/jxuLjd7EjGicUMg55QOtGOge3ySMgXqHpNhJuXMY
+ hLlAnklgvDcGfANUUUb7/GQ9k4i8tDFAm+dG91ue7jww4HxUlj333oA0T+/mOcGllkjifbehEAVtRN/S
+ xyHB7KUt7fO/eGRyQuK1Jijc/ucrw+XrwgJ7JyPX4TwCBfLXpBSMlMIhlZagNzEjGleQVXq3U+hMCMon
+ obnx+Iow8A5yqdqPON/koxPbWAnUuQGvu0F9ruTv1pnRqrdTF+1eKGygLb7IgKMWm09RZ8S5nMe/97Oh
+ aB36jj6sVXcRAOprJ2SRYoOyB/l791lQxFhA8vhEd7zdSHoQ0sivJ/M1RUrQm9hFNJIgde4UIXdK/usO
+ Ooab9w/g00F0indMP+K85/EyU6E6qbcP/sH5BrneLnI+Q7cuG8T2R/wjrYX6VdWC9c3H/G1vv7ugjwD9
+ Rb9BEggZUtKJMrlANwRECAnGIQsKGwuB+mI8Ahyv0tMU0jiR/7VDStCb2IdobEFfp3CDuWOic6KDDgog
+ f3kAou4WyrkDRW/d8yDueV/B9I2RRAhCdolSk6WDrqDojkWMh2Ge68S16vr7ePBqoTdxGnKjhW6n5I6J
+ zglEJx0QUDkBHef6WyjnFwVzXb9In7j/fQH9VkjSVttC7vNpCEHRHYtxUfU91zFEmQny943/q4nexLmQ
+ G18QNxU3mTunwN6q2w9QLqNb73zY1+t2rzcFF7Dvu95+oe63HpVd4wDGYhosb/e9nb7rHAr0Ji4E+WYS
+ omNq7Kls8cNBA4Pn2GNQ2mDPHjtn14rzucxiwa5Vrn2gmPZiV1/fZ3TzJ0xcI6Ev/2uGlK+88kr1//6M
+ rGyUeAVvAAAAAElFTkSuQmCC
+
+
+
\ No newline at end of file
diff --git a/SDRSharp/SDRSharp/Program.cs b/SDRSharp/SDRSharp/Program.cs
new file mode 100644
index 0000000..7bea792
--- /dev/null
+++ b/SDRSharp/SDRSharp/Program.cs
@@ -0,0 +1,48 @@
+using SDRSharp.Radio;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Text;
+using System.Windows.Forms;
+
+namespace SDRSharp
+{
+ public static class Program
+ {
+ [STAThread]
+ private static void Main()
+ {
+ AppDomain.CurrentDomain.UnhandledException += Program.CurrentDomain_UnhandledException;
+ if (Environment.OSVersion.Platform == PlatformID.Win32Windows || Environment.OSVersion.Platform == PlatformID.Win32NT)
+ {
+ Process currentProcess = Process.GetCurrentProcess();
+ currentProcess.PriorityBoostEnabled = true;
+ currentProcess.PriorityClass = (ProcessPriorityClass)Utils.GetIntSetting("processPriority", 256);
+ Utils.TimeBeginPeriod(1u);
+ }
+ DSPThreadPool.Initialize();
+ Control.CheckForIllegalCrossThreadCalls = false;
+ Application.EnableVisualStyles();
+ Application.Run(new MainForm());
+ if (Environment.OSVersion.Platform == PlatformID.Win32Windows || Environment.OSVersion.Platform == PlatformID.Win32NT)
+ {
+ Utils.TimeEndPeriod(1u);
+ }
+ DSPThreadPool.Terminate();
+ Application.Exit();
+ }
+
+ private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
+ {
+ Exception ex = (Exception)e.ExceptionObject;
+ StackTrace stackTrace = new StackTrace(ex);
+ StringBuilder stringBuilder = new StringBuilder();
+ StackFrame[] frames = stackTrace.GetFrames();
+ foreach (StackFrame stackFrame in frames)
+ {
+ stringBuilder.AppendLine("at " + stackFrame.GetMethod().Module.Name + "." + stackFrame.GetMethod().ReflectedType.Name + "." + stackFrame.GetMethod().Name + " (IL offset: 0x" + stackFrame.GetILOffset().ToString("x") + ")");
+ }
+ File.WriteAllText("crash.txt", ex.Message + Environment.NewLine + stringBuilder);
+ }
+ }
+}
diff --git a/SDRSharp/SDRSharp/SDRSharp.ico b/SDRSharp/SDRSharp/SDRSharp.ico
new file mode 100644
index 0000000..6033e98
Binary files /dev/null and b/SDRSharp/SDRSharp/SDRSharp.ico differ
diff --git a/SDRSharp/SDRSharp/SharpControlProxy.cs b/SDRSharp/SDRSharp/SharpControlProxy.cs
new file mode 100644
index 0000000..0038e44
--- /dev/null
+++ b/SDRSharp/SDRSharp/SharpControlProxy.cs
@@ -0,0 +1,1296 @@
+using SDRSharp.Common;
+using SDRSharp.PanView;
+using SDRSharp.Radio;
+using System;
+using System.ComponentModel;
+using System.Drawing.Drawing2D;
+using System.Windows.Forms;
+
+namespace SDRSharp
+{
+ public class SharpControlProxy : ISharpControl, INotifyPropertyChanged
+ {
+ private readonly MainForm _owner;
+
+ public bool Enabled
+ {
+ get;
+ set;
+ }
+
+ public DetectorType DetectorType
+ {
+ get
+ {
+ return this._owner.DetectorType;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.DetectorType = value;
+ });
+ }
+ else
+ {
+ this._owner.DetectorType = value;
+ }
+ }
+ }
+ }
+
+ public WindowType FilterType
+ {
+ get
+ {
+ return this._owner.FilterType;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.FilterType = value;
+ });
+ }
+ else
+ {
+ this._owner.FilterType = value;
+ }
+ }
+ }
+ }
+
+ public int AudioGain
+ {
+ get
+ {
+ return this._owner.AudioGain;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.AudioGain = value;
+ });
+ }
+ else
+ {
+ this._owner.AudioGain = value;
+ }
+ }
+ }
+ }
+
+ public bool AudioIsMuted
+ {
+ get
+ {
+ return this._owner.AudioIsMuted;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.AudioIsMuted = value;
+ });
+ }
+ else
+ {
+ this._owner.AudioIsMuted = value;
+ }
+ }
+ }
+ }
+
+ public long CenterFrequency
+ {
+ get
+ {
+ return this._owner.CenterFrequency;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.CenterFrequency = value;
+ });
+ }
+ else
+ {
+ this._owner.CenterFrequency = value;
+ }
+ }
+ }
+ }
+
+ public int CWShift
+ {
+ get
+ {
+ return this._owner.CWShift;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.CWShift = value;
+ });
+ }
+ else
+ {
+ this._owner.CWShift = value;
+ }
+ }
+ }
+ }
+
+ public bool FilterAudio
+ {
+ get
+ {
+ return this._owner.FilterAudio;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.FilterAudio = value;
+ });
+ }
+ else
+ {
+ this._owner.FilterAudio = value;
+ }
+ }
+ }
+ }
+
+ public bool UnityGain
+ {
+ get
+ {
+ return this._owner.UnityGain;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.UnityGain = value;
+ });
+ }
+ else
+ {
+ this._owner.UnityGain = value;
+ }
+ }
+ }
+ }
+
+ public int FilterBandwidth
+ {
+ get
+ {
+ return this._owner.FilterBandwidth;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.FilterBandwidth = value;
+ });
+ }
+ else
+ {
+ this._owner.FilterBandwidth = value;
+ }
+ }
+ }
+ }
+
+ public int FilterOrder
+ {
+ get
+ {
+ return this._owner.FilterOrder;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.FilterOrder = value;
+ });
+ }
+ else
+ {
+ this._owner.FilterOrder = value;
+ }
+ }
+ }
+ }
+
+ public bool FmStereo
+ {
+ get
+ {
+ return this._owner.FmStereo;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.FmStereo = value;
+ });
+ }
+ else
+ {
+ this._owner.FmStereo = value;
+ }
+ }
+ }
+ }
+
+ public long Frequency
+ {
+ get
+ {
+ return this._owner.Frequency;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.Frequency = value;
+ });
+ }
+ else
+ {
+ this._owner.Frequency = value;
+ }
+ }
+ }
+ }
+
+ public long FrequencyShift
+ {
+ get
+ {
+ return this._owner.FrequencyShift;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.FrequencyShift = value;
+ });
+ }
+ else
+ {
+ this._owner.FrequencyShift = value;
+ }
+ }
+ }
+ }
+
+ public bool FrequencyShiftEnabled
+ {
+ get
+ {
+ return this._owner.FrequencyShiftEnabled;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.FrequencyShiftEnabled = value;
+ });
+ }
+ else
+ {
+ this._owner.FrequencyShiftEnabled = value;
+ }
+ }
+ }
+ }
+
+ public bool UseAgc
+ {
+ get
+ {
+ return this._owner.UseAgc;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.UseAgc = value;
+ });
+ }
+ else
+ {
+ this._owner.UseAgc = value;
+ }
+ }
+ }
+ }
+
+ public bool AgcHang
+ {
+ get
+ {
+ return this._owner.AgcHang;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.AgcHang = value;
+ });
+ }
+ else
+ {
+ this._owner.AgcHang = value;
+ }
+ }
+ }
+ }
+
+ public int AgcThreshold
+ {
+ get
+ {
+ return this._owner.AgcThreshold;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.AgcThreshold = value;
+ });
+ }
+ else
+ {
+ this._owner.AgcThreshold = value;
+ }
+ }
+ }
+ }
+
+ public int AgcDecay
+ {
+ get
+ {
+ return this._owner.AgcDecay;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.AgcDecay = value;
+ });
+ }
+ else
+ {
+ this._owner.AgcDecay = value;
+ }
+ }
+ }
+ }
+
+ public int AgcSlope
+ {
+ get
+ {
+ return this._owner.AgcSlope;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.AgcSlope = value;
+ });
+ }
+ else
+ {
+ this._owner.AgcSlope = value;
+ }
+ }
+ }
+ }
+
+ public bool MarkPeaks
+ {
+ get
+ {
+ return this._owner.MarkPeaks;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.MarkPeaks = value;
+ });
+ }
+ else
+ {
+ this._owner.MarkPeaks = value;
+ }
+ }
+ }
+ }
+
+ public bool SnapToGrid
+ {
+ get
+ {
+ return this._owner.SnapToGrid;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.SnapToGrid = value;
+ });
+ }
+ else
+ {
+ this._owner.SnapToGrid = value;
+ }
+ }
+ }
+ }
+
+ public bool SquelchEnabled
+ {
+ get
+ {
+ return this._owner.SquelchEnabled;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.SquelchEnabled = value;
+ });
+ }
+ else
+ {
+ this._owner.SquelchEnabled = value;
+ }
+ }
+ }
+ }
+
+ public int SquelchThreshold
+ {
+ get
+ {
+ return this._owner.SquelchThreshold;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.SquelchThreshold = value;
+ });
+ }
+ else
+ {
+ this._owner.SquelchThreshold = value;
+ }
+ }
+ }
+ }
+
+ public bool SwapIq
+ {
+ get
+ {
+ return this._owner.SwapIq;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.SwapIq = value;
+ });
+ }
+ else
+ {
+ this._owner.SwapIq = value;
+ }
+ }
+ }
+ }
+
+ public bool IsPlaying
+ {
+ get
+ {
+ return this._owner.IsPlaying;
+ }
+ }
+
+ public float WAttack
+ {
+ get
+ {
+ return this._owner.WAttack;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.WAttack = value;
+ });
+ }
+ else
+ {
+ this._owner.WAttack = value;
+ }
+ }
+ }
+ }
+
+ public float WDecay
+ {
+ get
+ {
+ return this._owner.WDecay;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.WDecay = value;
+ });
+ }
+ else
+ {
+ this._owner.WDecay = value;
+ }
+ }
+ }
+ }
+
+ public float SAttack
+ {
+ get
+ {
+ return this._owner.SAttack;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.SAttack = value;
+ });
+ }
+ else
+ {
+ this._owner.SAttack = value;
+ }
+ }
+ }
+ }
+
+ public float SDecay
+ {
+ get
+ {
+ return this._owner.SDecay;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.SDecay = value;
+ });
+ }
+ else
+ {
+ this._owner.SDecay = value;
+ }
+ }
+ }
+ }
+
+ public int Zoom
+ {
+ get
+ {
+ return this._owner.Zoom;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.Zoom = value;
+ });
+ }
+ else
+ {
+ this._owner.Zoom = value;
+ }
+ }
+ }
+ }
+
+ public bool UseTimeMarkers
+ {
+ get
+ {
+ return this._owner.UseTimeMarkers;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.UseTimeMarkers = value;
+ });
+ }
+ else
+ {
+ this._owner.UseTimeMarkers = value;
+ }
+ }
+ }
+ }
+
+ public string RdsProgramService
+ {
+ get
+ {
+ return this._owner.RdsProgramService;
+ }
+ }
+
+ public string RdsRadioText
+ {
+ get
+ {
+ return this._owner.RdsRadioText;
+ }
+ }
+
+ public bool RdsUseFEC
+ {
+ get
+ {
+ return this._owner.RdsUseFEC;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ this._owner.RdsUseFEC = value;
+ }
+ }
+ }
+
+ public bool IsSquelchOpen
+ {
+ get
+ {
+ return this._owner.IsSquelchOpen;
+ }
+ }
+
+ public int RFBandwidth
+ {
+ get
+ {
+ return this._owner.RFBandwidth;
+ }
+ }
+
+ public int RFDisplayBandwidth
+ {
+ get
+ {
+ return this._owner.RFDisplayBandwidth;
+ }
+ }
+
+ public int FFTResolution
+ {
+ get
+ {
+ return this._owner.FFTResolution;
+ }
+ }
+
+ public float FFTRange
+ {
+ get
+ {
+ return this._owner.FFTRange;
+ }
+ }
+
+ public float FFTOffset
+ {
+ get
+ {
+ return this._owner.FFTOffset;
+ }
+ }
+
+ public int FFTContrast
+ {
+ get
+ {
+ return this._owner.FFTContrast;
+ }
+ }
+
+ public ColorBlend Gradient
+ {
+ get
+ {
+ return this._owner.Gradient;
+ }
+ }
+
+ public SpectrumStyle FFTSpectrumStyle
+ {
+ get
+ {
+ return this._owner.FFTSpectrumStyle;
+ }
+ }
+
+ public int StepSize
+ {
+ get
+ {
+ return this._owner.StepSize;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.StepSize = value;
+ });
+ }
+ else
+ {
+ this._owner.StepSize = value;
+ }
+ }
+ }
+ }
+
+ public bool SourceIsWaveFile
+ {
+ get
+ {
+ return this._owner.SourceIsWaveFile;
+ }
+ }
+
+ public string SourceName
+ {
+ get
+ {
+ return this._owner.SourceName;
+ }
+ }
+
+ public Type SourceType
+ {
+ get
+ {
+ return this._owner.SourceType;
+ }
+ }
+
+ public bool SourceIsSoundCard
+ {
+ get
+ {
+ return this._owner.SourceIsSoundCard;
+ }
+ }
+
+ public bool SourceIsTunable
+ {
+ get
+ {
+ return this._owner.SourceIsTunable;
+ }
+ }
+
+ public object Source
+ {
+ get
+ {
+ return this._owner.Source;
+ }
+ }
+
+ public bool BypassDemodulation
+ {
+ get
+ {
+ return this._owner.BypassDemodulation;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.BypassDemodulation = value;
+ });
+ }
+ else
+ {
+ this._owner.BypassDemodulation = value;
+ }
+ }
+ }
+ }
+
+ public int TunableBandwidth
+ {
+ get
+ {
+ return this._owner.TunableBandwidth;
+ }
+ }
+
+ public TuningStyle TuningStyle
+ {
+ get
+ {
+ return this._owner.TuningStyle;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.TuningStyle = value;
+ });
+ }
+ else
+ {
+ this._owner.TuningStyle = value;
+ }
+ }
+ }
+ }
+
+ public int IFOffset
+ {
+ get
+ {
+ return this._owner.IFOffset;
+ }
+ }
+
+ public float TuningLimit
+ {
+ get
+ {
+ return this._owner.TuningLimit;
+ }
+ set
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.TuningLimit = value;
+ });
+ }
+ else
+ {
+ this._owner.TuningLimit = value;
+ }
+ }
+ }
+ }
+
+ public float VisualSNR
+ {
+ get
+ {
+ return this._owner.VisualSNR;
+ }
+ }
+
+ public bool TuningStyleFreezed
+ {
+ get
+ {
+ return this._owner.TuningStyleFreezed;
+ }
+ set
+ {
+ this._owner.TuningStyleFreezed = value;
+ }
+ }
+
+ public double AudioSampleRate
+ {
+ get
+ {
+ return this._owner.AudioSampleRate;
+ }
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public event CustomPaintEventHandler WaterfallCustomPaint;
+
+ public event CustomPaintEventHandler SpectrumAnalyzerCustomPaint;
+
+ public event CustomPaintEventHandler SpectrumAnalyzerBackgroundCustomPaint;
+
+ public SharpControlProxy(MainForm owner)
+ {
+ this._owner = owner;
+ this._owner.PropertyChanged += this.PropertyChangedEventHandler;
+ this._owner.WaterfallCustomPaint += this.waterfall_CustomPaint;
+ this._owner.SpectrumAnalyzerCustomPaint += this.spectrumAnalyzer_CustomPaint;
+ this._owner.SpectrumAnalyzerBackgroundCustomPaint += this.spectrumAnalyzer_BackgroundCustomPaint;
+ }
+
+ public void SetFrequency(long frequency, bool onlyMoveCenterFrequency)
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.SetFrequency(frequency, onlyMoveCenterFrequency);
+ });
+ }
+ else
+ {
+ this._owner.SetFrequency(frequency, onlyMoveCenterFrequency);
+ }
+ }
+ }
+
+ public void ResetFrequency(long frequency, long centerFrequency)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.ResetFrequency(frequency, centerFrequency);
+ });
+ }
+ else
+ {
+ this._owner.ResetFrequency(frequency, centerFrequency);
+ }
+ }
+
+ public void ResetFrequency(long frequency)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.ResetFrequency(frequency);
+ });
+ }
+ else
+ {
+ this._owner.ResetFrequency(frequency);
+ }
+ }
+
+ public void GetSpectrumSnapshot(byte[] destArray)
+ {
+ this._owner.GetSpectrumSnapshot(destArray);
+ }
+
+ public void GetSpectrumSnapshot(float[] destArray, float scale = 1f, float offset = 0f)
+ {
+ this._owner.GetSpectrumSnapshot(destArray, scale, offset);
+ }
+
+ public void StartRadio()
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.StartRadio();
+ });
+ }
+ else
+ {
+ this._owner.StartRadio();
+ }
+ }
+ }
+
+ public void StopRadio()
+ {
+ if (this.Enabled)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.BeginInvoke((MethodInvoker)delegate
+ {
+ this._owner.StopRadio();
+ });
+ }
+ else
+ {
+ this._owner.StopRadio();
+ }
+ }
+ }
+
+ public void RegisterStreamHook(object streamHook, ProcessorType processorType)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.RegisterStreamHook(streamHook, processorType);
+ });
+ }
+ else
+ {
+ this._owner.RegisterStreamHook(streamHook, processorType);
+ }
+ }
+
+ public void UnregisterStreamHook(object streamHook)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.UnregisterStreamHook(streamHook);
+ });
+ }
+ else
+ {
+ this._owner.UnregisterStreamHook(streamHook);
+ }
+ }
+
+ public void RegisterFrontControl(UserControl control, PluginPosition position)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.RegisterFrontControl(control, position);
+ });
+ }
+ else
+ {
+ this._owner.RegisterFrontControl(control, position);
+ }
+ }
+
+ public void Perform()
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.Perform();
+ });
+ }
+ else
+ {
+ this._owner.Perform();
+ }
+ }
+
+ public void RefreshSource(bool reload)
+ {
+ if (this._owner.InvokeRequired)
+ {
+ this._owner.Invoke((MethodInvoker)delegate
+ {
+ this._owner.RefreshSource(reload);
+ });
+ }
+ else
+ {
+ this._owner.RefreshSource(reload);
+ }
+ }
+
+ private void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e)
+ {
+ PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
+ if (propertyChanged != null)
+ {
+ propertyChanged(sender, new PropertyChangedEventArgs(e.PropertyName));
+ }
+ }
+
+ private void spectrumAnalyzer_CustomPaint(object sender, CustomPaintEventArgs e)
+ {
+ CustomPaintEventHandler spectrumAnalyzerCustomPaint = this.SpectrumAnalyzerCustomPaint;
+ if (spectrumAnalyzerCustomPaint != null)
+ {
+ spectrumAnalyzerCustomPaint(sender, e);
+ }
+ }
+
+ private void spectrumAnalyzer_BackgroundCustomPaint(object sender, CustomPaintEventArgs e)
+ {
+ CustomPaintEventHandler spectrumAnalyzerBackgroundCustomPaint = this.SpectrumAnalyzerBackgroundCustomPaint;
+ if (spectrumAnalyzerBackgroundCustomPaint != null)
+ {
+ spectrumAnalyzerBackgroundCustomPaint(sender, e);
+ }
+ }
+
+ private void waterfall_CustomPaint(object sender, CustomPaintEventArgs e)
+ {
+ CustomPaintEventHandler waterfallCustomPaint = this.WaterfallCustomPaint;
+ if (waterfallCustomPaint != null)
+ {
+ waterfallCustomPaint(sender, e);
+ }
+ }
+ }
+}
diff --git a/SDRSharp/SDRSharp/app.manifest b/SDRSharp/SDRSharp/app.manifest
new file mode 100644
index 0000000..f8f55e0
--- /dev/null
+++ b/SDRSharp/SDRSharp/app.manifest
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SDRSharp/refs Panel Common FreqEdit PanView Radio & airspy.dll airspyhf.dll .txt b/SDRSharp/refs Panel Common FreqEdit PanView Radio & airspy.dll airspyhf.dll .txt
new file mode 100644
index 0000000..e69de29
diff --git a/SDRSharp/ver 1.0.0.1632.txt b/SDRSharp/ver 1.0.0.1632.txt
new file mode 100644
index 0000000..e69de29