using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.NetworkInformation; using System.Text; using System.Threading.Tasks; using skyscraper5.Dvb.Descriptors; using skyscraper5.Skyscraper.IO; using skyscraper5.Skyscraper.Plugins; namespace skyscraper5.Docsis.MacManagement { [SkyscraperPlugin] [MacManagementMessageType(3, 29)] [MacManagementMessageType(5, 51)] public class UpstreamChannelDescriptor : MacManagementMessage { public UpstreamChannelDescriptor(PhysicalAddress source, PhysicalAddress destination, byte[] buffer) : base(source, destination, buffer) { MemoryStream ms = new MemoryStream(buffer, false); UpstreamChannelID = ms.ReadUInt8(); ConfigurationChangeCount = ms.ReadUInt8(); MinislotSize = ms.ReadUInt8(); DownstreamChannelId = ms.ReadUInt8(); while (ms.GetAvailableBytes() >= 2) { byte type = ms.ReadUInt8(); byte length = ms.ReadUInt8(); if (ms.GetAvailableBytes() < length) { Valid = false; return; } byte[] v = ms.ReadBytes(length); switch (type) { //Found in CM-SP-MULPIv3.1-I03-1406101.pdf on page 107 case 1: ModulationRate = v[0]; break; case 2: (v[0], v[1], v[2], v[3]) = (v[3], v[2], v[1], v[0]); Frequency = BitConverter.ToUInt32(v); break; case 3: PreamblePattern = v; break; case 5: if (BurstDescriptors == null) BurstDescriptors = new List(); BurstDescriptors.Add(new BurstDescriptor(v)); break; case 6: ExtendedPreamblePattern = v; break; case 7: if (v[0] == 1) S_CDMAMode = true; else if (v[0] == 2) S_CDMAMode = false; else break; break; case 15: //Undocumented as of DOCSIS 4.0 ? break; case 16: RangingRequired = (RangingRequiredEnum)v[0]; break; case 23: goto case 5; case 24: (v[0], v[1]) = (v[1], v[0]); UcdChangeIndicator = new UcdChangeIndicatorBitmask(BitConverter.ToUInt16(v, 0)); break; case 25: OfdmaTimestampSnapshot = v[0] & 0x0f; OfdmaTimestampSnapshot <<= 8; //4 bits OfdmaTimestampSnapshot += v[1]; OfdmaTimestampSnapshot <<= 8; //12 bits OfdmaTimestampSnapshot += v[2]; OfdmaTimestampSnapshot <<= 8; //20 bits OfdmaTimestampSnapshot += v[3]; OfdmaTimestampSnapshot <<= 4; //28 bits OfdmaTimestampSnapshot += ((v[4] & 0xf0) >> 4); OfdmaTimestampSnapshotDivideBy20 = v[4] & 0x0f; break; case 26: OfdmaCyclicPrefixSize = v[0]; break; case 27: OfdmaRolloffPeriodSize = v[0]; break; case 28: SubcarrierSpacing = v[0]; break; case 29: (v[0], v[1], v[2], v[3]) = (v[3], v[2], v[1], v[0]); CenterFrequencyOfSubcarrier0 = BitConverter.ToUInt32(v); break; case 30: SubcarrierExclusionBand = new Tuple[length / 4]; for (int i = 0; i < length / 4; i++) { (v[(i * 4) + 0], v[(i * 4) + 1]) = (v[(i * 4) + 1], v[(i * 4) + 0]); (v[(i * 4) + 2], v[(i * 4) + 3]) = (v[(i * 4) + 3], v[(i * 4) + 2]); SubcarrierExclusionBand[i] = new Tuple(BitConverter.ToUInt16(v, (i * 4) + 0), BitConverter.ToUInt16(v, (i * 4) + 2)); } break; case 32: SymbolsInOfdmaFrame = v[0]; break; case 33: byte[] v2 = new byte[4]; (v2[0], v2[1], v2[2]) = (v[2], v[1], v[0]); RandomizationSeed = BitConverter.ToUInt32(v2, 0); break; default: if (type > 34) { Valid = false; return; } throw new NotImplementedException(String.Format("{0} TLV type {1}", nameof(UpstreamChannelDescriptor), type)); } } Valid = true; } public uint RandomizationSeed { get; set; } public byte? SymbolsInOfdmaFrame { get; set; } public Tuple[] SubcarrierExclusionBand { get; private set; } public uint? CenterFrequencyOfSubcarrier0 { get; set; } public byte? SubcarrierSpacing { get; set; } public byte? OfdmaRolloffPeriodSize { get; set; } public byte? OfdmaCyclicPrefixSize { get; set; } public int? OfdmaTimestampSnapshotDivideBy20 { get; set; } public int? OfdmaTimestampSnapshot { get; set; } public UcdChangeIndicatorBitmask UcdChangeIndicator { get; private set; } public List BurstDescriptors { get; private set; } public bool? S_CDMAMode { get; set; } public byte[] PreamblePattern { get; set; } public uint? Frequency { get; set; } public byte? ModulationRate { get; set; } public byte DownstreamChannelId { get; set; } public byte MinislotSize { get; set; } public byte ConfigurationChangeCount { get; set; } public byte UpstreamChannelID { get; set; } public byte[] ExtendedPreamblePattern { get; private set; } public RangingRequiredEnum RangingRequired { get; private set; } public enum RangingRequiredEnum : byte { NoRangingRequired = 0, UnicastInitialRangingRequired = 1, BroadcastInitialRangingRequired = 2, ProbingRequired = 3 } public class BurstDescriptor { public BurstDescriptor(byte[] buffer) { MemoryStream ms = new MemoryStream(buffer, false); IntervalUsageCode = ms.ReadUInt8(); while (ms.GetAvailableBytes() > 2) { byte type = ms.ReadUInt8(); byte length = ms.ReadUInt8(); byte[] v = ms.ReadBytes(length); switch (type) { //Found in CM-SP-MULPIv4.0-I01-190815.pdf, page 118 case 1: ModulationType = v[0]; break; case 2: if (v[0] == 1) DifferentialEncoding = true; else if (v[0] == 2) DifferentialEncoding = false; else break; break; case 3: (v[1], v[0]) = (v[0], v[1]); PreambleLength = BitConverter.ToUInt16(v, 0); break; case 4: (v[1], v[0]) = (v[0], v[1]); PreambleValueOffset = BitConverter.ToUInt16(v, 0); break; case 5: FecErrorCorrection = v[0]; break; case 6: FecCodewordInformationBytes = v[0]; break; case 7: (v[1], v[0]) = (v[0], v[1]); ScramblerSeed = BitConverter.ToUInt16(v, 0); ScramblerSeed &= 0x7fff; break; case 8: MaximumBurstSize = v[0]; break; case 9: GuardTimeSize = v[0]; break; case 10: LastCodewordLength = (CodewordLength)v[0]; break; case 11: if (v[0] == 1) Scrambler = true; else if (v[0] == 2) Scrambler = false; else break; break; case 12: RsInterleaverDepth = v[0]; break; case 13: (v[1], v[0]) = (v[0], v[1]); RsInterleaverBlockSize = BitConverter.ToUInt16(v, 0); break; case 14: PreambleType = (PreambleTypeEnum)v[0]; break; case 19: (v[1], v[0]) = (v[0], v[1]); SubcarriersInitialRanging = BitConverter.ToUInt16(v, 0); break; case 20: (v[1], v[0]) = (v[0], v[1]); SubcarriersFineRanging = BitConverter.ToUInt16(v, 0); break; case 21: OfdmaDataProfiles = new OfdmaDataProfile[length / 2]; for (int i = 0; i < length / 2; i++) { OfdmaDataProfiles[i] = new OfdmaDataProfile(v[(i * 2) + 0], v[(i * 2) + 1]); } break; case 22: OfdmaBroadcastIrStartingPowerLevel = (double)v[0] * 1.6; OfdmaBroadcastIrStartingPowerLevelIncrease = (double)v[1] * 0.25; break; default: throw new NotImplementedException(String.Format("{0} {1} {2}", nameof(BurstDescriptor), nameof(type), type)); } } } public OfdmaDataProfile[] OfdmaDataProfiles { get; private set; } public ushort? SubcarriersFineRanging { get; set; } public double? OfdmaBroadcastIrStartingPowerLevelIncrease { get; set; } public double? OfdmaBroadcastIrStartingPowerLevel { get; set; } public ushort? SubcarriersInitialRanging { get; set; } public PreambleTypeEnum? PreambleType { get; private set; } public enum PreambleTypeEnum : byte { QPSK0 = 1, QPSK1 = 2 } public ushort? RsInterleaverBlockSize { get; set; } public byte? RsInterleaverDepth { get; set; } public bool? Scrambler { get; set; } public CodewordLength? LastCodewordLength { get; set; } public enum CodewordLength : byte { Fixed = 1, Shortened = 2 } public byte? GuardTimeSize { get; set; } public byte? MaximumBurstSize { get; set; } public int? ScramblerSeed { get; set; } public byte? FecCodewordInformationBytes { get; set; } public byte? FecErrorCorrection { get; set; } public ushort? PreambleValueOffset { get; set; } public ushort? PreambleLength { get; set; } public bool? DifferentialEncoding { get; set; } public byte? ModulationType { get; set; } public byte IntervalUsageCode { get; set; } } public class UcdChangeIndicatorBitmask { private readonly ushort rawValue; public UcdChangeIndicatorBitmask(ushort readUInt16Be) { this.rawValue = readUInt16Be; } public bool ChangeIuc14 => (rawValue & 0x0020) != 0; public bool ChangeIuc13 => (rawValue & 0x0040) != 0; public bool ChangeIuc12 => (rawValue & 0x0080) != 0; public bool ChangeIuc11 => (rawValue & 0x0100) != 0; public bool ChangeIuc10 => (rawValue & 0x0200) != 0; public bool ChangeIuc9 => (rawValue & 0x0400) != 0; public bool ChangeIuc6 => (rawValue & 0x0800) != 0; public bool ChangeIuc5 => (rawValue & 0x1000) != 0; public bool ChangeOtherParameters => (rawValue & 0x2000) != 0; public bool ChangeUnusedSubcarrierSpecification => (rawValue & 0x4000) != 0; public bool ChangeSubcarrierExclusionBand => (rawValue & 0x8000) != 0; public ushort GetRawValue() { return rawValue; } } public class OfdmaDataProfile { private readonly byte first; private readonly byte second; public OfdmaDataProfile(byte first, byte second) { this.first = first; this.second = second; } public ModulationOrderIndex Modulation => (ModulationOrderIndex)((first & 0xf0) >> 4); public int PilotPatternIndex => (first & 0x0f); public byte NumberOfMinisolts => second; public enum ModulationOrderIndex { NoBitLoading, Reserved, QPSK, _8QAM, _16QAM, _32QAM, _64QAM, _128QAM, _256QAM, _512QAM, _1024QAM, _2048QAM, _4096QAM } } } }