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.Numerics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
using Echo.Core.Common.Packed;
using ImGuiNET;
using SDL2;
using SDL2Demo.SdlWrapper;
using testdrid.SdlWrapper; using testdrid.SdlWrapper;
namespace Echo.UserInterface.Backend; namespace Echo.UserInterface.Backend;
@ -20,7 +21,8 @@ using static SDL;
/// </summary> /// </summary>
public sealed unsafe class ImGuiDevice : IDisposable, IEventConsumer 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.window = window;
this.renderer = renderer; this.renderer = renderer;
@ -483,6 +485,7 @@ public sealed unsafe class ImGuiDevice : IDisposable, IEventConsumer
else _ = SDL_ShowCursor((int)SDL_bool.SDL_FALSE); else _ = SDL_ShowCursor((int)SDL_bool.SDL_FALSE);
} }
private uint numErrors;
void ExecuteCommandList(ImDrawListPtr list, in Float4 clipOffset, in Float4 clipSize) void ExecuteCommandList(ImDrawListPtr list, in Float4 clipOffset, in Float4 clipSize)
{ {
ImPtrVector<ImDrawCmdPtr> buffer = list.CmdBuffer; ImPtrVector<ImDrawCmdPtr> buffer = list.CmdBuffer;
@ -523,7 +526,10 @@ public sealed unsafe class ImGuiDevice : IDisposable, IEventConsumer
(int)command.ElemCount, sizeof(ushort) (int)command.ElemCount, sizeof(ushort)
) != 0) ) != 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.Text("Progress: ");
ImGui.SameLine(); ImGui.SameLine();
ImGui.SetNextItemWidth(100); 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("Estimated Time Remaining: {0}", remainTime.ToString()));
ImGui.Text(String.Format("Starting Frequency: {0} kHz", Start)); 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 Echo.UserInterface.Backend;
using ImGuiNET; using ImGuiNET;
using SDL2; using SDL2;
using skyscraper8.Skyscraper.Drawing;
using testdrid.SdlWrapper; using testdrid.SdlWrapper;
namespace SDL2Demo.Forms namespace SDL2Demo.Forms
{ {
internal class IqWindow : IRenderable internal class IqWindow : IRenderable, IDisposable
{ {
private readonly Renderer _renderer; private readonly Renderer _renderer;
private readonly Texture _texture; private readonly Texture _texture;
private readonly bool imgui; private readonly bool imgui;
private readonly Vector2 _imguiVector; private readonly Vector2 _imguiVector;
private bool noDraw; private readonly IqChartData _iqPlot;
public void ResetIqGraphic()
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; _renderer = renderer;
_texture = Texture.Create(_renderer, SDL.SDL_TextureAccess.SDL_TEXTUREACCESS_STREAMING, 256, 256); _texture = Texture.Create(_renderer, SDL.SDL_TextureAccess.SDL_TEXTUREACCESS_STREAMING, 256, 256);
_texture.SetDrawBlendMode(SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND); _texture.SetDrawBlendMode(SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND);
_iqPlot = iqPlot;
imgui = false; imgui = false;
} }
public IqWindow(ImGuiDevice imGuiDevice) public IqWindow(ImGuiDevice imGuiDevice, IqChartData iqPlot)
{ {
ResetIqGraphic();
IntPtr textureIntPtr = imGuiDevice.CreateTexture(new Int2(256, 256), true, !BitConverter.IsLittleEndian); IntPtr textureIntPtr = imGuiDevice.CreateTexture(new Int2(256, 256), true, !BitConverter.IsLittleEndian);
_texture = Texture.FromIntPointer(textureIntPtr); _texture = Texture.FromIntPointer(textureIntPtr);
_renderer = null; _renderer = null;
_imguiVector = new Vector2(256, 256); _imguiVector = new Vector2(256, 256);
imgui = true; _iqPlot = iqPlot;
imgui = true;
} }
private byte[][] iqData;
private ulong iqErrors;
public Point RenderOffset { get; set; } 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() private void RenderInternal()
{ {
if (noDraw) byte b = 0;
return; byte[][] iq = _iqPlot.IQ;
byte b = 0; for (int y = 0; y < 256; y++)
try {
{
for (int y = 0; y < iqData.Length; y++) for (int x = 0; x < 256; x++)
{ {
if (noDraw)
return; b = iq[x][y];
for (int x = 0; x < iqData[y].Length; x++) if (b != 0)
{ _texture.SetPixel(x, y, 255, (byte)(255 - b), 0, 0);
if (noDraw) else
return; _texture.SetPixel(x, y, 0, 0, 0, 0);
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++;
}
} }
public void Render() public void Render()
{ {
_texture.Lock(); lock (_iqPlot)
RenderInternal(); {
_texture.Unlock(); _texture.Lock();
RenderInternal();
_texture.Unlock();
if (imgui) if (imgui)
{ {
ImGui.PushStyleVar(ImGuiStyleVar.WindowMinSize, 256); ImGui.PushStyleVar(ImGuiStyleVar.WindowMinSize, new Vector2(272, 256));
ImGui.Begin("IQ", ImGuiWindowFlags.NoResize); ImGui.Begin("IQ", ImGuiWindowFlags.NoResize);
ImGui.Image(_texture.Pointer, _imguiVector); ImGui.ProgressBar(_iqPlot.ProgressFraction, Vector2.Zero, String.Format("{0}% (Z={1})", _iqPlot.ProgressHumanReadable, _iqPlot.ZAxis));
ImGui.End(); ImGui.Image(_texture.Pointer, _imguiVector);
} ImGui.End();
else ImGui.PopStyleVar();
{ }
_renderer.Copy(_texture, RenderOffset); else
} {
_renderer.Copy(_texture, RenderOffset);
}
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] private bool disposing;
internal void PushIqSamples(sbyte[] iqBuffer) public void Dispose()
{ {
byte i, q; lock (_iqPlot)
for (int idx = 0; idx < iqBuffer.Length; idx += 2) {
{ disposing = true;
i = (byte)(int)(iqBuffer[idx + 0] + sbyte.MaxValue); }
q = (byte)(int)(iqBuffer[idx + 1] + sbyte.MaxValue);
PushIqSample(i, q); _texture.Dispose();
}
} }
} }
} }

View File

@ -353,6 +353,27 @@ namespace SDL2Demo.Jobs
public SearchResult2 sr2; public SearchResult2 sr2;
public bool Satellite { get; private set; } 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() public bool RunBlindscan()

View File

@ -1,27 +1,30 @@
using skyscraper5.Skyscraper.Equipment; using ImGuiNET;
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 SDL2Demo.Forms; using SDL2Demo.Forms;
using skyscraper5.Skyscraper.IO;
using static SDL2Demo.Jobs.Blindscan;
using System.Drawing;
using System.Runtime.InteropServices;
using SDL2Demo.SdlWrapper; 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.Skyscraper.Scraper;
using skyscraper5.src.Mpeg2.PacketFilter; 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 namespace SDL2Demo.Jobs
{ {
class CoopBlindscan : IRenderable, IJob class CoopBlindscan : IRenderable, IJob
{ {
private static PluginLogger logger = PluginLogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private IDbBlindscanJobStorage jobStorage; private IDbBlindscanJobStorage jobStorage;
private List<TunerMetadata> tunerMetadataList; private List<TunerMetadata> tunerMetadataList;
private JobContext jobContext; private JobContext jobContext;
@ -67,13 +70,15 @@ namespace SDL2Demo.Jobs
private bool settingsWindowCaptureFile; private bool settingsWindowCaptureFile;
private int settingsWindowDiseqc; private int settingsWindowDiseqc;
private int settingsWindowSatellite; private int settingsWindowSatellite;
private bool settingsWindowCollectIqGraphs;
public void Render() public void Render()
{ {
if (ImGui.Begin("Blindscan", ref settingsWindowOpen, ImGuiWindowFlags.AlwaysAutoResize)) if (ImGui.Begin("Blindscan", ref settingsWindowOpen, ImGuiWindowFlags.AlwaysAutoResize))
{ {
if (tunerMetadataList.Count < 2) 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(); ImGui.End();
return; return;
} }
@ -130,6 +135,7 @@ namespace SDL2Demo.Jobs
ImGui.Checkbox("Scan High Horizontal Region", ref settingsWindowScanHorizontalHigh); ImGui.Checkbox("Scan High Horizontal Region", ref settingsWindowScanHorizontalHigh);
ImGui.Checkbox("Scan Low Vertical Region", ref settingsWindowScanVerticalLow); ImGui.Checkbox("Scan Low Vertical Region", ref settingsWindowScanVerticalLow);
ImGui.Checkbox("Scan High Vertical Region", ref settingsWindowScanVerticalHigh); ImGui.Checkbox("Scan High Vertical Region", ref settingsWindowScanVerticalHigh);
ImGui.Checkbox("Collect IQ Graphs", ref settingsWindowCollectIqGraphs);
ImGui.PushID("diseqc"); ImGui.PushID("diseqc");
ImGui.Text("DiSEqC"); ImGui.Text("DiSEqC");
@ -682,6 +688,50 @@ namespace SDL2Demo.Jobs
JobContext.Puppets[2].AutoMoveToHome(); JobContext.Puppets[2].AutoMoveToHome();
JobContext.Puppets[3].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) private void RunSkyscraper(BlindscanResult result)
{ {
@ -789,6 +839,18 @@ namespace SDL2Demo.Jobs
throw new NotImplementedException("Couldn't figure out what signal info to use."); 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++) for (int mis = 0; mis < misCounter; mis++)
{ {
if (misMode) if (misMode)

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading.Tasks; 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) public void SetPixel(int x, int y, byte a, byte r, byte g, byte b)
{ {
if (y < 0) 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.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using skyscraper5.Skyscraper.IO;
namespace skyscraper8.Skyscraper.Drawing namespace skyscraper8.Skyscraper.Drawing
{ {
public class IqChartData public class IqChartData
{ {
public const int DESIRABLE_AMOUNT_OF_POINTS = 100000; public const int DESIRABLE_AMOUNT_OF_POINTS = 100000;
private static PluginLogger logger = PluginLogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private IqChartData() private IqChartData()
{ {
IQ = new byte[256][]; IQ = new byte[256][];
@ -74,7 +79,7 @@ namespace skyscraper8.Skyscraper.Drawing
if (IQ[i][q] > progress) if (IQ[i][q] > progress)
{ {
progress = IQ[i][q]; 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; 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, ModFailure = 106,
PlsFailure = 107, PlsFailure = 107,
BlScanFailure = 108, BlScanFailure = 108,
DataSaving = 4 DataSaving = 4,
IqCollecting = 5,
IqSaving = 6
} }
} }