From ef903cba960698890417cc109cc98c13e873910f Mon Sep 17 00:00:00 2001 From: feyris-tan <4116042+feyris-tan@users.noreply.github.com> Date: Mon, 25 May 2026 22:48:07 +0200 Subject: [PATCH] Implemented ATSC 3 L1BasicSignalling and L1DetailSignalling --- .../Atsc/A322/AtscPlpBasebandParser.cs | 48 ++- skyscraper8/Atsc/A322/L1BasicSignalling.cs | 12 +- skyscraper8/Atsc/A322/L1DetailSignalling.cs | 302 +++++++++++++++++- .../A324/BasebandPacketDataStreamReceiver.cs | 15 +- skyscraper8/Atsc/A324/PreambleData.cs | 2 +- .../Atsc/A324/TImingAndManagementPacket.cs | 128 ++++++++ skyscraper8/Atsc/A330/AlpPacket.cs | 9 + skyscraper8/Skyscraper/IO/InBitStream.cs | 8 +- 8 files changed, 509 insertions(+), 15 deletions(-) create mode 100644 skyscraper8/Atsc/A324/TImingAndManagementPacket.cs diff --git a/skyscraper8/Atsc/A322/AtscPlpBasebandParser.cs b/skyscraper8/Atsc/A322/AtscPlpBasebandParser.cs index 3a5f7f8..5141b51 100644 --- a/skyscraper8/Atsc/A322/AtscPlpBasebandParser.cs +++ b/skyscraper8/Atsc/A322/AtscPlpBasebandParser.cs @@ -1,4 +1,5 @@ -using skyscraper5.Skyscraper.IO; +using skyscraper5.Ietf.Rfc971; +using skyscraper5.Skyscraper.IO; using skyscraper8.Atsc.A330; using System; using System.Collections.Generic; @@ -103,7 +104,8 @@ namespace skyscraper8.Atsc.A322 } else { - throw new NotImplementedException("Concatenation Header not yet implemented."); + stateMachineState = 7; + break; } } stateMachineState = 4; @@ -125,6 +127,45 @@ namespace skyscraper8.Atsc.A322 DeliverPacket(currentAlpPacket, neededBuffer); stateMachineState = 1; break; + case 7: //Read Concatenation Header first byte. + byte[] concatHeaderFirstByteBuffer = new byte[1]; + int readResult = ms.Read(concatHeaderFirstByteBuffer, 0, 1); + if (readResult == 0) + break; + + currentAlpPacket.ConcatLengthMsb = (concatHeaderFirstByteBuffer[0] & 0xf0) >> 4; + currentAlpPacket.Count = (concatHeaderFirstByteBuffer[0] & 0x0e) >> 1; + currentAlpPacket.SIF = (concatHeaderFirstByteBuffer[0] & 0x01) != 0; + + int neededLength = 12 * currentAlpPacket.Count; + if ((currentAlpPacket.Count & 1) == 0) + neededLength += 4; + neededLength /= 8; + + neededBufferOffset = 0; + stateMachineState = 8; + + break; + case 8: //Get Concatenation Header Lengths + int readSuccesful3 = ms.Read(neededBuffer, neededBufferOffset, neededBuffer.Length - neededBufferOffset); + neededBufferOffset += readSuccesful3; + if (neededBufferOffset == neededBuffer.Length) + { + stateMachineState = 9; + } + break; + case 9: //Parse Concatenation Header Lengths + currentAlpPacket.ParseConcatenationHeaderLengths(neededBuffer); + if (currentAlpPacket.PacketType == AlpPacketType.LinkLayerSignalling) + { + throw new NotImplementedException("Concatenated Link Layer Signalling Packets in ATSC 3.0 not yet supported."); + } + + currentAlpPacket.Length2 = (currentAlpPacket.ConcatLengthMsb << 11) | currentAlpPacket.Length; + neededBufferOffset = 0; + neededBuffer = new byte[currentAlpPacket.Length2]; + stateMachineState = 5; + break; default: throw new NotImplementedException(String.Format("ATSC PLP Baseband Parser State machine state #{0}", stateMachineState)); } @@ -133,7 +174,7 @@ namespace skyscraper8.Atsc.A322 private void DeliverPacket(AlpPacket? currentAlpPacket, byte[]? neededBuffer) { - Console.WriteLine("PacketType = {0} \t Length = {1}", currentAlpPacket.PacketType, neededBuffer.Length); + Console.WriteLine("PacketType = {0} \t Length = {1} (first bytes = {2:X2},{3:X2},{4:X2})", currentAlpPacket.PacketType, neededBuffer.Length, neededBuffer[0], neededBuffer[1], neededBuffer[2]); } internal void OnSyncLoss() @@ -145,6 +186,7 @@ namespace skyscraper8.Atsc.A322 private int stateMachineState; private byte[] neededBuffer; private int neededBufferOffset; + } internal enum OfiDescription diff --git a/skyscraper8/Atsc/A322/L1BasicSignalling.cs b/skyscraper8/Atsc/A322/L1BasicSignalling.cs index ccb6cc4..d018b56 100644 --- a/skyscraper8/Atsc/A322/L1BasicSignalling.cs +++ b/skyscraper8/Atsc/A322/L1BasicSignalling.cs @@ -39,8 +39,8 @@ namespace skyscraper8.Atsc.A322 L1DetailAdditionalParityMode = bitstream.ReadBitsAsByte(2); L1DetailTotalCells = bitstream.ReadBitsAsUint(19); FirstSubMimo = bitstream.ReadBitAsBoolean(); - FirstSubMiso = bitstream.ReadBitsAsByte(2); - FirstSubFftSize = bitstream.ReadBitsAsByte(2); + FirstSubMiso = (MisoSignallingFormat)bitstream.ReadBitsAsByte(2); + FirstSubFftSize = (FftSizeSignallingFormat)bitstream.ReadBitsAsByte(2); FirstSubReducedCarries = bitstream.ReadBitsAsByte(3); FirstSubGuardInterval = bitstream.ReadBitsAsByte(4); FirstSubNumOfdmSymbols = bitstream.ReadBitsAsUint(11); @@ -49,6 +49,9 @@ namespace skyscraper8.Atsc.A322 FirstSubSbsFirst = bitstream.ReadBitAsBoolean(); FirstSubSbsLast = bitstream.ReadBitAsBoolean(); FirstSubMimoMixed = bitstream.ReadBitAsBoolean(); + + byte[] bytes = bitstream.ReadBitsAsByteArray(47); //reserved + uint crc = bitstream.ReadBitsAsUint(32); Valid = true; } @@ -71,8 +74,8 @@ namespace skyscraper8.Atsc.A322 public byte L1DetailAdditionalParityMode { get; } public uint L1DetailTotalCells { get; } public bool FirstSubMimo { get; } - public byte FirstSubMiso { get; } - public byte FirstSubFftSize { get; } + public MisoSignallingFormat FirstSubMiso { get; } + public FftSizeSignallingFormat FirstSubFftSize { get; } public byte FirstSubReducedCarries { get; } public byte FirstSubGuardInterval { get; private set; } public uint FirstSubNumOfdmSymbols { get; } @@ -98,4 +101,5 @@ namespace skyscraper8.Atsc.A322 AceOnly, TrAndAce } + } diff --git a/skyscraper8/Atsc/A322/L1DetailSignalling.cs b/skyscraper8/Atsc/A322/L1DetailSignalling.cs index 354825a..46ff666 100644 --- a/skyscraper8/Atsc/A322/L1DetailSignalling.cs +++ b/skyscraper8/Atsc/A322/L1DetailSignalling.cs @@ -1,4 +1,5 @@ using skyscraper5.Skyscraper; +using skyscraper5.Skyscraper.IO; using System; using System.Collections.Generic; using System.Linq; @@ -9,8 +10,307 @@ namespace skyscraper8.Atsc.A322 { internal class L1DetailSignalling : Validatable { - public L1DetailSignalling(byte[] bytes) + public L1DetailSignalling(byte[] bytes, L1BasicSignalling basicSignalling) { + InBitStream bitStream = new InBitStream(bytes); + this.Version = new Version(bitStream.ReadBitsAsByte(4), 0); + + byte numRf = bitStream.ReadBitsAsByte(3); + RfIds = new ushort[numRf]; + for (int i = 1; i < numRf; i++) + { + RfIds[i] = bitStream.ReadBitsAsUInt16(16); + bitStream.ReadBitsAsByte(3); //reserved + } + + if (basicSignalling.TimeInfoFlag != 0) + { + uint secs = 0; + int msecs = 0; + int microsecs = 0; + int nanosecs = 0; + secs = bitStream.ReadBitsAsUint(32); + msecs = bitStream.ReadBitsAsUInt16(10); + if ((int)basicSignalling.TimeInfoFlag != 1) + { + microsecs = bitStream.ReadBitsAsUInt16(10); + if ((int)basicSignalling.TimeInfoFlag != 2) + { + nanosecs = bitStream.ReadBitsAsUInt16(10); + } + } + } + + Subframes = new L1Subframe[basicSignalling.NumSubframes]; + for (int i = 0; i < basicSignalling.NumSubframes; i++) + { + L1Subframe child = new L1Subframe(); + Subframes[i] = child; + + if (i > 0) + { + child.Mimo = bitStream.ReadBitAsBoolean(); + child.Miso = (MisoSignallingFormat)bitStream.ReadBitsAsByte(2); + child.FftSize = (FftSizeSignallingFormat)bitStream.ReadBitsAsByte(2); + child.ReducedCarriers = bitStream.ReadBitsAsByte(3); + child.GuardInterval = bitStream.ReadBitsAsByte(3); + child.NumOfdmSymbols = bitStream.ReadBitsAsUInt16(11); + child.ScatteredPilotPatern = bitStream.ReadBitsAsByte(5); + child.ScatteredPilotBoost = bitStream.ReadBitsAsByte(3); + child.SbsFirst = bitStream.ReadBitAsBoolean(); + child.SbsLast = bitStream.ReadBitAsBoolean(); + } + + if (basicSignalling.NumSubframes > 0) + { + child.SubframeMultiplex = bitStream.ReadBitAsBoolean(); + } + child.FrequencyInterleaver = bitStream.ReadBitAsBoolean(); + + if (((i == 0) && (basicSignalling.FirstSubSbsFirst || basicSignalling.FirstSubSbsLast)) || ((i > 0) && (child.SbsFirst | child.SbsLast))) + { + child.SbsNullCells = bitStream.ReadBitsAsUInt16(13); + } + byte numPlp = bitStream.ReadBitsAsByte(6); + child.Plps = new L1SubframePlp[numPlp]; + + for (int j = 0; j < numPlp; j++) + { + L1SubframePlp plp = new L1SubframePlp(); + child.Plps[j] = plp; + + plp.Id = bitStream.ReadBitsAsByte(6); + plp.LlsFlag = bitStream.ReadBitAsBoolean(); + plp.Layer = bitStream.ReadBitsAsByte(2); + plp.Start = bitStream.ReadBitsAsUint(24); + plp.Size = bitStream.ReadBitsAsUint(24); + plp.ScramblerType = bitStream.ReadBitsAsByte(2); + plp.FecType = (PlpFecTypeSignallingFormat)bitStream.ReadBitsAsByte(4); + if (plp.NeedModCod()) + { + plp.Mod = (PlpModSignallingFormat)bitStream.ReadBitsAsByte(4); + plp.Cod = (PlpCodSignallingFormat)bitStream.ReadBitsAsByte(4); + } + plp.TiMode = (PlpTiModeSignallingFormat)bitStream.ReadBitsAsByte(2); + if (plp.TiMode == PlpTiModeSignallingFormat.NoTimeInterleaving) + { + plp.FecBlockStart = bitStream.ReadBitsAsUInt16(15); + } + else if (plp.TiMode == PlpTiModeSignallingFormat.ConvolutionalTimeInterleaving) + { + plp.CtiFecBlockStart = bitStream.ReadBitsAsUint(22); + } + if (numRf > 0) + { + byte numChannelBonded = bitStream.ReadBitsAsByte(3); + if (numChannelBonded > 0) + { + plp.ChannelBondingFormat = (PlpChannelBondingFormatSignallingFormat)bitStream.ReadBitsAsByte(2); + plp.BondedRfId = new byte[numChannelBonded]; + for (byte k = 0; k < numChannelBonded; k++) + { + plp.BondedRfId[k] = bitStream.ReadBitsAsByte(3); + } + } + } + if ((i == 0 && basicSignalling.FirstSubMimo) || (i > 0 && child.Mimo)) + { + plp.MimoStreamCombining = bitStream.ReadBitAsBoolean(); + plp.MimoIqInterleaving = bitStream.ReadBitAsBoolean(); + plp.MimoPh = bitStream.ReadBitAsBoolean(); + } + + if (plp.Layer == 0) + { + plp.Type = bitStream.ReadBitAsBoolean(); + if (plp.Type) + { + plp.NumSubslices = bitStream.ReadBitsAsUInt16(14); + plp.SubsliceInterval = bitStream.ReadBitsAsUint(24); + } + if (((plp.TiMode == PlpTiModeSignallingFormat.ConvolutionalTimeInterleaving) || (plp.TiMode == PlpTiModeSignallingFormat.HybridTimeInterleaving) && (plp.Mod == 0))) + { + plp.TiExtendedInterleaving = bitStream.ReadBitAsBoolean(); + } + if (plp.TiMode == PlpTiModeSignallingFormat.ConvolutionalTimeInterleaving) + { + plp.CtiDepth = bitStream.ReadBitsAsByte(3); + plp.CtiStartRow = bitStream.ReadBitsAsUInt16(11); + } + else if (plp.TiMode == PlpTiModeSignallingFormat.HybridTimeInterleaving) + { + plp.HtiInterSubframe = bitStream.ReadBitAsBoolean(); + byte htiNumTiBlocks = bitStream.ReadBitsAsByte(4); + plp.HtiNumFecBlocksMax = bitStream.ReadBitsAsUInt16(12); + if (!plp.HtiInterSubframe) + { + plp.HtiNumFecBlocksMax = bitStream.ReadBitsAsUInt16(12); + } + else + { + plp.HtiNumFecBlocks = new ushort[htiNumTiBlocks]; + for (byte k = 0; k < htiNumTiBlocks; k++) + { + plp.HtiNumFecBlocks[k] = bitStream.ReadBitsAsUInt16(12); + } + } + plp.HtiCellInterleaver = bitStream.ReadBitAsBoolean(); + } + } + else + { + plp.LdmInjectionLevel = bitStream.ReadBitsAsByte(5); + } + } + } + Bsid = bitStream.ReadBitsAsUInt16(16); + for (int i = 0; i < basicSignalling.NumSubframes; i++) + { + if (i > 0) + { + Subframes[i].MimoMixed = bitStream.ReadBitAsBoolean(); + } + if ((i == 0 && basicSignalling.FirstSubMimoMixed) || (i > 0 || Subframes[i].MimoMixed)) + { + for (int j = 0; j < Subframes[i].Plps.Length; j++) + { + Subframes[i].Plps[j].Mimo = bitStream.ReadBitAsBoolean(); + if (Subframes[i].Plps[j].Mimo) + { + Subframes[i].Plps[j].MimoStreamCombining = bitStream.ReadBitAsBoolean(); + Subframes[i].Plps[j].MimoIqInterleaving = bitStream.ReadBitAsBoolean(); + Subframes[i].Plps[j].MimoPh = bitStream.ReadBitAsBoolean(); + } + } + } + } + + Valid = true; + } + + public Version Version { get; } + public ushort[] RfIds { get; private set; } + public L1Subframe[] Subframes { get; private set; } + public ushort Bsid { get; } + } + + internal class L1SubframePlp + { + public byte Id { get; internal set; } + public bool LlsFlag { get; internal set; } + public byte Layer { get; internal set; } + public uint Start { get; internal set; } + public uint Size { get; internal set; } + public byte ScramblerType { get; internal set; } + public PlpFecTypeSignallingFormat FecType { get; internal set; } + public PlpModSignallingFormat Mod { get; internal set; } + public PlpCodSignallingFormat Cod { get; internal set; } + public PlpTiModeSignallingFormat TiMode { get; internal set; } + public ushort FecBlockStart { get; internal set; } + public uint CtiFecBlockStart { get; internal set; } + public PlpChannelBondingFormatSignallingFormat ChannelBondingFormat { get; internal set; } + public byte[] BondedRfId { get; internal set; } + public bool MimoStreamCombining { get; internal set; } + public bool MimoIqInterleaving { get; internal set; } + public bool MimoPh { get; internal set; } + public bool Type { get; internal set; } + public ushort NumSubslices { get; internal set; } + public uint SubsliceInterval { get; internal set; } + public bool TiExtendedInterleaving { get; internal set; } + public byte CtiDepth { get; internal set; } + public ushort CtiStartRow { get; internal set; } + public bool HtiInterSubframe { get; internal set; } + public ushort HtiNumFecBlocksMax { get; internal set; } + public ushort[] HtiNumFecBlocks { get; internal set; } + public bool HtiCellInterleaver { get; internal set; } + public byte LdmInjectionLevel { get; internal set; } + public bool Mimo { get; internal set; } + + internal bool NeedModCod() + { + throw new NotImplementedException(); } } + + class L1Subframe + { + public bool Mimo { get; internal set; } + public MisoSignallingFormat Miso { get; internal set; } + public FftSizeSignallingFormat FftSize { get; internal set; } + public byte ReducedCarriers { get; internal set; } + public byte GuardInterval { get; internal set; } + public ushort NumOfdmSymbols { get; internal set; } + public byte ScatteredPilotPatern { get; internal set; } + public byte ScatteredPilotBoost { get; internal set; } + public bool SbsFirst { get; internal set; } + public bool SbsLast { get; internal set; } + public bool SubframeMultiplex { get; internal set; } + public bool FrequencyInterleaver { get; internal set; } + public ushort SbsNullCells { get; internal set; } + public L1SubframePlp[] Plps { get; internal set; } + public bool MimoMixed { get; internal set; } + } + + enum MisoSignallingFormat + { + NoMiso, + _64Coefficients, + _256Coefficients, + } + + enum FftSizeSignallingFormat + { + _8k, + _16k, + _32k, + } + + enum PlpFecTypeSignallingFormat + { + Bch16kLdpc, + Bch64kLdpc, + Crc16kLdpc, + Crc64kLdpc, + _16kLdpc, + _64kLdpc, + } + + enum PlpModSignallingFormat + { + Qpsk, + _16QAM, + _64QAM, + _256QAM, + _1024QAM, + _4096Q + } + + enum PlpCodSignallingFormat + { + _2_15, + _3_15, + _4_15, + _5_15, + _6_15, + _7_15, + _8_15, + _9_15, + _10_15, + _11_15, + _12_15, + _13_15, + } + + enum PlpTiModeSignallingFormat + { + NoTimeInterleaving, + ConvolutionalTimeInterleaving, + HybridTimeInterleaving + } + + enum PlpChannelBondingFormatSignallingFormat + { + PlainChannelBonding, + SnrAveragedChannelBonding + } } diff --git a/skyscraper8/Atsc/A324/BasebandPacketDataStreamReceiver.cs b/skyscraper8/Atsc/A324/BasebandPacketDataStreamReceiver.cs index 3e64a01..812c6b9 100644 --- a/skyscraper8/Atsc/A324/BasebandPacketDataStreamReceiver.cs +++ b/skyscraper8/Atsc/A324/BasebandPacketDataStreamReceiver.cs @@ -115,7 +115,7 @@ namespace skyscraper8.Atsc.A324 } else if (plp == 65) { - throw new NotImplementedException("desynced timing and management"); + DesyncTimingAndManagement(); } else if (plp <= 63) { @@ -126,7 +126,18 @@ namespace skyscraper8.Atsc.A324 throw new NotImplementedException(String.Format("Unknown PLP: {0}", plp)); } } + plpSequenceNumbers[plp] = rtpPacket.RtpSequenceNumber; + plpSequenceNumbers[plp]++; + } + private TimingAndManagementPacket timingAndManagement; + + private void DesyncTimingAndManagement() + { + if (timingAndManagement != null) + { + timingAndManagement.Valid = false; + } } private void DesyncPreamble() @@ -153,7 +164,7 @@ namespace skyscraper8.Atsc.A324 private void HandleTimingAndManagementData(byte[] rtpPayload) { - throw new NotImplementedException(); + timingAndManagement = new TimingAndManagementPacket(rtpPayload); } private PreambleData preambleData; diff --git a/skyscraper8/Atsc/A324/PreambleData.cs b/skyscraper8/Atsc/A324/PreambleData.cs index acc789d..a3b167e 100644 --- a/skyscraper8/Atsc/A324/PreambleData.cs +++ b/skyscraper8/Atsc/A324/PreambleData.cs @@ -30,7 +30,7 @@ namespace skyscraper8.Atsc.A324 } long detailLength = expectedLength - 25; - DetailSignalling = new L1DetailSignalling(ms.ReadBytes(detailLength)); + DetailSignalling = new L1DetailSignalling(ms.ReadBytes(detailLength), BasicSignalling); Valid = DetailSignalling.Valid; } diff --git a/skyscraper8/Atsc/A324/TImingAndManagementPacket.cs b/skyscraper8/Atsc/A324/TImingAndManagementPacket.cs new file mode 100644 index 0000000..32ab5f1 --- /dev/null +++ b/skyscraper8/Atsc/A324/TImingAndManagementPacket.cs @@ -0,0 +1,128 @@ +using skyscraper5.Skyscraper; +using skyscraper5.Skyscraper.IO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace skyscraper8.Atsc.A324 +{ + internal class TimingAndManagementPacket : Validatable + { + public TimingAndManagementPacket(byte[] rtpPayload) + { + //STRUCTURE DATA + InBitStream bitStream = new InBitStream(rtpPayload); + ushort length = bitStream.ReadBitsAsUInt16(16); + if (length != rtpPayload.Length) + { + Valid = false; + return; + } + + byte versionMajor = bitStream.ReadBitsAsByte(4); + byte versionMinor = bitStream.ReadBitsAsByte(4); + this.Version = new Version(versionMajor, versionMinor); + + MajLogRepCntPre = bitStream.ReadBitsAsByte(4); + MajLogRepCntTim = bitStream.ReadBitsAsByte(4); + + byte bootstrapMajor = bitStream.ReadBitsAsByte(4); + byte bootstrapMinor = bitStream.ReadBitsAsByte(4); + this.Bootstrap = new Version(bootstrapMajor, bootstrapMinor); + + MinTimeToNext = bitStream.ReadBitsAsByte(5); + SystemBandwidth = bitStream.ReadBitsAsByte(2); + BsrCoefficient = bitStream.ReadBitsAsByte(7); + PreambleStructure = bitStream.ReadBitsAsByte(8); + EaWakeup = bitStream.ReadBitsAsByte(2); + byte numEmissionTim = bitStream.ReadBitsAsByte(6); + byte numXmtrsInGroupMinus1 = bitStream.ReadBitsAsByte(6); + XmtrGroupNum = bitStream.ReadBitsAsByte(7); + MajLogOverride = bitStream.ReadBitsAsByte(3); + NumMisoFiltCodes = bitStream.ReadBitsAsByte(2); + TxCarrierOffset = bitStream.ReadBitsAsByte(2); + MimoFlag = bitStream.ReadBitAsBoolean(); + bitStream.ReadBitsAsByte(6); //reserved + + //BOOTSTRAP TIMING DATA + EmissionTim = new TimeSpan[numEmissionTim]; + for (int i = 0; i < numEmissionTim; i++) + { + uint seconds = bitStream.ReadBitsAsUint(32); + uint nanoseconds = bitStream.ReadBitsAsUint(32); + long microsecs = nanoseconds / 1000; + EmissionTim[i] = new TimeSpan(0, 0, 0, (int)seconds, 0, (int)microsecs); + } + + //PER TRANSMITTER DATA + for (int i = 0; i <= numXmtrsInGroupMinus1; i++) + { + int intMimoFlag = MimoFlag ? 1 : 0; + for (int j = 0; j <= intMimoFlag; j++) + { + if (PerTransmitterData == null) + PerTransmitterData = new PerTransmitPolarizationData[numXmtrsInGroupMinus1 + 1][]; + if (PerTransmitterData[i] == null) + PerTransmitterData[i] = new PerTransmitPolarizationData[intMimoFlag + 1]; + + PerTransmitPolarizationData child = new PerTransmitPolarizationData(); + PerTransmitterData[i][j] = child; + + child.XmtrId = bitStream.ReadBitsAsUInt16(13); + if (j == 0) + { + child.TxTimeOffset = bitStream.ReadBitsAsUInt16(16); + } + child.TxIdInjectionLevel = bitStream.ReadBitsAsUint(4); + child.MisoFiltCodeIndex = bitStream.ReadBitsAsByte(2); + } + if (!MimoFlag) + { + bitStream.ReadBitsAsUint(29); + } + else + { + bitStream.ReadBitsAsUint(10); + } + } + + //PACKET RELEASE TIME + byte pktRlsSeconds = bitStream.ReadBitsAsByte(4); + ushort pktRlsAMilliseconds = bitStream.ReadBitsAsUInt16(10); + PktRls = new TimeSpan(0, 0, 0, pktRlsSeconds, pktRlsAMilliseconds); + bitStream.SkipBits(2); + + //ERROR CHECK DATA + ushort crc16 = bitStream.ReadBitsAsUInt16(16); + } + + public Version Version { get; } + public byte MajLogRepCntPre { get; } + public byte MajLogRepCntTim { get; } + public Version Bootstrap { get; } + public byte MinTimeToNext { get; } + public byte SystemBandwidth { get; } + public byte BsrCoefficient { get; } + public byte PreambleStructure { get; } + public byte EaWakeup { get; } + public byte XmtrGroupNum { get; } + public byte MajLogOverride { get; } + public byte NumMisoFiltCodes { get; } + public byte TxCarrierOffset { get; } + public bool MimoFlag { get; } + public TimeSpan[] EmissionTim { get; } + + public PerTransmitPolarizationData[][] PerTransmitterData { get; } + public TimeSpan PktRls { get; } + } + + public class PerTransmitPolarizationData + { + public ushort XmtrId { get; internal set; } + public ushort TxTimeOffset { get; internal set; } + public uint TxIdInjectionLevel { get; internal set; } + public byte MisoFiltCodeIndex { get; internal set; } + } +} diff --git a/skyscraper8/Atsc/A330/AlpPacket.cs b/skyscraper8/Atsc/A330/AlpPacket.cs index d327cbd..89a1abf 100644 --- a/skyscraper8/Atsc/A330/AlpPacket.cs +++ b/skyscraper8/Atsc/A330/AlpPacket.cs @@ -32,6 +32,15 @@ namespace skyscraper8.Atsc.A330 public bool? HeaderMode { get; } public bool? SegmentationConcatenation { get; } public int Length { get; } + public int ConcatLengthMsb { get; internal set; } + public int Count { get; internal set; } + public bool SIF { get; internal set; } + public int Length2 { get; internal set; } + + internal void ParseConcatenationHeaderLengths(byte[]? neededBuffer) + { + throw new NotImplementedException(); + } } enum AlpPacketType diff --git a/skyscraper8/Skyscraper/IO/InBitStream.cs b/skyscraper8/Skyscraper/IO/InBitStream.cs index 2f82ca1..4b4ddb6 100644 --- a/skyscraper8/Skyscraper/IO/InBitStream.cs +++ b/skyscraper8/Skyscraper/IO/InBitStream.cs @@ -110,12 +110,12 @@ namespace skyscraper5.Skyscraper.IO public byte[] ReadBitsAsByteArray(uint v) { - byte[] result = new byte[v / 8]; + byte[] result = new byte[(v / 8) + 1]; int offset = 0; while (v > 0) { - uint nextSize = Math.Min(8, v); - result[offset++] = ReadBitsAsByte(8); + byte nextSize = Math.Min((byte)8, (byte)v); + result[offset++] = ReadBitsAsByte(nextSize); v -= nextSize; } return result; @@ -125,7 +125,7 @@ namespace skyscraper5.Skyscraper.IO public long GetBitsAvailable() { - long result = _wrapped.GetAvailableBytes(); + long result = _wrapped.GetAvailableBytes() * 8; result += (8 - bitPointer); return result; }