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