2025-10-04 20:51:01 +02:00

1217 lines
41 KiB
C#

using ImGuiNET;
using SDL2Demo.Forms;
using SDL2Demo.SdlWrapper;
using skyscraper5.Dvb.Descriptors;
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.Drawing;
using skyscraper8.Skyscraper.Plugins;
using skyscraper8.UI.ImGui.Forms;
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 testdrid.SdlWrapper;
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;
private List<SatellitePosition> satellites;
private List<LnbType> lnbs;
public CoopBlindscan(List<TunerMetadata> tuners, List<SatellitePosition> satellitePositions, List<LnbType> lnbTypes, IDbBlindscanJobStorage jobStorage)
{
this.jobStorage = jobStorage;
this.tunerMetadataList = tuners.Where(x => x.Type == STD_TYPE.STD_DVBS || x.Type == STD_TYPE.STD_DVBS2).ToList();
this.settingsWindowOpen = true;
this.satellites = satellitePositions;
this.lnbs = lnbTypes;
this.lastTuner = -2;
this.somethingStarted = false;
this.settingsWindowScanHorizontalLow = true;
this.settingsWindowScanHorizontalHigh = true;
this.settingsWindowScanVerticalLow = true;
this.settingsWindowScanVerticalHigh = true;
this.settingsWindowDiseqc = 1;
if (tuners.Count > 2)
{
this.settingsWindowTunerAselection = 0;
this.settingsWindowTunerBselection = 1;
}
}
public void Cancel()
{
throw new NotImplementedException();
}
#region Configuration Window
public JobContext JobContext { get { return jobContext; } set { jobContext = value; } }
public bool WindowOpen => settingsWindowOpen;
public CoopBlindscanConfiguration Configuration { get; private set; }
private bool settingsWindowOpen;
private int settingsWindowTunerAselection;
private int settingsWindowTunerBselection;
private bool settingsWindowScanHorizontalLow, settingsWindowScanHorizontalHigh, settingsWindowScanVerticalLow, settingsWindowScanVerticalHigh;
private bool settingsWindowCaptureFile;
private int settingsWindowDiseqc;
private int settingsWindowSatellite;
private bool settingsWindowCollectIqGraphs;
private bool settingsWindowCollectRfSpectrum;
public void Render()
{
if (ImGui.Begin("Blindscan", ref settingsWindowOpen, ImGuiWindowFlags.AlwaysAutoResize))
{
if (tunerMetadataList.Count < 2)
{
ImGui.Text("You need at least two DVB-S Tuners for the cooperative blindscan.");
ImGui.End();
return;
}
ImGui.Text("This assumes that both tuners are connected to the same antenna set-up, using e.g. a splitter.");
ImGui.PushID("tunerA");
if (ImGui.BeginCombo("Tuner for BLScanEx", tunerMetadataList[settingsWindowTunerAselection].Name))
{
for (int i = 0; i < tunerMetadataList.Count; i++)
{
bool isSelected = settingsWindowTunerAselection == i;
if (ImGui.Selectable(tunerMetadataList[i].Name,isSelected))
{
settingsWindowTunerAselection = i;
}
}
ImGui.EndCombo();
}
ImGui.PopID();
ImGui.PushID("tunerB");
if (ImGui.BeginCombo("Tuner for SetFilter", tunerMetadataList[settingsWindowTunerBselection].Name))
{
for (int i = 0; i < tunerMetadataList.Count; i++)
{
bool isSelected = settingsWindowTunerBselection == i;
if (ImGui.Selectable(tunerMetadataList[i].Name, isSelected))
{
settingsWindowTunerBselection = i;
}
}
ImGui.EndCombo();
}
ImGui.PopID();
ImGui.PushID("satellite");
if (ImGui.BeginCombo("Target Satellite", satellites[settingsWindowSatellite].name))
{
for (int i = 0; i < satellites.Count; i++)
{
bool isSelected = settingsWindowSatellite == i;
if (ImGui.Selectable(satellites[i].name,isSelected))
{
settingsWindowSatellite = i;
}
}
ImGui.EndCombo();
}
ImGui.PopID();
ImGui.Checkbox("Capture Packets to files", ref settingsWindowCaptureFile);
ImGui.Checkbox("Scan Low Horizontal Region", ref settingsWindowScanHorizontalLow);
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.Checkbox("Collect RF Spectrum", ref settingsWindowCollectRfSpectrum);
ImGui.PushID("diseqc");
ImGui.Text("DiSEqC");
ImGui.SameLine();
ImGui.RadioButton("A", ref settingsWindowDiseqc, 1);
ImGui.SameLine();
ImGui.RadioButton("B", ref settingsWindowDiseqc, 2);
ImGui.SameLine();
ImGui.RadioButton("C", ref settingsWindowDiseqc, 3);
ImGui.SameLine();
ImGui.RadioButton("D", ref settingsWindowDiseqc, 4);
ImGui.PopID();
bool sameTunerSelected = settingsWindowTunerAselection == settingsWindowTunerBselection;
if (sameTunerSelected)
{
ImGui.Text("Please select different tuners for the operations.");
ImGui.BeginDisabled();
}
if (ImGui.Button("Start"))
{
settingsWindowOpen = false;
int lnbIndex = tunerMetadataList[settingsWindowTunerAselection].Lnbs[settingsWindowDiseqc - 1];
LnbType lnb = lnbs.Find(x => x.Id == lnbIndex);
Configuration = new CoopBlindscanConfiguration(tunerMetadataList[settingsWindowTunerAselection], tunerMetadataList[settingsWindowTunerBselection], settingsWindowScanHorizontalLow, settingsWindowScanHorizontalHigh, settingsWindowScanVerticalLow, settingsWindowScanVerticalHigh, settingsWindowDiseqc, settingsWindowSatellite, SatellitePosition.FromChecksum(satellites[settingsWindowSatellite].Checksum), lnb, settingsWindowCaptureFile);
}
ImGui.SameLine();
if (sameTunerSelected)
ImGui.EndDisabled();
if (ImGui.Button("Cancel"))
{
settingsWindowOpen = false;
}
ImGui.End();
}
}
public class CoopBlindscanConfiguration
{
public CoopBlindscanConfiguration(TunerMetadata tunerA, TunerMetadata tunerB, bool doHorizontalLow, bool doHorizontalHigh, bool doVerticalLow, bool doVerticalHigh, int diseqc, int satelliteId, SatellitePosition direction, LnbType lnbType, bool recordingEnabled)
{
TunerA = tunerA;
TunerB = tunerB;
DoHorizontalLow = doHorizontalLow;
DoHorizontalHigh = doHorizontalHigh;
DoVerticalLow = doVerticalLow;
DoVerticalHigh = doVerticalHigh;
Diseqc = diseqc;
SatelliteId = satelliteId;
Direction = direction;
LnbType = lnbType;
RecordingEnabled = recordingEnabled;
}
public TunerMetadata TunerA { get; }
public TunerMetadata TunerB { get; }
public bool DoHorizontalLow { get; }
public bool DoHorizontalHigh { get; }
public bool DoVerticalLow { get; }
public bool DoVerticalHigh { get; }
public int Diseqc { get; }
public int SatelliteId { get; }
public SatellitePosition Direction { get; }
public LnbType LnbType { get; }
public bool RecordingEnabled { get; }
}
#endregion
#region Blindscanning Driver
private IStreamReader streamReader;
private DbBlindscanJob jobInDb;
private List<BlindscanResult> foundFrequencies;
private FoundFrequenciesWindow ourFoundFrequenciesWindow;
private BlindscanProgressWindow _blindscanProgressWindow;
private string recordingFilename;
private int packetsToDrop;
private Queue<byte[]> packetsQueue;
private JobDisplay jobDisplay;
private ISkyscraperContext skyscraperContext;
private DateTime startedAt;
private ulong packetsReceivedInTotal;
private ulong droppedPackets;
public void Run()
{
if (JobContext == null)
throw new NullReferenceException("Job Context was not enrolled.");
if (JobContext.StreamReader == null)
throw new NullReferenceException("No StreamReader available");
if (streamReader == null)
streamReader = JobContext.StreamReader;
jobInDb = new DbBlindscanJob(Guid.NewGuid(), Configuration.TunerA.MacAddress, Configuration.TunerA.Type, Configuration.SatelliteId, JobContext.Gps, Configuration.Direction);
jobInDb.HorizontalHighState = Configuration.DoHorizontalHigh ? DbBlindscanJobPolarizationStatus.SELECTED_WAITING : DbBlindscanJobPolarizationStatus.NOT_SELECTED;
jobInDb.HorizontalLowState = Configuration.DoHorizontalLow ? DbBlindscanJobPolarizationStatus.SELECTED_WAITING : DbBlindscanJobPolarizationStatus.NOT_SELECTED;
jobInDb.VerticalLowState = Configuration.DoVerticalLow ? DbBlindscanJobPolarizationStatus.SELECTED_WAITING : DbBlindscanJobPolarizationStatus.NOT_SELECTED;
jobInDb.VerticalHighState = Configuration.DoVerticalHigh ? DbBlindscanJobPolarizationStatus.SELECTED_WAITING : DbBlindscanJobPolarizationStatus.NOT_SELECTED;
jobStorage.InsertBlindscanJob(jobInDb);
foundFrequencies = new List<BlindscanResult>();
ourFoundFrequenciesWindow = new FoundFrequenciesWindow(foundFrequencies, Configuration.TunerA.Type, JobContext);
JobContext.Renderables.Add(ourFoundFrequenciesWindow);
if (!RunBlindscan())
{
SetTuner(-1);
JobContext.ReadyForNextJob = true;
return;
}
JobContext.Puppets[0].AutoMoveToHome();
JobContext.Puppets[1].AutoMoveToHome();
JobContext.Puppets[2].AutoMoveTo(new Point(0, 720 / 2));
JobContext.Puppets[3].AutoMoveTo(new Point(1280 / 2, 0));
JobContext.ReadyForNextJob = true;
Console.WriteLine("Blindscan Job done!");
return;
}
public bool RunBlindscan()
{
Caps caps = Configuration.TunerA.Caps;
if (settingsWindowCollectRfSpectrum)
{
SetTuner(1);
RfSpectrumData rfSpectrum = GatherRfSpectrum();
if (rfSpectrum != null)
{
jobContext.ObjectStorage.StoreRfSpectrum(jobInDb.JobGuid, rfSpectrum);
}
}
if (caps.HasFlag(Caps.SR_BLSCAN2))
{
SearchResult sr1 = default;
int tpNum = default;
int lof1 = Configuration.LnbType.Lof1 * 1000;
int lof2 = Configuration.LnbType.Lof2 * 1000;
int lofSw = Configuration.LnbType.LofSw * 1000;
int diseqc = Configuration.TunerA.DiseqcType;
int startFreq = Configuration.LnbType.MinimumFrequency * 1000;
int endFreq = Configuration.LnbType.MaximumFrequency * 1000;
bool anythingSuceeded = false;
IntPtr allocHGlobal = Marshal.AllocHGlobal(UInt16.MaxValue);
if (lofSw != 0)
{
if (Configuration.DoHorizontalLow)
{
SetTuner(1);
if (SendDiseqcCommand(false, true))
{
//JobContext.LogWindow.Log(String.Format("Scanning low horizontal band..."));
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_BLINDSCANNING;
jobStorage.UpdateJobState(jobInDb);
bool hLower = BlScan2Wrap(startFreq, lofSw, 0, lof1, lof2, lofSw, allocHGlobal, ref tpNum, ((ref SearchResult searchResult) => SearchResult1Callback(searchResult, 1)));
JobContext.Puppets[0].AutoMoveToHome();
JobContext.Puppets[1].AutoMoveToHome();
if (!hLower)
{
JobContext.MessageQueue.Enqueue(new MessageWindow("Blindscanning low horizontal area failed."));
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED;
jobStorage.UpdateJobState(jobInDb);
}
else
{
anythingSuceeded = true;
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_SCRAPING;
jobStorage.UpdateJobState(jobInDb);
SetTuner(2);
SendDiseqcCommand(false, true);
RunScrape();
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
jobStorage.UpdateJobState(jobInDb);
foundFrequencies.Clear();
}
}
else
{
JobContext.MessageQueue.Enqueue(new MessageWindow("DiSEqC Switch to low horizontal area failed."));
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED_DISEQC;
jobStorage.UpdateJobState(jobInDb);
}
}
if (Configuration.DoHorizontalHigh)
{
SetTuner(1);
if (SendDiseqcCommand(true, true))
{
//JobContext.LogWindow.Log(String.Format("Scanning high horizontal band..."));
jobInDb.HorizontalHighState = DbBlindscanJobPolarizationStatus.SELECTED_BLINDSCANNING;
jobStorage.UpdateJobState(jobInDb);
bool hUpper = BlScan2Wrap(lofSw, endFreq, 0, lof1, lof2, lofSw, allocHGlobal, ref tpNum, ((ref SearchResult searchResult) => SearchResult1Callback(searchResult, 2)));
JobContext.Puppets[0].AutoMoveToHome();
JobContext.Puppets[1].AutoMoveToHome();
if (!hUpper)
{
JobContext.MessageQueue.Enqueue(new MessageWindow("Blindscanning high horizontal area failed."));
jobInDb.HorizontalHighState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED;
jobStorage.UpdateJobState(jobInDb);
}
else
{
anythingSuceeded = true;
jobInDb.HorizontalHighState = DbBlindscanJobPolarizationStatus.SELECTED_SCRAPING;
jobStorage.UpdateJobState(jobInDb);
SetTuner(2);
SendDiseqcCommand(true, true);
RunScrape();
jobInDb.HorizontalHighState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
jobStorage.UpdateJobState(jobInDb);
foundFrequencies.Clear();
}
}
else
{
JobContext.MessageQueue.Enqueue(new MessageWindow("DiSEqC Switch to high horizontal area failed."));
jobInDb.HorizontalHighState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED_DISEQC;
jobStorage.UpdateJobState(jobInDb);
}
}
if (Configuration.DoVerticalLow)
{
SetTuner(1);
if (SendDiseqcCommand(false, false))
{
//JobContext.LogWindow.Log(String.Format("Scanning low vertical band..."));
jobInDb.VerticalLowState = DbBlindscanJobPolarizationStatus.SELECTED_BLINDSCANNING;
jobStorage.UpdateJobState(jobInDb);
bool vLower = BlScan2Wrap(startFreq, lofSw, 1, lof1, lof2, lofSw, allocHGlobal, ref tpNum, ((ref SearchResult searchResult) => SearchResult1Callback(searchResult, 3)));
JobContext.Puppets[0].AutoMoveToHome();
JobContext.Puppets[1].AutoMoveToHome();
if (!vLower)
{
JobContext.MessageQueue.Enqueue(new MessageWindow("Blindscanning low vertical area failed."));
jobInDb.VerticalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED;
jobStorage.UpdateJobState(jobInDb);
}
else
{
anythingSuceeded = true;
jobInDb.VerticalLowState = DbBlindscanJobPolarizationStatus.SELECTED_SCRAPING;
jobStorage.UpdateJobState(jobInDb);
SetTuner(2);
SendDiseqcCommand(false, false);
RunScrape();
jobInDb.VerticalLowState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
jobStorage.UpdateJobState(jobInDb);
foundFrequencies.Clear();
}
}
else
{
JobContext.MessageQueue.Enqueue(new MessageWindow("DiSEqC Switch to low vertical area failed."));
jobInDb.VerticalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED_DISEQC;
jobStorage.UpdateJobState(jobInDb);
}
}
if (Configuration.DoVerticalHigh)
{
SetTuner(1);
if (SendDiseqcCommand(true, false))
{
//JobContext.LogWindow.Log(String.Format("Scanning high vertical band..."));
jobInDb.VerticalHighState = DbBlindscanJobPolarizationStatus.SELECTED_BLINDSCANNING;
jobStorage.UpdateJobState(jobInDb);
bool vUpper = BlScan2Wrap(lofSw, endFreq, 1, lof1, lof2, lofSw, allocHGlobal, ref tpNum, ((ref SearchResult searchResult) => SearchResult1Callback(searchResult, 4)));
JobContext.Puppets[0].AutoMoveToHome();
JobContext.Puppets[1].AutoMoveToHome();
if (!vUpper)
{
JobContext.MessageQueue.Enqueue(new MessageWindow("Blindscanning high vertical area failed."));
jobInDb.VerticalHighState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED;
jobStorage.UpdateJobState(jobInDb);
}
else
{
anythingSuceeded = true;
jobInDb.VerticalHighState = DbBlindscanJobPolarizationStatus.SELECTED_SCRAPING;
jobStorage.UpdateJobState(jobInDb);
SetTuner(2);
SendDiseqcCommand(true, false);
RunScrape();
jobInDb.VerticalHighState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
jobStorage.UpdateJobState(jobInDb);
foundFrequencies.Clear();
}
}
else
{
JobContext.MessageQueue.Enqueue(new MessageWindow("DiSEqC Switch to high vertical area failed."));
jobInDb.VerticalHighState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED_DISEQC;
jobStorage.UpdateJobState(jobInDb);
}
}
}
else
{
SetTuner(1);
if (SendDiseqcCommand(false, true))
{
//JobContext.LogWindow.Log(String.Format("Scanning left circular band..."));
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_BLINDSCANNING;
jobStorage.UpdateJobState(jobInDb);
bool lCirc = BlScan2Wrap(startFreq, endFreq, 0, lof1, lof2, lofSw, allocHGlobal, ref tpNum, ((ref SearchResult searchResult) => SearchResult1Callback(searchResult, 1)));
JobContext.Puppets[0].AutoMoveToHome();
JobContext.Puppets[1].AutoMoveToHome();
if (!lCirc)
{
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED;
jobStorage.UpdateJobState(jobInDb);
JobContext.MessageQueue.Enqueue(new MessageWindow("Blindscanning left circular area failed."));
}
else
{
anythingSuceeded = true;
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_SCRAPING;
jobStorage.UpdateJobState(jobInDb);
SetTuner(2);
SendDiseqcCommand(false, true);
RunScrape();
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
jobStorage.UpdateJobState(jobInDb);
foundFrequencies.Clear();
}
}
else
{
JobContext.MessageQueue.Enqueue(new MessageWindow("DiSEqC Switch to left circulation failed."));
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED_DISEQC;
jobStorage.UpdateJobState(jobInDb);
}
SetTuner(1);
if (SendDiseqcCommand(false, false))
{
//JobContext.LogWindow.Log(String.Format("Scanning right circular band..."));
jobInDb.VerticalLowState = DbBlindscanJobPolarizationStatus.SELECTED_BLINDSCANNING;
jobStorage.UpdateJobState(jobInDb);
bool rCirc = BlScan2Wrap(startFreq, endFreq, 1, lof1, lof2, lofSw, allocHGlobal, ref tpNum, ((ref SearchResult searchResult) => SearchResult1Callback(searchResult, 3)));
JobContext.Puppets[0].AutoMoveToHome();
JobContext.Puppets[1].AutoMoveToHome();
if (!rCirc)
{
JobContext.MessageQueue.Enqueue(new MessageWindow("Blindscanning right circular area failed."));
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED;
jobStorage.UpdateJobState(jobInDb);
}
else
{
anythingSuceeded = true;
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_SCRAPING;
jobStorage.UpdateJobState(jobInDb);
SetTuner(2);
SendDiseqcCommand(false, false);
RunScrape();
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
jobStorage.UpdateJobState(jobInDb);
foundFrequencies.Clear();
}
}
else
{
JobContext.MessageQueue.Enqueue(new MessageWindow("DiSEqC Switch to right circulation failed."));
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
jobStorage.UpdateJobState(jobInDb);
}
}
Marshal.FreeHGlobal(allocHGlobal);
SetTuner(-1);
return anythingSuceeded;
}
else
{
throw new NotImplementedException("Don't know how to blindscan with this tuner.");
}
}
private bool somethingStarted;
private int lastTuner;
private bool SetTuner(int mode)
{
if (lastTuner == mode)
return true;
if (somethingStarted)
{
streamReader.StopDVB();
somethingStarted = false;
}
bool result;
switch(mode)
{
case 1:
result = streamReader.StartDvbEx(Configuration.TunerA.Index);
break;
case 2:
result = streamReader.StartDvbEx(Configuration.TunerB.Index);
break;
case -1:
result = true;
break;
default:
throw new NotImplementedException(String.Format("Tuner mode {0}", mode));
}
if (mode != -1 && result)
somethingStarted = true;
if (!result)
throw new Exception("Starting tuner failed.");
lastTuner = mode;
return true;
}
private bool BlScan2Wrap(int freq_start, int freq_stop, int pol, int lof1, int lof2, int lofSw, IntPtr pSearchResult, ref int pTpNum, BlScanCallback lpFunc)
{
_blindscanProgressWindow = new BlindscanProgressWindow();
_blindscanProgressWindow.Start = freq_start;
_blindscanProgressWindow.Progress = freq_start;
_blindscanProgressWindow.End = freq_stop;
lock (JobContext.Renderables)
{
JobContext.Renderables.Add(_blindscanProgressWindow);
}
bool result = streamReader.BLScan2(freq_start, freq_stop, pol, lof1, lof2, lofSw, pSearchResult, ref pTpNum, lpFunc);
lock (JobContext.Renderables)
{
JobContext.Renderables.Remove(_blindscanProgressWindow);
}
return result;
}
private bool SendDiseqcCommand( bool highBand, bool horizontal)
{
DiSEqC_Opcode myOpcode = DiSEqC_Opcode.DISEQC_HIGH_NIBBLE;
if (highBand)
myOpcode |= DiSEqC_Opcode.DISEQC_HIGH_BAND;
else
myOpcode |= DiSEqC_Opcode.DISEQC_LOW_BAND;
if (horizontal)
myOpcode |= DiSEqC_Opcode.DISEQC_HORIZONTAL;
else
myOpcode |= DiSEqC_Opcode.DISEQC_VERTICAL;
int parameter = Configuration.Diseqc;
switch (parameter)
{
case 1:
myOpcode |= DiSEqC_Opcode.DISEQC_OPTION_A;
myOpcode |= DiSEqC_Opcode.DISEQC_POSITION_A;
break;
case 2:
myOpcode |= DiSEqC_Opcode.DISEQC_OPTION_A;
myOpcode |= DiSEqC_Opcode.DISEQC_POSITION_B;
break;
case 3:
myOpcode |= DiSEqC_Opcode.DISEQC_OPTION_B;
myOpcode |= DiSEqC_Opcode.DISEQC_POSITION_A;
break;
case 4:
myOpcode |= DiSEqC_Opcode.DISEQC_OPTION_B;
myOpcode |= DiSEqC_Opcode.DISEQC_POSITION_B;
break;
default:
throw new ArgumentOutOfRangeException("DiSEqC Switch Position");
}
//JobContext.LogWindow.Log(String.Format("Send DiSEqC Command {0:X2}", (byte)myOpcode), 8);
DateTime started = DateTime.Now;
bool result = streamReader.SendDiSEqC((uint)2, myOpcode);
TimeSpan timeTaken = DateTime.Now - started;
//JobContext.LogWindow.Log(String.Format("DiSEqC Comannd sent in {0:F1} seconds.", timeTaken.TotalSeconds));
if (timeTaken.TotalSeconds > 10)
{
//JobContext.LogWindow.Log(String.Format("Something went wrong while performing the DiSEqC operation, trying again..."), 8);
Thread.Sleep(1000);
return SendDiseqcCommand(highBand, horizontal);
}
return result;
}
private void SearchResult1Callback(SearchResult searchResult, int polarityIndex)
{
BlindscanResult blindscanResult = new BlindscanResult(searchResult, Configuration.LnbType.MinimumFrequency, Configuration.LnbType.MaximumFrequency);
JobContext.Puppets[blindscanResult.sr1.Pol].AutoMoveTo(blindscanResult.Position);
lock (JobContext.PressurePlates)
{
JobContext.PressurePlates.Add(blindscanResult);
}
lock (foundFrequencies)
{
foundFrequencies.Add(blindscanResult);
}
//JobContext.LogWindow.Log(String.Format("Found frequency: {0}, {1}", searchResult.Freq / 1000, searchResult.Pol == 0 ? "H" : "V"));
SoundPlayer.PlaySoundFile("lock.wav");
_blindscanProgressWindow.Progress = searchResult.Freq;
jobStorage.InsertSearchResult(jobInDb, true, searchResult, polarityIndex, new SearchResult2());
}
public void RunScrape()
{
int lof1 = Configuration.LnbType.Lof1 * 1000;
int lof2 = Configuration.LnbType.Lof2 * 1000;
int lofSw = Configuration.LnbType.LofSw * 1000;
foreach (BlindscanResult blindscanResult in foundFrequencies)
{
recordingFilename = null;
DateTime now = DateTime.Now;
JobContext.Puppets[2 + blindscanResult.sr1.Pol].AutoMoveTo(blindscanResult.Position);
blindscanResult.State = BlindscanResultState.Tuning;
jobStorage.UpdateTransponderState(jobInDb, blindscanResult.Satellite, blindscanResult.sr1, blindscanResult.State, blindscanResult.sr2);
bool channel = streamReader.SetChannel(blindscanResult.sr1.Freq, blindscanResult.sr1.SR,
blindscanResult.sr1.Pol, blindscanResult.sr1.FEC, lof1, lof2, lofSw);
if (!channel)
{
blindscanResult.State = BlindscanResultState.TuningFailed;
jobStorage.UpdateTransponderState(jobInDb, blindscanResult.Satellite, blindscanResult.sr1, blindscanResult.State, blindscanResult.sr2);
continue;
}
recordingFilename = String.Format(
"skyscraper_{0:D4}{1:D2}{2:D2}_{3:D2}{4:D2}_{8:D4}{9}_{5}_{6}_{7}.ts",
now.Year, now.Month, now.Day, now.Hour, now.Minute, blindscanResult.sr1.Freq / 1000,
blindscanResult.sr1.Pol == 0 ? "H" : "V", blindscanResult.sr1.SR / 1000,
(int)(Configuration.Direction.angle * 10),
Configuration.Direction.cardinalDirection == 0 ? "E" : "W");
RunSkyscraper(blindscanResult);
blindscanResult.Visible = false;
}
JobContext.Puppets[0].AutoMoveToHome();
JobContext.Puppets[1].AutoMoveToHome();
JobContext.Puppets[2].AutoMoveToHome();
JobContext.Puppets[3].AutoMoveToHome();
}
private RfSpectrumData GatherRfSpectrum()
{
Caps caps = streamReader.GetCaps();
if (caps.HasFlag(Caps.SR_RFSCAN2))
{
return GatherRfSpectrumCable();
}
else if (!caps.HasFlag(Caps.SR_RFSCAN))
{
logger.Log(PluginLogLevel.Error, "Couldn't figure out whether to use RFScan or RFScan2!");
return null;
}
SdlScottPlotWindowRfSpectrum window = new SdlScottPlotWindowRfSpectrum(jobContext.ImgUiDevice);
jobContext.Renderables.Add(window);
int lof1 = Configuration.LnbType.Lof1 * 1000;
int lof2 = Configuration.LnbType.Lof2 * 1000;
int lofSw = Configuration.LnbType.LofSw * 1000;
int startFreq = Configuration.LnbType.MinimumFrequency * 1000;
int endFreq = Configuration.LnbType.MaximumFrequency * 1000;
RfSpectrumData spectrumData = RfSpectrumData.Create();
void RunRfScan(RfSpectrumData spectrum, int startFreq, int endFreq, SatelliteDeliverySystemDescriptor.PolarizationEnum polarization, DiSEqC_Opcode diseqcCmd, int lof1, int lof2, int lofSw)
{
const int STEP = 1000;
streamReader.SendDiSEqC(2, diseqcCmd);
RfSpectrumDataBlock block = spectrumData.CreateBlock(startFreq, endFreq, STEP, polarization);
for (int i = startFreq; i <= endFreq; i += STEP)
{
double rf = Double.NaN;
streamReader.RFScan(i, diseqcCmd.HasFlag(DiSEqC_Opcode.DISEQC_HORIZONTAL) ? 0 : 1, lof1, lof2, lofSw, out rf);
Console.WriteLine("{0} {1}, {2}", i, polarization.ToString().Substring(0, 1), rf);
block.Push(i, rf);
window.EnqueueSample(polarization, i, rf);
}
}
foreach (DiSEqC_Opcode opcode in GetIqGraphDiseqcOpcodes())
{
RunRfScan(spectrumData, startFreq, endFreq,
opcode.HasFlag(DiSEqC_Opcode.DISEQC_HORIZONTAL) ? SatelliteDeliverySystemDescriptor.PolarizationEnum.HorizontalLinear : SatelliteDeliverySystemDescriptor.PolarizationEnum.VerticalLinear,
opcode, lof1, lof2, lofSw);
}
lock (jobContext.Renderables)
{
JobContext.Renderables.Remove(window);
window.Dispose();
window = null;
}
return spectrumData;
}
private RfSpectrumData GatherRfSpectrumCable()
{
throw new NotImplementedException();
}
private IEnumerable<DiSEqC_Opcode> GetIqGraphDiseqcOpcodes()
{
DiSEqC_Opcode baseDiseqc = DiSEqC_Opcode.DISEQC_HIGH_NIBBLE;
int parameter = Configuration.Diseqc;
switch (parameter)
{
case 1:
baseDiseqc |= DiSEqC_Opcode.DISEQC_OPTION_A;
baseDiseqc |= DiSEqC_Opcode.DISEQC_POSITION_A;
break;
case 2:
baseDiseqc |= DiSEqC_Opcode.DISEQC_OPTION_A;
baseDiseqc |= DiSEqC_Opcode.DISEQC_POSITION_B;
break;
case 3:
baseDiseqc |= DiSEqC_Opcode.DISEQC_OPTION_B;
baseDiseqc |= DiSEqC_Opcode.DISEQC_POSITION_A;
break;
case 4:
baseDiseqc |= DiSEqC_Opcode.DISEQC_OPTION_B;
baseDiseqc |= DiSEqC_Opcode.DISEQC_POSITION_B;
break;
default:
throw new ArgumentOutOfRangeException("DiSEqC Switch Position");
}
if (settingsWindowScanHorizontalLow || settingsWindowScanHorizontalHigh)
yield return baseDiseqc | DiSEqC_Opcode.DISEQC_HORIZONTAL | DiSEqC_Opcode.DISEQC_LOW_BAND;
if (settingsWindowScanVerticalLow || settingsWindowScanVerticalHigh)
yield return baseDiseqc | DiSEqC_Opcode.DISEQC_VERTICAL | DiSEqC_Opcode.DISEQC_LOW_BAND;
yield break;
}
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);
Monitor.Enter(JobContext.Renderables);
JobContext.Renderables.Add(iqWindow);
Monitor.Exit(JobContext.Renderables);
sbyte[] buffer = new sbyte[400];
DateTime started = DateTime.Now;
while (!result.IsComplete)
{
bool iqScan = streamReader.IQScan(0, buffer, (uint)(buffer.Length / 2));
if (!iqScan)
{
result = null;
logger.Log(PluginLogLevel.Error, "IQScan failed");
break;
}
result.PushPacket(buffer);
}
TimeSpan finished = DateTime.Now - started;
logger.Log(PluginLogLevel.Debug, "Finished IQ acquisiton in {0} ({1} samples per call)", finished.ToString(), buffer.Length / 2);
lock (jobContext.Renderables)
{
JobContext.Renderables.Remove(iqWindow);
iqWindow.Dispose();
iqWindow = null;
}
return result;
}
private IqChartData GatherIQGraphCable()
{
throw new NotImplementedException();
}
private void RunSkyscraper(BlindscanResult result)
{
int misCounter = 1;
bool misMode = false;
bool cableTvMode = false;
Caps caps = streamReader.GetCaps();
SearchResult satelliteSr = new SearchResult();
SearchResult2 cableSr = new SearchResult2();
if (caps.HasFlag(Caps.SR_SIGINFO))
{
//JobContext.LogWindow.Log(String.Format("Trying to BLScanEx..."), 8);
if (!streamReader.BLScanEx(result.sr1.Freq, 5000, result.sr1.Pol,
Configuration.LnbType.Lof1 * 1000, Configuration.LnbType.Lof2 * 1000,
Configuration.LnbType.LofSw * 1000, 1000000, (STD_TYPE)result.sr1.StdType,
ref satelliteSr))
{
//No blindscan? No problem! Try anyway!
satelliteSr = result.sr1;
satelliteSr.Lock = false;
result.State = BlindscanResultState.BlScanFailure;
jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
}
if (!satelliteSr.Lock)
{
//JobContext.LogWindow.Log(String.Format("Trying to SetChannel..."), 8);
bool channel = streamReader.SetChannel(result.sr1.Freq, result.sr1.SR, result.sr1.Freq, result.sr1.FEC, Configuration.LnbType.Lof1 * 1000, Configuration.LnbType.Lof2 * 1000, Configuration.LnbType.LofSw * 1000);
if (!channel)
{
result.State = BlindscanResultState.TuningFailed;
SoundPlayer.PlaySoundFile("fail.wav");
jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
return;
}
else
{
//JobContext.LogWindow.Log(String.Format("Trying to get SignalInfo..."), 8);
bool signalInfo = streamReader.SignalInfo(ref satelliteSr);
if (!signalInfo)
{
result.State = BlindscanResultState.TuningFailed;
jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
SoundPlayer.PlaySoundFile("fail.wav");
return;
}
if (!satelliteSr.Lock)
{
result.State = BlindscanResultState.NoLock;
jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
SoundPlayer.PlaySoundFile("fail.wav");
return;
}
}
}
if (caps.HasFlag(Caps.SR_MISSEL))
{
if (satelliteSr.MIS > 16)
satelliteSr.MIS = 1;
if (satelliteSr.MIS == 0)
satelliteSr.MIS = 1;
if (satelliteSr.MIS != 1)
misMode = true;
misCounter = satelliteSr.MIS;
}
}
else if (caps.HasFlag(Caps.SR_SIGINFO2))
{
//JobContext.LogWindow.Log(String.Format("Trying to SetChannel2..."), 8);
bool channel2 = streamReader.SetChannel2((uint)result.sr2.Freq, (uint)result.sr2.BW);
if (!channel2)
{
result.State = BlindscanResultState.TuningFailed;
jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
SoundPlayer.PlaySoundFile("fail.wav");
return;
}
//JobContext.LogWindow.Log(String.Format("Trying to get SignalInfo2..."), 8);
bool signalInfo2 = streamReader.SignalInfo2(ref cableSr);
if (!signalInfo2)
{
result.State = BlindscanResultState.TuningFailed;
jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
SoundPlayer.PlaySoundFile("fail.wav");
return;
}
if (!cableSr.Lock)
{
result.State = BlindscanResultState.NoLock;
jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
SoundPlayer.PlaySoundFile("fail.wav");
return;
}
}
else
{
throw new NotImplementedException("Couldn't figure out what signal info to use.");
}
if (settingsWindowCollectIqGraphs)
{
result.State = BlindscanResultState.IqCollecting;
IqChartData plot = GatherIqGraph();
if (plot != null)
{
result.State = BlindscanResultState.IqSaving;
JobContext.ObjectStorage.StoreIqGraph(jobInDb.JobGuid, result.GetFrequency(), result.GetPolarity(), plot);
}
}
for (int mis = 0; mis < misCounter; mis++)
{
if (misMode)
{
//JobContext.LogWindow.Log(String.Format("Selecting MIS IS {0}", satelliteSr.IS[mis]), 8);
bool misSel = streamReader.MISSel(misMode, satelliteSr.IS[mis], 0xff);
if (!misSel)
{
result.State = BlindscanResultState.MisFailure;
jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
SoundPlayer.PlaySoundFile("fail.wav");
return;
}
}
//Start Filter
TsRecorder tsRecorder = null;
if (Configuration.RecordingEnabled)
{
tsRecorder = new TsRecorder();
tsRecorder.Recording = true;
string outputDirName = JobContext.Ini.ReadValue("recording", "output_dir", "recording_output");
tsRecorder.RecordingOutputDirectory = new DirectoryInfo(outputDirName);
tsRecorder.RecordingOutputDirectory.EnsureExists();
if (tsRecorder.PrepareRecording())
{
tsRecorder.SetNextFilename(recordingFilename);
tsRecorder.CreateBufferedStream();
}
}
IntPtr filterReference = IntPtr.MaxValue;
//JobContext.LogWindow.Log(String.Format("Set-Up filter..."), 8);
packetsToDrop = 1024;
packetsQueue = new Queue<byte[]>();
ourFoundFrequenciesWindow.statusPacketsInqueue = 0;
ourFoundFrequenciesWindow.statusPacketsInTotal = 0;
bool filter = streamReader.SetFilter(8192, DvbCallback, 0x02, 2, ref filterReference);
if (!filter)
{
result.State = BlindscanResultState.FilterFailure;
jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
SoundPlayer.PlaySoundFile("fail.wav");
return;
}
//JobContext.LogWindow.Log(String.Format("Filter set-up complete!"), 8);
//Use the Filter
result.State = BlindscanResultState.Scraping;
jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
jobDisplay = new JobDisplay(JobContext);
lock (JobContext.Renderables)
{
JobContext.Renderables.Add(jobDisplay);
}
SoundPlayer.PlaySoundFile("Success1.wav");
SkipFilter skipper = new SkipFilter(SkipFilter.CRAZYSCAN_BUFFER * 7);
skyscraperContext = SkyscraperContextFactory.CreateSkyscraper(JobContext.DataStorage, JobContext.ObjectStorage);
skyscraperContext.TcpProxyEnabled = true;
skyscraperContext.UiJunction = jobDisplay;
IPacketFilter[] packetFilters = new IPacketFilter[0];
if (tsRecorder != null)
packetFilters = new IPacketFilter[] { tsRecorder, skipper };
skyscraperContext.InitalizeFilterChain(packetFilters);
byte[] singlePacketBuffer = new byte[188];
startedAt = DateTime.MinValue;
packetsReceivedInTotal = 0;
ourFoundFrequenciesWindow.allowZapNow = true;
//The actual scraping happens in this loop
while (!StopConditionMet())
{
if (!HasPackets())
{
Thread.Sleep(100);
continue;
}
byte[] packetBuffer = null;
lock (packetsQueue)
{
packetBuffer = packetsQueue.Dequeue();
}
for (int i = 0; i < packetBuffer.Length; i += 188)
{
Array.Copy(packetBuffer, i, singlePacketBuffer, 0, 188);
skyscraperContext.IngestSinglePacket(singlePacketBuffer);
}
}
ourFoundFrequenciesWindow.allowZapNow = false;
//Stop Filter
//JobContext.LogWindow.Log(String.Format("Deleting Filter..."), 8);
bool stopped = streamReader.DelFilter(filterReference);
if (!stopped)
{
result.State = BlindscanResultState.DelFilterFailed;
jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
return;
}
//JobContext.LogWindow.Log(String.Format("Deleted filter!"), 8);
DrainPackets();
skyscraperContext.Dispose();
if (tsRecorder != null)
tsRecorder.Dispose();
lock (JobContext.Renderables)
{
JobContext.Renderables.Remove(jobDisplay);
}
result.State = BlindscanResultState.DataSaving;
jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
foreach (HumanReadableService humanReadableService in jobDisplay.GetServices())
{
jobStorage.InsertTransponderService(jobInDb, result.Satellite, result.sr1, result.sr2, humanReadableService);
}
jobDisplay = null;
result.State = BlindscanResultState.Done;
jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
}
}
private void DvbCallback(IntPtr data, int length)
{
if (length % 188 == 0)
{
try
{
byte[] buffer = new byte[length];
Marshal.Copy(data, buffer, 0, length);
if (packetsToDrop <= 0)
{
lock (packetsQueue)
{
packetsQueue.Enqueue(buffer);
}
}
packetsToDrop -= (buffer.Length / 188);
packetsReceivedInTotal += (uint)(buffer.Length / 188);
ourFoundFrequenciesWindow.statusPacketsInTotal = packetsReceivedInTotal;
ourFoundFrequenciesWindow.statusPacketsInqueue = packetsQueue.Count;
}
catch (OutOfMemoryException e)
{
droppedPackets++;
}
}
else
{
//JobContext.LogWindow.Log(String.Format("odd packet size!"), 8);
}
}
private bool StopConditionMet()
{
if (startedAt == DateTime.MinValue)
startedAt = DateTime.Now;
if (ourFoundFrequenciesWindow.zapNowRequested)
{
ourFoundFrequenciesWindow.zapNowRequested = false;
return true;
}
if (ourFoundFrequenciesWindow.doNotAutoZap)
return false;
if (packetsReceivedInTotal == 0 && (DateTime.Now - startedAt).TotalSeconds > 5)
return true;
if (jobDisplay != null)
{
if (jobDisplay.GseCommunicationParties > 1000)
return true;
}
if (!skyscraperContext.EnableTimeout)
{
skyscraperContext.TimeoutSeconds = 10;
skyscraperContext.EnableTimeout = true;
}
if (packetsReceivedInTotal > 0 && skyscraperContext.IsAbortConditionMet())
return true;
return false;
}
private bool HasPackets()
{
lock (packetsQueue)
{
return packetsQueue.Count > 0;
}
}
private void DrainPackets()
{
if (packetsQueue.Count == 0)
return;
//JobContext.LogWindow.Log(String.Format("{0} packets left after switching off the filter.", packetsQueue.Count), 8);
DateTime startedAt = DateTime.Now;
byte[] singlePacketBuffer = new byte[188];
byte[] packetBuffer = null;
DateTime drainTickerPrevious = DateTime.Now, drainTickerNow = DateTime.Now;
while (packetsQueue.Count > 0)
{
packetBuffer = packetsQueue.Dequeue();
if (packetBuffer == null)
continue;
for (int i = 0; i < packetBuffer.Length; i += 188)
{
Array.Copy(packetBuffer, i, singlePacketBuffer, 0, 188);
skyscraperContext.IngestSinglePacket(singlePacketBuffer);
}
drainTickerPrevious = drainTickerNow;
drainTickerNow = DateTime.Now;
if (drainTickerNow.Second != drainTickerPrevious.Second)
{
//JobContext.LogWindow.Log(String.Format("{0} packets left ({1}).", packetsQueue.Count, drainTickerNow.ToLongTimeString()),8);
}
}
//JobContext.LogWindow.Log(String.Format("Packets drained in {0} seconds.", (DateTime.Now - startedAt).TotalSeconds), 8);
}
#endregion
}
}