def test_003_shorter_than_8_symbols_per_full_pass(self): punc = spinal.StridedPuncturingSchedule(2, 1) output = [punc.next() for i in xrange(200)] self.assertTrue(output == [0, 1] * 100) punc = spinal.StridedPuncturingSchedule(3, 1) output = [punc.next() for i in xrange(300)] self.assertTrue(output == [0, 2, 1] * 100) punc = spinal.StridedPuncturingSchedule(6, 2) output = [punc.next() for i in xrange(70)] self.assertTrue(output == [4, 0, 5, 2, 3, 5, 1] * 10)
def test_001_output_7_4(self): STRIDED_7_4_OUTPUT = [ 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6, 4, 0, 6, 6, 2, 6, 3, 5, 1, 6 ] punc = spinal.StridedPuncturingSchedule(7, 4) output_7_4 = [punc.next() for i in xrange(len(STRIDED_7_4_OUTPUT))] self.assertEquals(output_7_4[:20], STRIDED_7_4_OUTPUT[:20]) self.assertTrue(output_7_4 == STRIDED_7_4_OUTPUT, "Output doesn't match reference")
def test_001_repetitions_are_correct(self): """ Make sure that the stream positions output by the repetition puncturing schedule are indeed repetitions of the original puncturing schedule. """ NUM_CODE_STEPS = 20 TESTED_POSITIONS = 100 NUM_REPETITION_SET = [1, 2, 3, 6] for numRepetitions in NUM_REPETITION_SET: for numLastStepSymbolsPerPass in xrange(1, 4): punc = spinal.StridedPuncturingSchedule( NUM_CODE_STEPS, numLastStepSymbolsPerPass) punc2 = spinal.StridedPuncturingSchedule( NUM_CODE_STEPS, numLastStepSymbolsPerPass) repeatedPunc = spinal.RepeatingPuncturingSchedule( punc2, numRepetitions) indices = wireless.vectorus() # Run twice, once without resetting and another time after # resetting the puncturing schedules for resetIter in xrange(2): punc.batchNext(TESTED_POSITIONS, indices) unrepeated = list(indices) repeatedPunc.batchNext(TESTED_POSITIONS * numRepetitions, indices) repeated = list(indices) for i in range(len(repeated)): self.assertEqual( repeated[i], unrepeated[i / numRepetitions], "location %d in repeated schedule should match location %d in original schedule" % (i, i / numRepetitions)) # We reset to check that the repeating puncturing schedule # works well after a reset punc.reset() repeatedPunc.reset()
def test_002_puncturing(self): """ This test considers a scenario where several passes are generated contiguously. We produce the sequence telling which spine value each symbol was generated from, in an inefficient but straightforward way. This sequence is then used to check the correctness of the puncturing schedule implementation. """ MAX_NUM_CODE_STEPS = 20 NUM_PASSES = 20 for numCodeSteps in xrange(1, MAX_NUM_CODE_STEPS + 1): for numLastStepSymbolsPerPass in xrange(3): codePassTransmitted = range(numCodeSteps) codeStepsMod4 = numCodeSteps % 4 # the number of code steps is increased to be a multiple of 4. # the last code step is transmitted in the added symbols if codeStepsMod4 != 0: codePassTransmitted += ([numCodeSteps - 1] * (4 - codeStepsMod4)) # Also, in order to transmit more of last code step, we add # 4*numLastStepSymbolsPerPass of the last code step codePassTransmitted += ([numCodeSteps - 1] * (4 * numLastStepSymbolsPerPass)) passes = [] shiftMap = [1, 3, 0, 2] for passInd in xrange(NUM_PASSES): passes += codePassTransmitted[shiftMap[passInd % 4]::4] numSymbolsPerPass = len(codePassTransmitted) / 4 punc = spinal.StridedPuncturingSchedule( numCodeSteps, numLastStepSymbolsPerPass) # Check validity of numSymbolsForCodeStep for passInd in xrange(0, NUM_PASSES): testedPasses = passes[:(passInd + 1) * numSymbolsPerPass] for step in xrange(numCodeSteps): self.assertEqual( punc.numSymbolsForCodeStep(passInd + 1, step), testedPasses.count(step)) # Check validity of symbolIndexInPassStream for ind, step in enumerate(passes): symbolNumInStep = passes[:ind + 1].count(step) - 1 self.assertEqual( punc.symbolIndexInPassStream(step, symbolNumInStep), ind)
def test_001_no_repetitions(self): NUM_TESTED_LOCATIONS = 500 for modulus in xrange(1, 30): for numLastValueSymbols in [1, 2, 7]: punc = spinal.StridedPuncturingSchedule( modulus, numLastValueSymbols) numSymbolsFullPass = modulus - 1 + numLastValueSymbols spineIndices = wireless.vectorus() proto = spinal.StridedProtocol(1, modulus, NUM_TESTED_LOCATIONS, numLastValueSymbols, 1) lastNumSymbols = 0 nextNumSymbols = proto.numSymbolsNextDecode(0) while (nextNumSymbols != 0): # How many symbols were added? numAddedSymbols = nextNumSymbols - lastNumSymbols self.assertNotEqual(numAddedSymbols, 0, "Protocol did not advance!") if nextNumSymbols != NUM_TESTED_LOCATIONS: self.assertGreaterEqual( numAddedSymbols, numSymbolsFullPass / 8, "Expected the number of symbols between decodes to fit the puncturing" ) self.assertLessEqual( numAddedSymbols, (numSymbolsFullPass + 7) / 8, "Expected the number of symbols between decodes to fit the puncturing" ) # Get the spine indices from the puncturing schedule punc.batchNext(numAddedSymbols, spineIndices) # Make sure that the sequence in spineIndices is strictly # increasing for i in xrange(spineIndices.size() - 1): self.assertGreaterEqual( spineIndices[i + 1], spineIndices[i], "Expected the spine location between decodes to be a non-decreasing sequence, but location %d violates this" % (lastNumSymbols + i)) # Advance to next protocol demand proto.setResult(0, nextNumSymbols, False) lastNumSymbols = nextNumSymbols nextNumSymbols = proto.numSymbolsNextDecode(0)
def test_001_passLength(self): """ The puncturing schedule should return the right pass length: If number of code steps, N, is divided evenly by 4, then the length of the pass should be N/4 + numLastSymbolsPerPass. Otherwise, length of pass should be similar (but N/4 should be rounded up) """ MAX_NUM_CODE_STEPS = 20 for numCodeSteps in xrange(1, MAX_NUM_CODE_STEPS + 1): for numLastStepSymbolsPerPass in xrange(3): punc = spinal.StridedPuncturingSchedule( numCodeSteps, numLastStepSymbolsPerPass) self.assertEquals(punc.numSymbolsPerPass(), ((numCodeSteps + 3) / 4) + numLastStepSymbolsPerPass)
def test_002_output_32_2(self): STRIDED_32_2_OUTPUT = [ 4, 12, 20, 28, 0, 8, 16, 24, 31, 6, 14, 22, 30, 2, 10, 18, 26, 7, 15, 23, 31, 3, 11, 19, 27, 5, 13, 21, 29, 1, 9, 17, 25, 4, 12, 20, 28, 0, 8, 16, 24, 31, 6, 14, 22, 30, 2, 10, 18, 26, 7, 15, 23, 31, 3, 11, 19, 27, 5, 13, 21, 29, 1, 9, 17, 25, 4, 12, 20, 28, 0, 8, 16, 24, 31, 6, 14, 22, 30, 2, 10, 18, 26, 7, 15, 23, 31, 3, 11, 19, 27, 5, 13, 21, 29, 1, 9, 17, 25, 4, 12, 20, 28, 0, 8, 16, 24, 31, 6, 14, 22, 30, 2, 10, 18, 26, 7, 15, 23, 31, 3, 11, 19, 27, 5, 13, 21, 29, 1, 9, 17, 25, 4, 12, 20, 28, 0, 8, 16, 24, 31, 6, 14, 22, 30, 2, 10, 18, 26, 7, 15, 23, 31, 3, 11, 19, 27, 5, 13, 21, 29, 1, 9, 17, 25, 4, 12, 20, 28, 0, 8, 16, 24, 31, 6, 14, 22, 30, 2, 10, 18, 26, 7, 15, 23, 31, 3, 11, 19, 27, 5, 13, 21, 29, 1, 9, 17, 25, 4, 12, 20, 28, 0, 8, 16, 24, 31, 6, 14, 22, 30, 2, 10, 18, 26, 7, 15, 23, 31, 3, 11, 19, 27, 5, 13, 21, 29, 1, 9, 17, 25, 4, 12, 20, 28, 0, 8, 16, 24, 31, 6, 14, 22, 30, 2, 10, 18, 26, 7, 15, 23, 31, 3, 11, 19, 27, 5, 13, 21, 29, 1, 9, 17, 25, 4, 12, 20, 28, 0, 8, 16, 24, 31, 6, 14, 22, 30, 2, 10, 18, 26, 7, 15, 23, 31, 3, 11, 19, 27, 5, 13, 21, 29, 1, 9, 17, 25, 4, 12, 20, 28, 0, 8, 16, 24, 31, 6, 14, 22, 30, 2, 10, 18, 26, 7, 15, 23, 31, 3, 11, 19, 27, 5, 13, 21, 29, 1, 9, 17, 25, 4, 12, 20, 28, 0, 8, 16, 24, 31, 6, 14, 22, 30, 2, 10, 18, 26, 7, 15, 23, 31, 3, 11, 19, 27, 5, 13, 21, 29, 1, 9, 17, 25, 4, 12, 20, 28, 0, 8, 16, 24, 31, 6, 14, 22, 30, 2, 10, 18, 26, 7, 15, 23, 31, 3, 11, 19, 27, 5, 13, 21, 29, 1, 9, 17, 25, 4, 12, 20, 28, 0, 8, 16, 24, 31, 6, 14, 22, 30, 2, 10, 18, 26, 7, 15, 23, 31, 3, 11, 19, 27, 5, 13, 21, 29, 1, 9, 17, 25, 4, 12, 20, 28, 0, 8, 16, 24, 31, 6, 14, 22, 30, 2, 10, 18, 26, 7, 15, 23, 31, 3, 11, 19, 27, 5, 13, 21, 29, 1, 9, 17, 25, 4, 12, 20, 28, 0, 8, 16, 24, 31, 6, 14, 22, 30, 2, 10, 18, 26, 7, 15, 23, 31, 3, 11, 19, 27, 5, 13, 21, 29, 1, 9, 17, 25, 4, 12, 20, 28, 0 ] punc = spinal.StridedPuncturingSchedule(32, 2) output_32_2 = [punc.next() for i in xrange(len(STRIDED_32_2_OUTPUT))] self.assertEquals(output_32_2[:20], STRIDED_32_2_OUTPUT[:20]) self.assertTrue(output_32_2 == STRIDED_32_2_OUTPUT, "Output doesn't match reference")
def test_001_Produces_correct_symbols(self): PACKET_LENGTH_BITS = 192 NUM_PASSES = 60 NUM_CACHED_PASSES = 30 NUM_LAST_CODE_STEP_SYMBOLS = 2 NUM_PACKETS = 10 NUM_COMPARISONS = 200 codeParams = spinal.reference.CodeParams(salsaNumRounds = 12, hashWordSize = 64, numBitsPerCodingStep = 4, symbolSizeBits = 10, transmittedPointPrecisionBits = 16) # get packets packets = [numpy.random.bytes(PACKET_LENGTH_BITS / 8) for i in xrange(NUM_PACKETS)] assert(PACKET_LENGTH_BITS % codeParams.numBitsPerCodingStep == 0) # decide on symbol schedule numCodingSteps = PACKET_LENGTH_BITS / codeParams.numBitsPerCodingStep numSymbolsForCodingStep = numpy.ones([numCodingSteps], dtype=numpy.uint32) numSymbolsForCodingStep *= NUM_PASSES numSymbolsForCodingStep[-1] *= NUM_LAST_CODE_STEP_SYMBOLS fullPassLength = numCodingSteps - 1 + NUM_LAST_CODE_STEP_SYMBOLS # uncached C++ encoder uncachedEncoder = spinal.SalsaEncoder( codeParams.numBitsPerCodingStep, numCodingSteps, spinal.StridedPuncturingSchedule(numCodingSteps, NUM_LAST_CODE_STEP_SYMBOLS), codeParams.symbolSizeBits) # cached C++ encoder cachedEncoder = spinal.CachedSalsaEncoder( uncachedEncoder, NUM_PACKETS, NUM_CACHED_PASSES * fullPassLength) for i in xrange(NUM_PACKETS): cachedEncoder.setPacket(i, packets[i]) for compInd in xrange(NUM_COMPARISONS): packetInd = random.randint(0, NUM_PACKETS-1) firstSymbolInd = random.randint(0, NUM_PASSES*fullPassLength - 1) lastSymbolInd = random.randint(firstSymbolInd, NUM_PASSES*fullPassLength - 1) # encode using uncached uncachedSymbols = numpy.ones([lastSymbolInd - firstSymbolInd + 1], dtype=numpy.int32) * 0x7F7F7F7F uncachedEncoder.encode(packets[packetInd], firstSymbolInd, lastSymbolInd, uncachedSymbols) # encode using cached cachedSymbols = numpy.ones([lastSymbolInd - firstSymbolInd + 1], dtype=numpy.int32) * 0x77777777 cachedEncoder.getSymbols(packetInd, firstSymbolInd, lastSymbolInd, cachedSymbols) # compare self.assertTrue( (uncachedSymbols == cachedSymbols).all() )
def test_001_Punctured_Encoder(self): PACKET_LENGTH_BITS = 192 NUM_PASSES = 5 NUM_LAST_CODE_STEP_SYMBOLS = 2 codeParams = spinal.reference.CodeParams(salsaNumRounds = 12, hashWordSize = 64, numBitsPerCodingStep = 4, symbolSizeBits = 10, transmittedPointPrecisionBits = 10, lookaheadDepth = 0) # get packet packet = numpy.random.bytes(PACKET_LENGTH_BITS / 8) assert(PACKET_LENGTH_BITS % codeParams.numBitsPerCodingStep == 0) numCodingSteps = PACKET_LENGTH_BITS / codeParams.numBitsPerCodingStep totalNumSymbols = (numCodingSteps - 1 + NUM_LAST_CODE_STEP_SYMBOLS) * NUM_PASSES # decide on symbol schedule cppSpineValueIndices = wireless.vectorus() for codeStep in xrange(numCodingSteps - 1): for i in xrange(NUM_PASSES): cppSpineValueIndices.push_back(codeStep) # For last code step for i in xrange(NUM_PASSES * NUM_LAST_CODE_STEP_SYMBOLS): cppSpineValueIndices.push_back(numCodingSteps - 1) # Make c++ encoder without puncturing cppEncoder = spinal.CodeFactory( codeParams.numBitsPerCodingStep, numCodingSteps, codeParams.symbolSizeBits) \ .salsa().encoder() unpuncturedSymbols = wireless.vectorus() # Encode unpunctured with C++ encoder cppEncoder.setPacket(packet) cppEncoder.encode(cppSpineValueIndices, unpuncturedSymbols) # Make puncturing schedule punc = spinal.StridedPuncturingSchedule(numCodingSteps, NUM_LAST_CODE_STEP_SYMBOLS) # Make encoder with puncturing puncturedEncoder = spinal.PuncturedEncoder(cppEncoder, punc) # Encode with puncturing puncturedSymbols = wireless.vectorus() puncturedEncoder.setPacket(packet) puncturedEncoder.encode(totalNumSymbols, puncturedSymbols) # Make a dictionary from spine value to all locations that are produced # from that spine value d = {} punc.reset() for i in xrange(totalNumSymbols): d.setdefault(punc.next(), []).append(i) # Check the two encoders produced the same symbols (taking into account # the puncturing schedule) for codeStep in xrange(numCodingSteps - 1): for i in xrange(NUM_PASSES): self.assertEqual(unpuncturedSymbols[codeStep * NUM_PASSES + i], puncturedSymbols[ d[codeStep][i] ], "location %d should contain the %dth sample from spine value %d, but does not match" \ % (d[codeStep][i], i, codeStep)) # last codestep codeStep = (numCodingSteps - 1) for i in xrange(NUM_PASSES * NUM_LAST_CODE_STEP_SYMBOLS): self.assertEqual(unpuncturedSymbols[codeStep * NUM_PASSES + i], puncturedSymbols[ d[codeStep][i] ], "location %d should contain the %dth sample from spine value %d, but does not match" \ % (d[codeStep][i], i, codeStep))