915 lines
32 KiB
C#
915 lines
32 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using skyscraper5.src.Skyscraper.FrequencyListGenerator;
|
|
using skyscraper8.Skyscraper.Plugins;
|
|
using skyscraper5.Skyscraper.IO.CrazycatStreamReader;
|
|
using skyscraper8.Skyscraper.Drawing;
|
|
using skyscraper5.Dvb.Descriptors;
|
|
using skyscraper5.src.Mpeg2.PacketFilter;
|
|
using skyscraper5.Skyscraper.IO;
|
|
using skyscraper5.Skyscraper.Scraper;
|
|
using System.Runtime.InteropServices;
|
|
using skyscraper5.Skyscraper.Equipment;
|
|
|
|
namespace skyscraper8.Skyscraper.FrequencyListGenerator
|
|
{
|
|
public class BaseBlindscanJob
|
|
{
|
|
private static PluginLogger logger = PluginLogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
|
|
|
public BaseBlindscanJob(BlindscanJobConfiguration config)
|
|
{
|
|
if (config == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(config));
|
|
}
|
|
|
|
this.config = config;
|
|
}
|
|
|
|
private BlindscanJobConfiguration config;
|
|
private DbBlindscanJob jobInDb;
|
|
private List<BlindscanSearchResult> foundFrequencies;
|
|
|
|
public void Run()
|
|
{
|
|
if (config == null)
|
|
throw new NullReferenceException("No config? Dude, what?");
|
|
if (config.StreamReader == null)
|
|
throw new NullReferenceException("No StreamReader available");
|
|
|
|
|
|
jobInDb = new DbBlindscanJob(Guid.NewGuid(), config.TunerMetadata.MacAddress, config.TunerMetadata.Type, config.DiseqcIndex, config.Gps, config.SatellitePosition);
|
|
jobInDb.HorizontalHighState = config.DoHorizontalHigh ? DbBlindscanJobPolarizationStatus.SELECTED_WAITING : DbBlindscanJobPolarizationStatus.NOT_SELECTED;
|
|
jobInDb.HorizontalLowState = config.DoHorizontalLow ? DbBlindscanJobPolarizationStatus.SELECTED_WAITING : DbBlindscanJobPolarizationStatus.NOT_SELECTED;
|
|
jobInDb.VerticalLowState = config.DoVerticalLow ? DbBlindscanJobPolarizationStatus.SELECTED_WAITING : DbBlindscanJobPolarizationStatus.NOT_SELECTED;
|
|
jobInDb.VerticalHighState = config.DoVerticalHigh ? DbBlindscanJobPolarizationStatus.SELECTED_WAITING : DbBlindscanJobPolarizationStatus.NOT_SELECTED;
|
|
config.DataStorage.InsertBlindscanJob(jobInDb);
|
|
|
|
foundFrequencies = new List<BlindscanSearchResult>();
|
|
config.Ui.OnBlindscanOpenFoundFrquenciesWindow(foundFrequencies, config.TunerMetadata.Type);
|
|
|
|
if (!config.StreamReader.StartDvbEx(config.TunerMetadata.Index))
|
|
{
|
|
if (!config.StreamReader.StopDVB())
|
|
{
|
|
throw new Exception("failed to stop DVB in order to restart it.");
|
|
}
|
|
|
|
if (!config.StreamReader.StartDvbEx(config.TunerMetadata.Index))
|
|
{
|
|
throw new Exception("Failed to start DVB");
|
|
}
|
|
}
|
|
if (!RunBlindscan())
|
|
{
|
|
config.StreamReader.StopDVB();
|
|
config.Ui.OnBlindscanJobDone(false);
|
|
return;
|
|
}
|
|
|
|
config.Ui.OnBlindscanJobDone(true);
|
|
logger.Log(PluginLogLevel.Info,"Blindscan Job done!");
|
|
return;
|
|
}
|
|
|
|
public bool RunBlindscan()
|
|
{
|
|
Caps caps = config.TunerMetadata.Caps;
|
|
|
|
if (config.DoCollectRfSpectrum)
|
|
{
|
|
RfSpectrumData rfSpectrum = GatherRfSpectrum();
|
|
if (rfSpectrum != null)
|
|
{
|
|
config.ObjectStorage.StoreRfSpectrum(jobInDb.JobGuid, rfSpectrum);
|
|
}
|
|
}
|
|
if (caps.HasFlag(Caps.SR_BLSCAN2))
|
|
{
|
|
SearchResult sr1 = default;
|
|
int tpNum = default;
|
|
|
|
int lof1 = config.LnbType.Lof1 * 1000;
|
|
int lof2 = config.LnbType.Lof2 * 1000;
|
|
int lofSw = config.LnbType.LofSw * 1000;
|
|
int diseqc = config.TunerMetadata.DiseqcType;
|
|
int startFreq = config.LnbType.MinimumFrequency * 1000;
|
|
int endFreq = config.LnbType.MaximumFrequency * 1000;
|
|
bool anythingSuceeded = false;
|
|
nint allocHGlobal = Marshal.AllocHGlobal(ushort.MaxValue);
|
|
|
|
if (lofSw != 0)
|
|
{
|
|
if (config.DoHorizontalLow)
|
|
{
|
|
if (SendDiseqcCommand(false, true))
|
|
{
|
|
logger.Log(PluginLogLevel.Info,"Scanning low horizontal band...");
|
|
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_BLINDSCANNING;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
bool hLower = BlScan2Wrap(startFreq, lofSw, 0, lof1, lof2, lofSw, allocHGlobal, ref tpNum, (ref SearchResult sr1) => SearchResult1Callback(sr1, 1));
|
|
config.Ui.OnBlindscanBandComplete();
|
|
if (!hLower)
|
|
{
|
|
config.Ui.OnBlindscanErrorMessage("Blindscanning low horizontal area failed.");
|
|
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
}
|
|
else
|
|
{
|
|
anythingSuceeded = true;
|
|
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_SCRAPING;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
SendDiseqcCommand(false, true);
|
|
RunScrape();
|
|
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
foundFrequencies.Clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
config.Ui.OnBlindscanErrorMessage("DiSEqC Switch to low horizontal area failed.");
|
|
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED_DISEQC;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
}
|
|
}
|
|
|
|
if (config.DoHorizontalHigh)
|
|
{
|
|
if (SendDiseqcCommand(true, true))
|
|
{
|
|
logger.Log(PluginLogLevel.Info,"Scanning high horizontal band...");
|
|
jobInDb.HorizontalHighState = DbBlindscanJobPolarizationStatus.SELECTED_BLINDSCANNING;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
bool hUpper = BlScan2Wrap(lofSw, endFreq, 0, lof1, lof2, lofSw, allocHGlobal, ref tpNum, (ref SearchResult sr1) => SearchResult1Callback(sr1, 2));
|
|
config.Ui.OnBlindscanBandComplete();
|
|
if (!hUpper)
|
|
{
|
|
config.Ui.OnBlindscanErrorMessage("Blindscanning high horizontal area failed.");
|
|
jobInDb.HorizontalHighState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
}
|
|
else
|
|
{
|
|
anythingSuceeded = true;
|
|
jobInDb.HorizontalHighState = DbBlindscanJobPolarizationStatus.SELECTED_SCRAPING;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
SendDiseqcCommand(true, true);
|
|
RunScrape();
|
|
jobInDb.HorizontalHighState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
foundFrequencies.Clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
config.Ui.OnBlindscanErrorMessage("DiSEqC Switch to high horizontal area failed.");
|
|
jobInDb.HorizontalHighState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED_DISEQC;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
}
|
|
}
|
|
|
|
if (config.DoVerticalLow)
|
|
{
|
|
if (SendDiseqcCommand(false, false))
|
|
{
|
|
logger.Log(PluginLogLevel.Info,"Scanning low vertical band...");
|
|
jobInDb.VerticalLowState = DbBlindscanJobPolarizationStatus.SELECTED_BLINDSCANNING;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
bool vLower = BlScan2Wrap(startFreq, lofSw, 1, lof1, lof2, lofSw, allocHGlobal, ref tpNum, (ref SearchResult sr1) => SearchResult1Callback(sr1, 3));
|
|
config.Ui.OnBlindscanBandComplete();
|
|
if (!vLower)
|
|
{
|
|
config.Ui.OnBlindscanErrorMessage("Blindscanning low vertical area failed.");
|
|
jobInDb.VerticalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
}
|
|
else
|
|
{
|
|
anythingSuceeded = true;
|
|
jobInDb.VerticalLowState = DbBlindscanJobPolarizationStatus.SELECTED_SCRAPING;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
SendDiseqcCommand(false, false);
|
|
RunScrape();
|
|
jobInDb.VerticalLowState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
foundFrequencies.Clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
config.Ui.OnBlindscanErrorMessage("DiSEqC Switch to low vertical area failed.");
|
|
jobInDb.VerticalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED_DISEQC;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
}
|
|
}
|
|
|
|
if (config.DoVerticalHigh)
|
|
{
|
|
if (SendDiseqcCommand(true, false))
|
|
{
|
|
logger.Log(PluginLogLevel.Info,"Scanning high vertical band...");
|
|
jobInDb.VerticalHighState = DbBlindscanJobPolarizationStatus.SELECTED_BLINDSCANNING;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
bool vUpper = BlScan2Wrap(lofSw, endFreq, 1, lof1, lof2, lofSw, allocHGlobal, ref tpNum, (ref SearchResult sr1) => SearchResult1Callback(sr1, 4));
|
|
config.Ui.OnBlindscanBandComplete();
|
|
if (!vUpper)
|
|
{
|
|
config.Ui.OnBlindscanErrorMessage("Blindscanning high vertical area failed.");
|
|
jobInDb.VerticalHighState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
}
|
|
else
|
|
{
|
|
anythingSuceeded = true;
|
|
jobInDb.VerticalHighState = DbBlindscanJobPolarizationStatus.SELECTED_SCRAPING;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
SendDiseqcCommand(true, false);
|
|
RunScrape();
|
|
jobInDb.VerticalHighState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
foundFrequencies.Clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
config.Ui.OnBlindscanErrorMessage("DiSEqC Switch to high vertical area failed.");
|
|
jobInDb.VerticalHighState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED_DISEQC;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (SendDiseqcCommand(false, true))
|
|
{
|
|
logger.Log(PluginLogLevel.Info, string.Format("Scanning left circular band..."));
|
|
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_BLINDSCANNING;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
bool lCirc = BlScan2Wrap(startFreq, endFreq, 0, lof1, lof2, lofSw, allocHGlobal, ref tpNum, (ref SearchResult sr1) => SearchResult1Callback(sr1, 1));
|
|
config.Ui.OnBlindscanBandComplete();
|
|
if (!lCirc)
|
|
{
|
|
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
config.Ui.OnBlindscanErrorMessage("Blindscanning left circular area failed.");
|
|
}
|
|
else
|
|
{
|
|
anythingSuceeded = true;
|
|
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_SCRAPING;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
SendDiseqcCommand(false, true);
|
|
RunScrape();
|
|
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
foundFrequencies.Clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
config.Ui.OnBlindscanErrorMessage("DiSEqC Switch to left circulation failed.");
|
|
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED_DISEQC;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
}
|
|
if (SendDiseqcCommand(false, false))
|
|
{
|
|
logger.Log(PluginLogLevel.Info, string.Format("Scanning right circular band..."));
|
|
jobInDb.VerticalLowState = DbBlindscanJobPolarizationStatus.SELECTED_BLINDSCANNING;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
bool rCirc = BlScan2Wrap(startFreq, endFreq, 1, lof1, lof2, lofSw, allocHGlobal, ref tpNum, (ref SearchResult sr1) => SearchResult1Callback(sr1, 3));
|
|
config.Ui.OnBlindscanBandComplete();
|
|
if (!rCirc)
|
|
{
|
|
config.Ui.OnBlindscanErrorMessage("Blindscanning right circular area failed.");
|
|
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
}
|
|
else
|
|
{
|
|
anythingSuceeded = true;
|
|
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_SCRAPING;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
SendDiseqcCommand(false, false);
|
|
RunScrape();
|
|
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
foundFrequencies.Clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
config.Ui.OnBlindscanErrorMessage("DiSEqC Switch to right circulation failed.");
|
|
jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
|
|
config.DataStorage.UpdateJobState(jobInDb);
|
|
}
|
|
}
|
|
|
|
Marshal.FreeHGlobal(allocHGlobal);
|
|
return anythingSuceeded;
|
|
}
|
|
else
|
|
{
|
|
throw new NotImplementedException("Don't know how to blindscan with this tuner.");
|
|
}
|
|
}
|
|
|
|
|
|
private bool BlScan2Wrap(int freq_start, int freq_stop, int pol, int lof1, int lof2, int lofSw, nint pSearchResult, ref int pTpNum, BlScanCallback lpFunc)
|
|
{
|
|
config.Ui.OnBlindscanBeforeBLScan(freq_start, freq_start, freq_stop);
|
|
|
|
bool result = config.StreamReader.BLScan2(freq_start, freq_stop, pol, lof1, lof2, lofSw, pSearchResult, ref pTpNum, lpFunc);
|
|
|
|
config.Ui.OnBlindscanAfterBLScan();
|
|
|
|
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 = config.DiseqcIndex;
|
|
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");
|
|
}
|
|
|
|
logger.Log(PluginLogLevel.Info, string.Format("Send DiSEqC Command {0:X2}", (byte)myOpcode), 8);
|
|
|
|
DateTime started = DateTime.Now;
|
|
bool result = config.StreamReader.SendDiSEqC(2, myOpcode);
|
|
TimeSpan timeTaken = DateTime.Now - started;
|
|
logger.Log(PluginLogLevel.Debug, string.Format("DiSEqC Comannd sent in {0:F1} seconds.", timeTaken.TotalSeconds));
|
|
if (timeTaken.TotalSeconds > 10)
|
|
{
|
|
logger.Log(PluginLogLevel.Warn, 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)
|
|
{
|
|
BlindscanSearchResult blindscanSearchResult = new BlindscanSearchResult();
|
|
blindscanSearchResult.SearchResult = searchResult;
|
|
blindscanSearchResult.Standard = (STD_TYPE)searchResult.StdType;
|
|
logger.Log(PluginLogLevel.Info, string.Format("Found frequency: {0}, {1}", searchResult.Freq / 1000, searchResult.Pol == 0 ? "H" : "V"));
|
|
lock (foundFrequencies)
|
|
{
|
|
foundFrequencies.Add(blindscanSearchResult);
|
|
}
|
|
config.DataStorage.InsertSearchResult(jobInDb, true, searchResult, polarityIndex, new SearchResult2());
|
|
config.Ui.OnBlindscanSearchResult1Callback(blindscanSearchResult, polarityIndex, config.LnbType.MinimumFrequency, config.LnbType.MaximumFrequency);
|
|
}
|
|
|
|
|
|
public void RunScrape()
|
|
{
|
|
int lof1 = config.LnbType.Lof1 * 1000;
|
|
int lof2 = config.LnbType.Lof2 * 1000;
|
|
int lofSw = config.LnbType.LofSw * 1000;
|
|
|
|
|
|
foreach (BlindscanSearchResult blindscanResult in foundFrequencies)
|
|
{
|
|
DateTime now = DateTime.Now;
|
|
|
|
blindscanResult.State = BlindscanResultState.Tuning;
|
|
config.DataStorage.UpdateTransponderState(jobInDb, blindscanResult.IsSatellite(), blindscanResult.SearchResult, blindscanResult.State, blindscanResult.SearchResult2);
|
|
config.Ui.OnBlindscanBeforeSetChannel(blindscanResult, config.LnbType);
|
|
bool channel = config.StreamReader.SetChannel(blindscanResult.SearchResult.Freq, blindscanResult.SearchResult.SR, blindscanResult.SearchResult.Pol, blindscanResult.SearchResult.FEC, lof1, lof2, lofSw);
|
|
if (!channel)
|
|
{
|
|
blindscanResult.State = BlindscanResultState.TuningFailed;
|
|
config.DataStorage.UpdateTransponderState(jobInDb, blindscanResult.IsSatellite(), blindscanResult.SearchResult, blindscanResult.State, blindscanResult.SearchResult2);
|
|
continue;
|
|
}
|
|
|
|
RunSkyscraper(blindscanResult);
|
|
config.Ui.OnBlindscanScrapeTransponderComplete(blindscanResult);
|
|
}
|
|
|
|
config.Ui.OnScrapeBandComplete();
|
|
}
|
|
|
|
|
|
|
|
private RfSpectrumData GatherRfSpectrum()
|
|
{
|
|
Caps caps = config.TunerMetadata.Caps;
|
|
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;
|
|
}
|
|
|
|
config.Ui.OnBlindscanBeginRfSpectrum();
|
|
|
|
int lof1 = config.LnbType.Lof1 * 1000;
|
|
int lof2 = config.LnbType.Lof2 * 1000;
|
|
int lofSw = config.LnbType.LofSw * 1000;
|
|
int startFreq = config.LnbType.MinimumFrequency * 1000;
|
|
int endFreq = config.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;
|
|
config.StreamReader.SendDiSEqC(2, diseqcCmd);
|
|
RfSpectrumDataBlock block = spectrumData.CreateBlock(startFreq, endFreq, STEP, polarization);
|
|
for (int i = startFreq; i <= endFreq; i += STEP)
|
|
{
|
|
double rf = double.NaN;
|
|
config.StreamReader.RFScan(i, diseqcCmd.HasFlag(DiSEqC_Opcode.DISEQC_HORIZONTAL) ? 0 : 1, lof1, lof2, lofSw, out rf);
|
|
logger.Log(PluginLogLevel.Debug,"{0} {1}, {2}", i, polarization.ToString().Substring(0, 1), rf);
|
|
block.Push(i, rf);
|
|
config.Ui.OnBlindscanRfSpectrumEnqueueSample(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);
|
|
}
|
|
|
|
config.Ui.OnBlindscanEndRfSpectrum();
|
|
|
|
return spectrumData;
|
|
}
|
|
|
|
private RfSpectrumData GatherRfSpectrumCable()
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
private IEnumerable<DiSEqC_Opcode> GetIqGraphDiseqcOpcodes()
|
|
{
|
|
DiSEqC_Opcode baseDiseqc = DiSEqC_Opcode.DISEQC_HIGH_NIBBLE;
|
|
|
|
int parameter = config.DiseqcIndex;
|
|
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 (config.DoHorizontalLow || config.DoHorizontalHigh)
|
|
yield return baseDiseqc | DiSEqC_Opcode.DISEQC_HORIZONTAL | DiSEqC_Opcode.DISEQC_LOW_BAND;
|
|
if (config.DoVerticalLow || config.DoVerticalHigh)
|
|
yield return baseDiseqc | DiSEqC_Opcode.DISEQC_VERTICAL | DiSEqC_Opcode.DISEQC_LOW_BAND;
|
|
yield break;
|
|
}
|
|
private IqChartData GatherIqGraph()
|
|
{
|
|
Caps caps = config.TunerMetadata.Caps;
|
|
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();
|
|
config.Ui.OnBlindscanBeginIqSpectrum(result);
|
|
|
|
|
|
sbyte[] buffer = new sbyte[400];
|
|
DateTime started = DateTime.Now;
|
|
while (!result.IsComplete)
|
|
{
|
|
bool iqScan = config.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);
|
|
|
|
config.Ui.OnBlindscanEndIq();
|
|
|
|
return result;
|
|
}
|
|
private IqChartData GatherIQGraphCable()
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
private void RunSkyscraper(BlindscanSearchResult result)
|
|
{
|
|
int misCounter = 1;
|
|
bool misMode = false;
|
|
bool cableTvMode = false;
|
|
Caps caps = config.TunerMetadata.Caps;
|
|
SearchResult satelliteSr = new SearchResult();
|
|
SearchResult2 cableSr = new SearchResult2();
|
|
|
|
if (caps.HasFlag(Caps.SR_SIGINFO))
|
|
{
|
|
int polarity = result.SearchResult.Pol;
|
|
int freq = result.SearchResult.Freq;
|
|
bool isHighBand = freq > config.LnbType.LofSw * 1000;
|
|
bool isHorizontal = polarity == 0;
|
|
|
|
SendDiseqcCommand(isHighBand, isHorizontal);
|
|
logger.Log(PluginLogLevel.Info, string.Format("Trying to BLScanEx..."), 8);
|
|
if (!config.StreamReader.BLScanEx(result.SearchResult.Freq, 5000, result.SearchResult.Pol,
|
|
config.LnbType.Lof1 * 1000, config.LnbType.Lof2 * 1000,
|
|
config.LnbType.LofSw * 1000, 1000000, (STD_TYPE)result.SearchResult.StdType,
|
|
ref satelliteSr))
|
|
{
|
|
//No blindscan? No problem! Try anyway!
|
|
satelliteSr = result.SearchResult;
|
|
satelliteSr.Lock = false;
|
|
result.State = BlindscanResultState.BlScanFailure;
|
|
config.DataStorage.UpdateTransponderState(jobInDb, result.IsSatellite(), result.SearchResult, result.State, result.SearchResult2);
|
|
}
|
|
|
|
if (!satelliteSr.Lock)
|
|
{
|
|
int attempts = (result.SearchResult.Freq / 1000).GetHighestDigit();
|
|
bool success = false;
|
|
for (int i = 0; i < attempts; i++)
|
|
{
|
|
Thread.Sleep(1000);
|
|
result.State = BlindscanResultState.Retrying;
|
|
logger.Log(PluginLogLevel.Info, string.Format("Retrying BLScanEx... ({0}/{1})", i + 1, attempts + 1), 8);
|
|
bool retryResult = config.StreamReader.BLScanEx(result.SearchResult.Freq, 5000, result.SearchResult.Pol, config.LnbType.Lof1 * 1000, config.LnbType.Lof2 * 1000, config.LnbType.LofSw * 1000, 1000000, (STD_TYPE)result.SearchResult.StdType,ref satelliteSr);
|
|
if (!retryResult)
|
|
{
|
|
result.State = BlindscanResultState.BlScanFailure;
|
|
continue;
|
|
}
|
|
if (!satelliteSr.Lock)
|
|
{
|
|
result.State = BlindscanResultState.NoLock;
|
|
continue;
|
|
}
|
|
success = true;
|
|
break;
|
|
}
|
|
config.DataStorage.UpdateTransponderState(jobInDb, result.IsSatellite(), result.SearchResult, result.State, result.SearchResult2);
|
|
if (!success)
|
|
return;
|
|
logger.Log(PluginLogLevel.Debug, "Sucessfully tuned with retries.");
|
|
}
|
|
|
|
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))
|
|
{
|
|
logger.Log(PluginLogLevel.Info, string.Format("Trying to SetChannel2..."), 8);
|
|
bool channel2 = config.StreamReader.SetChannel2((uint)result.SearchResult2.Freq, (uint)result.SearchResult2.BW);
|
|
if (!channel2)
|
|
{
|
|
result.State = BlindscanResultState.TuningFailed;
|
|
config.DataStorage.UpdateTransponderState(jobInDb, result.IsSatellite(), result.SearchResult, result.State, result.SearchResult2);
|
|
config.Ui.OnBlindscanLockFail(result.SearchResult, result.State);
|
|
return;
|
|
}
|
|
|
|
logger.Log(PluginLogLevel.Info, string.Format("Trying to get SignalInfo2..."), 8);
|
|
bool signalInfo2 = config.StreamReader.SignalInfo2(ref cableSr);
|
|
if (!signalInfo2)
|
|
{
|
|
result.State = BlindscanResultState.TuningFailed;
|
|
config.DataStorage.UpdateTransponderState(jobInDb, result.IsSatellite(), result.SearchResult, result.State, result.SearchResult2);
|
|
config.Ui.OnBlindscanLockFail(result.SearchResult, result.State);
|
|
return;
|
|
}
|
|
|
|
if (!cableSr.Lock)
|
|
{
|
|
result.State = BlindscanResultState.NoLock;
|
|
config.DataStorage.UpdateTransponderState(jobInDb, result.IsSatellite(), result.SearchResult, result.State, result.SearchResult2);
|
|
config.Ui.OnBlindscanLockFail(result.SearchResult, result.State);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new NotImplementedException("Couldn't figure out what signal info to use.");
|
|
}
|
|
|
|
if (config.DoCollectIqGraphs)
|
|
{
|
|
result.State = BlindscanResultState.IqCollecting;
|
|
IqChartData plot = GatherIqGraph();
|
|
if (plot != null)
|
|
{
|
|
result.State = BlindscanResultState.IqSaving;
|
|
config.ObjectStorage.StoreIqGraph(jobInDb.JobGuid, result.GetFrequency(), result.GetPolarity(config.LnbType.LofSw), plot);
|
|
}
|
|
}
|
|
|
|
for (int mis = 0; mis < misCounter; mis++)
|
|
{
|
|
if (misMode)
|
|
{
|
|
logger.Log(PluginLogLevel.Info, string.Format("Selecting MIS IS {0}", satelliteSr.IS[mis]), 8);
|
|
bool misSel = config.StreamReader.MISSel(misMode, satelliteSr.IS[mis], 0xff);
|
|
if (!misSel)
|
|
{
|
|
result.State = BlindscanResultState.MisFailure;
|
|
config.DataStorage.UpdateTransponderState(jobInDb, result.IsSatellite(), result.SearchResult, result.State, result.SearchResult2);
|
|
config.Ui.OnBlindscanLockFail(result.SearchResult, result.State);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//Start Filter
|
|
TsRecorder tsRecorder = null;
|
|
if (config.DoRecordTs)
|
|
{
|
|
tsRecorder = new TsRecorder();
|
|
tsRecorder.Recording = true;
|
|
string outputDirName = config.Ini.ReadValue("recording", "output_dir", "recording_output");
|
|
tsRecorder.RecordingOutputDirectory = new DirectoryInfo(outputDirName);
|
|
tsRecorder.RecordingOutputDirectory.EnsureExists();
|
|
if (tsRecorder.PrepareRecording())
|
|
{
|
|
DateTime now = DateTime.Now;
|
|
string 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, result.GetFrequency() / 1000,
|
|
result.GetPolarity(config.LnbType.LofSw), result.GetSymbolRate() / 1000,
|
|
(int)(config.SatellitePosition.angle * 10),
|
|
config.SatellitePosition.cardinalDirection == 0 ? "E" : "W");
|
|
tsRecorder.SetNextFilename(recordingFilename);
|
|
tsRecorder.CreateBufferedStream();
|
|
}
|
|
}
|
|
|
|
nint filterReference = nint.MaxValue;
|
|
logger.Log(PluginLogLevel.Info, string.Format("Set-Up filter..."), 8);
|
|
packetsQueue = new Queue<byte[]>();
|
|
|
|
bool filter = config.StreamReader.SetFilter(8192, DvbCallback, 0x02, 2, ref filterReference);
|
|
if (!filter)
|
|
{
|
|
result.State = BlindscanResultState.FilterFailure;
|
|
config.DataStorage.UpdateTransponderState(jobInDb, result.IsSatellite(), result.SearchResult, result.State, result.SearchResult2);
|
|
config.Ui.OnBlindscanLockFail(result.SearchResult, result.State);
|
|
return;
|
|
}
|
|
logger.Log(PluginLogLevel.Debug, string.Format("Filter set-up complete!"), 8);
|
|
|
|
//Use the Filter
|
|
result.State = BlindscanResultState.Scraping;
|
|
config.DataStorage.UpdateTransponderState(jobInDb, result.IsSatellite(), result.SearchResult, result.State, result.SearchResult2);
|
|
config.Ui.OnBlindscanFilterSetUp();
|
|
|
|
SkipFilter skipper = new SkipFilter(SkipFilter.CRAZYSCAN_BUFFER * 10);
|
|
skyscraperContext = SkyscraperContextFactory.CreateSkyscraper(config.DataStorage, config.ObjectStorage);
|
|
skyscraperContext.TcpProxyEnabled = true;
|
|
skyscraperContext.UiJunction = config.Ui;
|
|
IPacketFilter[] packetFilters = new IPacketFilter[0];
|
|
if (tsRecorder != null)
|
|
packetFilters = new IPacketFilter[] { tsRecorder, skipper };
|
|
skyscraperContext.InitalizeFilterChain(packetFilters);
|
|
|
|
|
|
byte[] singlePacketBuffer = new byte[188];
|
|
packetsReceivedInTotal = 0;
|
|
startedAt = DateTime.Now;
|
|
|
|
//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);
|
|
}
|
|
}
|
|
|
|
config.Ui.OnBlindscanScrapeStopCondition();
|
|
//Stop Filter
|
|
logger.Log(PluginLogLevel.Debug, string.Format("Deleting Filter..."), 8);
|
|
bool stopped = config.StreamReader.DelFilter(filterReference);
|
|
if (!stopped)
|
|
{
|
|
result.State = BlindscanResultState.DelFilterFailed;
|
|
config.DataStorage.UpdateTransponderState(jobInDb, result.IsSatellite(), result.SearchResult, result.State, result.SearchResult2);
|
|
return;
|
|
}
|
|
logger.Log(PluginLogLevel.Debug, string.Format("Deleted filter!"), 8);
|
|
DrainPackets();
|
|
skyscraperContext.Dispose();
|
|
|
|
if (tsRecorder != null)
|
|
tsRecorder.Dispose();
|
|
|
|
result.State = BlindscanResultState.DataSaving;
|
|
config.DataStorage.UpdateTransponderState(jobInDb, result.IsSatellite(), result.SearchResult, result.State, result.SearchResult2);
|
|
|
|
foreach (HumanReadableService humanReadableService in config.Ui.GetServices())
|
|
{
|
|
config.DataStorage.InsertTransponderService(jobInDb, result.IsSatellite(), result.SearchResult, result.SearchResult2, humanReadableService);
|
|
}
|
|
|
|
result.State = BlindscanResultState.Done;
|
|
config.DataStorage.UpdateTransponderState(jobInDb, result.IsSatellite(), result.SearchResult, result.State, result.SearchResult2);
|
|
config.Ui.OnBlindscanAfterPacketDrain();
|
|
|
|
}
|
|
}
|
|
|
|
private Queue<byte[]> packetsQueue;
|
|
private void DvbCallback(nint data, int length)
|
|
{
|
|
if (length % 188 == 0)
|
|
{
|
|
try
|
|
{
|
|
byte[] buffer = new byte[length];
|
|
Marshal.Copy(data, buffer, 0, length);
|
|
|
|
lock (packetsQueue)
|
|
{
|
|
packetsQueue.Enqueue(buffer);
|
|
}
|
|
|
|
packetsReceivedInTotal += (uint)(buffer.Length / 188);
|
|
config.Ui.OnBlindscanPacketOk(buffer.Length / 188, packetsQueue.Count);
|
|
}
|
|
catch (OutOfMemoryException e)
|
|
{
|
|
config.Ui.OnBlindscanPacketError(2, length);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logger.Log(PluginLogLevel.Warn, string.Format("odd packet size!"), 8);
|
|
config.Ui.OnBlindscanPacketError(1, length);
|
|
}
|
|
}
|
|
|
|
private ISkyscraperContext skyscraperContext;
|
|
private ulong packetsReceivedInTotal;
|
|
private DateTime startedAt;
|
|
private bool StopConditionMet()
|
|
{
|
|
if (startedAt == DateTime.MinValue)
|
|
startedAt = DateTime.Now;
|
|
|
|
if (config.Ui.IsZapNowRequested())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (!config.Ui.MayAutoZap())
|
|
return false;
|
|
|
|
if (packetsReceivedInTotal == 0 && (DateTime.Now - startedAt).TotalSeconds > 5)
|
|
return true;
|
|
|
|
if (skyscraperContext.GetIpCommunicationParties() > 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;
|
|
|
|
logger.Log(PluginLogLevel.Warn, 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)
|
|
{
|
|
logger.Log(PluginLogLevel.Warn, string.Format("{0} packets left ({1}).", packetsQueue.Count, drainTickerNow.ToLongTimeString()), 8);
|
|
}
|
|
}
|
|
|
|
logger.Log(PluginLogLevel.Warn, string.Format("Packets drained in {0} seconds.", (DateTime.Now - startedAt).TotalSeconds), 8);
|
|
}
|
|
}
|
|
}
|