Proof-of-concept of IQ gathering.

This commit is contained in:
feyris-tan 2025-07-28 23:08:43 +02:00
parent c7188b56c5
commit c618e87a4a
8 changed files with 221 additions and 105 deletions

View File

@ -1,12 +1,13 @@
using System;
using Echo.Core.Common.Packed;
using ImGuiNET;
using SDL2;
using SDL2Demo.SdlWrapper;
using skyscraper8.Skyscraper.Plugins;
using System;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using Echo.Core.Common.Packed;
using ImGuiNET;
using SDL2;
using SDL2Demo.SdlWrapper;
using testdrid.SdlWrapper;
namespace Echo.UserInterface.Backend;
@ -20,7 +21,8 @@ using static SDL;
/// </summary>
public sealed unsafe class ImGuiDevice : IDisposable, IEventConsumer
{
public ImGuiDevice(IntPtr window, IntPtr renderer)
private static PluginLogger logger = PluginLogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public ImGuiDevice(IntPtr window, IntPtr renderer)
{
this.window = window;
this.renderer = renderer;
@ -483,6 +485,7 @@ public sealed unsafe class ImGuiDevice : IDisposable, IEventConsumer
else _ = SDL_ShowCursor((int)SDL_bool.SDL_FALSE);
}
private uint numErrors;
void ExecuteCommandList(ImDrawListPtr list, in Float4 clipOffset, in Float4 clipSize)
{
ImPtrVector<ImDrawCmdPtr> buffer = list.CmdBuffer;
@ -523,7 +526,10 @@ public sealed unsafe class ImGuiDevice : IDisposable, IEventConsumer
(int)command.ElemCount, sizeof(ushort)
) != 0)
{
throw SdlException.GenerateException();
numErrors++;
SdlException sdlException = SdlException.GenerateException();
//We can tolerate the Geometry Renderer going sideways a few times
logger.Log(PluginLogLevel.Error, String.Format("{0}, ({1}x)",sdlException.ToString(),numErrors));
}
}
}

View File

@ -36,7 +36,8 @@ namespace SDL2Demo.Forms
ImGui.Text("Progress: ");
ImGui.SameLine();
ImGui.SetNextItemWidth(100);
ImGui.ProgressBar(Convert.ToSingle(frac) / 100.0f, Vector2.Zero, String.Format("{0:0.##}%", frac));
float progressBarFrac = Convert.ToSingle(frac) / 100.0f;
ImGui.ProgressBar(progressBarFrac, Vector2.Zero, String.Format("{0:0.##}%", frac));
ImGui.Text(String.Format("Estimated Time Remaining: {0}", remainTime.ToString()));
ImGui.Text(String.Format("Starting Frequency: {0} kHz", Start));

View File

@ -10,118 +10,93 @@ using Echo.Core.Common.Packed;
using Echo.UserInterface.Backend;
using ImGuiNET;
using SDL2;
using skyscraper8.Skyscraper.Drawing;
using testdrid.SdlWrapper;
namespace SDL2Demo.Forms
{
internal class IqWindow : IRenderable
internal class IqWindow : IRenderable, IDisposable
{
private readonly Renderer _renderer;
private readonly Texture _texture;
private readonly bool imgui;
private readonly Vector2 _imguiVector;
private bool noDraw;
public void ResetIqGraphic()
private readonly IqChartData _iqPlot;
public IqWindow(Renderer renderer, IqChartData iqPlot)
{
noDraw = true;
iqData = new byte[256][];
for (int i = 0; i < iqData.Length; i++)
{
iqData[i] = new byte[256];
}
noDraw = false;
}
public IqWindow(Renderer renderer)
{
ResetIqGraphic();
_renderer = renderer;
_texture = Texture.Create(_renderer, SDL.SDL_TextureAccess.SDL_TEXTUREACCESS_STREAMING, 256, 256);
_texture.SetDrawBlendMode(SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND);
_iqPlot = iqPlot;
imgui = false;
}
public IqWindow(ImGuiDevice imGuiDevice)
public IqWindow(ImGuiDevice imGuiDevice, IqChartData iqPlot)
{
ResetIqGraphic();
IntPtr textureIntPtr = imGuiDevice.CreateTexture(new Int2(256, 256), true, !BitConverter.IsLittleEndian);
_texture = Texture.FromIntPointer(textureIntPtr);
_renderer = null;
_imguiVector = new Vector2(256, 256);
imgui = true;
_iqPlot = iqPlot;
imgui = true;
}
private byte[][] iqData;
private ulong iqErrors;
public Point RenderOffset { get; set; }
public void PushIqSample(byte i, byte q)
{
if (iqData[i][q] == byte.MaxValue)
return;
iqData[i][q]++;
}
private void RenderInternal()
{
if (noDraw)
return;
byte b = 0;
try
{
for (int y = 0; y < iqData.Length; y++)
{
if (noDraw)
return;
for (int x = 0; x < iqData[y].Length; x++)
{
if (noDraw)
return;
b = iqData[y][x];
if (b != 0)
_texture.SetPixel(x, y, 255, (byte)(255 - b), 0, 0);
else
_texture.SetPixel(x, y, 0, 0, 0, 0);
}
}
}
catch (NullReferenceException e)
{
iqErrors++;
}
byte b = 0;
byte[][] iq = _iqPlot.IQ;
for (int y = 0; y < 256; y++)
{
for (int x = 0; x < 256; x++)
{
b = iq[x][y];
if (b != 0)
_texture.SetPixel(x, y, 255, (byte)(255 - b), 0, 0);
else
_texture.SetPixel(x, y, 0, 0, 0, 0);
}
}
}
public void Render()
{
_texture.Lock();
RenderInternal();
_texture.Unlock();
lock (_iqPlot)
{
_texture.Lock();
RenderInternal();
_texture.Unlock();
if (imgui)
{
ImGui.PushStyleVar(ImGuiStyleVar.WindowMinSize, 256);
ImGui.Begin("IQ", ImGuiWindowFlags.NoResize);
ImGui.Image(_texture.Pointer, _imguiVector);
ImGui.End();
}
else
{
_renderer.Copy(_texture, RenderOffset);
}
if (imgui)
{
ImGui.PushStyleVar(ImGuiStyleVar.WindowMinSize, new Vector2(272, 256));
ImGui.Begin("IQ", ImGuiWindowFlags.NoResize);
ImGui.ProgressBar(_iqPlot.ProgressFraction, Vector2.Zero, String.Format("{0}% (Z={1})", _iqPlot.ProgressHumanReadable, _iqPlot.ZAxis));
ImGui.Image(_texture.Pointer, _imguiVector);
ImGui.End();
ImGui.PopStyleVar();
}
else
{
_renderer.Copy(_texture, RenderOffset);
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void PushIqSamples(sbyte[] iqBuffer)
private bool disposing;
public void Dispose()
{
byte i, q;
for (int idx = 0; idx < iqBuffer.Length; idx += 2)
{
i = (byte)(int)(iqBuffer[idx + 0] + sbyte.MaxValue);
q = (byte)(int)(iqBuffer[idx + 1] + sbyte.MaxValue);
PushIqSample(i, q);
}
lock (_iqPlot)
{
disposing = true;
}
_texture.Dispose();
}
}
}

View File

@ -353,6 +353,27 @@ namespace SDL2Demo.Jobs
public SearchResult2 sr2;
public bool Satellite { get; private set; }
public string FrequencyAndPolarityToString()
{
if (Satellite)
{
char polarity;
switch (sr1.Pol)
{
case 0: polarity = 'H'; break;
case 1: polarity = 'V'; break;
case 2: polarity = 'L'; break;
case 3: polarity = 'R'; break;
default: polarity = 'W'; break; //W for WTF?
}
return String.Format("{0}_{1}", sr1.Freq, polarity);
}
else
{
return String.Format("{0}", sr2.Freq);
}
}
}
public bool RunBlindscan()

View File

@ -1,27 +1,30 @@
using skyscraper5.Skyscraper.Equipment;
using skyscraper5.Skyscraper;
using skyscraper5.src.Skyscraper.FrequencyListGenerator;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper.IO.CrazycatStreamReader;
using System.IO;
using ImGuiNET;
using ImGuiNET;
using SDL2Demo.Forms;
using skyscraper5.Skyscraper.IO;
using static SDL2Demo.Jobs.Blindscan;
using System.Drawing;
using System.Runtime.InteropServices;
using SDL2Demo.SdlWrapper;
using skyscraper5.Skyscraper;
using skyscraper5.Skyscraper.Equipment;
using skyscraper5.Skyscraper.IO;
using skyscraper5.Skyscraper.IO.CrazycatStreamReader;
using skyscraper5.Skyscraper.Scraper;
using skyscraper5.src.Mpeg2.PacketFilter;
using skyscraper5.src.Skyscraper.FrequencyListGenerator;
using skyscraper8.Skyscraper.Plugins;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using skyscraper8.Skyscraper.Drawing;
using static SDL2Demo.Jobs.Blindscan;
namespace SDL2Demo.Jobs
{
class CoopBlindscan : IRenderable, IJob
{
private static PluginLogger logger = PluginLogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private IDbBlindscanJobStorage jobStorage;
private List<TunerMetadata> tunerMetadataList;
private JobContext jobContext;
@ -67,13 +70,15 @@ namespace SDL2Demo.Jobs
private bool settingsWindowCaptureFile;
private int settingsWindowDiseqc;
private int settingsWindowSatellite;
private bool settingsWindowCollectIqGraphs;
public void Render()
{
if (ImGui.Begin("Blindscan", ref settingsWindowOpen, ImGuiWindowFlags.AlwaysAutoResize))
{
if (tunerMetadataList.Count < 2)
{
ImGui.Text("You need at least to DVB-S Tuners for the cooperative blindscan.");
ImGui.Text("You need at least two DVB-S Tuners for the cooperative blindscan.");
ImGui.End();
return;
}
@ -130,6 +135,7 @@ namespace SDL2Demo.Jobs
ImGui.Checkbox("Scan High Horizontal Region", ref settingsWindowScanHorizontalHigh);
ImGui.Checkbox("Scan Low Vertical Region", ref settingsWindowScanVerticalLow);
ImGui.Checkbox("Scan High Vertical Region", ref settingsWindowScanVerticalHigh);
ImGui.Checkbox("Collect IQ Graphs", ref settingsWindowCollectIqGraphs);
ImGui.PushID("diseqc");
ImGui.Text("DiSEqC");
@ -682,6 +688,50 @@ namespace SDL2Demo.Jobs
JobContext.Puppets[2].AutoMoveToHome();
JobContext.Puppets[3].AutoMoveToHome();
}
private IqChartData GatherIQGraphCable()
{
throw new NotImplementedException();
}
private IqChartData GatherIqGraph()
{
Caps caps = streamReader.GetCaps();
if (caps.HasFlag(Caps.SR_IQSCAN2))
{
return GatherIQGraphCable();
}
else if (!caps.HasFlag(Caps.SR_IQSCAN))
{
logger.Log(PluginLogLevel.Error, "Couldn't figure out whether to use IQScan or IQScan2!");
return null;
}
IqChartData result = IqChartData.Create();
IqWindow iqWindow = new IqWindow(JobContext.ImgUiDevice, result);
JobContext.Renderables.Add(iqWindow);
sbyte[] buffer = new sbyte[200];
while (!result.IsComplete)
{
bool iqScan = streamReader.IQScan(0, buffer, 100);
if (!iqScan)
{
result = null;
logger.Log(PluginLogLevel.Error, "IQScan failed");
break;
}
result.PushPacket(buffer);
}
lock (jobContext.Renderables)
{
JobContext.Renderables.Remove(iqWindow);
iqWindow.Dispose();
iqWindow = null;
}
return result;
}
private void RunSkyscraper(BlindscanResult result)
{
@ -789,6 +839,18 @@ namespace SDL2Demo.Jobs
throw new NotImplementedException("Couldn't figure out what signal info to use.");
}
if (settingsWindowCollectIqGraphs)
{
result.State = BlindscanResultState.IqCollecting;
IqChartData plot = GatherIqGraph();
result.State = BlindscanResultState.IqSaving;
string fname = String.Format("{0}_{1}.bin", jobInDb.JobGuid.ToString("D"), result.FrequencyAndPolarityToString());
FileStream fileStream = File.OpenWrite(fname);
plot.SaveTo(fileStream);
fileStream.Flush();
fileStream.Close();
}
for (int mis = 0; mis < misCounter; mis++)
{
if (misMode)

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
@ -87,6 +88,27 @@ namespace testdrid.SdlWrapper
}
}
public void Fill(byte a, byte r, byte g, byte b)
{
SDL.SDL_Rect rekt = new SDL.SDL_Rect();
rekt.h = GetHeight();
rekt.w = GetWidth();
rekt.x = 0;
rekt.y = 0;
uint color = (uint)(a << 24);
color += (uint)(r << 16);
color += (uint)(g << 8);
color += b;
int sdlFillRect = SDL.SDL_FillRect(Pointer, ref rekt, color);
if (sdlFillRect != 0)
{
throw SdlException.GenerateException();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetPixel(int x, int y, byte a, byte r, byte g, byte b)
{
if (y < 0)

View File

@ -1,16 +1,21 @@
using System;
using skyscraper5.Skyscraper.IO;
using skyscraper8.Skyscraper.Plugins;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper.IO;
namespace skyscraper8.Skyscraper.Drawing
{
public class IqChartData
{
public const int DESIRABLE_AMOUNT_OF_POINTS = 100000;
private static PluginLogger logger = PluginLogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private IqChartData()
{
IQ = new byte[256][];
@ -74,7 +79,7 @@ namespace skyscraper8.Skyscraper.Drawing
if (IQ[i][q] > progress)
{
progress = IQ[i][q];
Console.WriteLine(progress);
logger.Log(PluginLogLevel.Debug, "IQ Z-Axis now at {0}", progress);
}
}
}
@ -120,5 +125,27 @@ namespace skyscraper8.Skyscraper.Drawing
scaled = true;
}
public float ProgressFraction
{
get
{
float l = totalSamples;
float r = DESIRABLE_AMOUNT_OF_POINTS;
float x = l / r;
return x;
}
}
public double ProgressHumanReadable
{
get
{
double frac = ((double)(totalSamples) * 100.0) / (double)(DESIRABLE_AMOUNT_OF_POINTS);
return frac;
}
}
public byte ZAxis => progress;
}
}

View File

@ -25,6 +25,8 @@ namespace skyscraper5.src.Skyscraper.FrequencyListGenerator
ModFailure = 106,
PlsFailure = 107,
BlScanFailure = 108,
DataSaving = 4
DataSaving = 4,
IqCollecting = 5,
IqSaving = 6
}
}