Beispiel #1
0
    def test_synchronize(self) -> None:
        """Default synchronization routine should properly split signals into frame-sections."""

        num_streams = 3
        num_samples_test = [50, 100, 150, 200]

        for num_samples in num_samples_test:

            signal = np.exp(
                2j * self.rnd.uniform(0, pi, (num_streams, 1))) @ np.exp(
                    2j * self.rnd.uniform(0, pi, (1, num_samples)))
            channel_state = ChannelStateInformation.Ideal(
                num_samples, num_streams)

            synchronized_frames = self.waveform_generator.synchronization.synchronize(
                signal, channel_state)

            # Number of frames is the number of frames that fit into the samples
            num_frames = len(synchronized_frames)
            expected_num_frames = int(
                floor(num_samples / self.waveform_generator.samples_in_frame))
            self.assertEqual(expected_num_frames, num_frames)

            # Frames and channel states should each contain the correct amount of samples
            for frame_signal, frame_channel_state in synchronized_frames:

                self.assertEqual(num_streams, frame_signal.shape[0])
                self.assertEqual(self.waveform_generator.samples_in_frame,
                                 frame_signal.shape[1])
                self.assertEqual(self.waveform_generator.samples_in_frame,
                                 frame_channel_state.num_samples)
    def test_synchronization(self) -> None:
        """Synchronization should properly partition signal samples into frame sections."""

        # Generate frame signal models
        num_samples = 2 * self.max_offset + self.num_frames * self.waveform.samples_in_frame
        csi = ChannelStateInformation.Ideal(num_samples)
        samples = np.zeros((1, num_samples), dtype=complex)
        expected_frames = []
        pilot_indices = self.rng.integers(
            0, self.max_offset, self.num_frames) + np.arange(
                self.num_frames) * self.waveform.samples_in_frame

        for p in pilot_indices:

            data_symbols = Symbols(
                self.rng.integers(0, self.waveform.modulation_order,
                                  self.waveform.symbols_per_frame))
            signal_samples = self.waveform.modulate(data_symbols).samples

            samples[:, p:p + self.waveform.samples_in_frame] += signal_samples
            expected_frames.append(samples[:, p:p +
                                           self.waveform.samples_in_frame])

        synchronized_frames = self.synchronization.synchronize(samples, csi)

        if len(synchronized_frames) != len(expected_frames):
            self.fail()

        for expected_frame, (synchronized_frame,
                             _) in zip(expected_frames, synchronized_frames):
            assert_array_equal(expected_frame, synchronized_frame)
Beispiel #3
0
    def test_synchronize(self) -> None:
        """Test the proper estimation of delays during Schmidl-Cox synchronization"""

        for d, n in product(self.delays_in_samples, self.num_frames):

            symbols = np.exp(
                2j * pi * self.rng.uniform(0, 1,
                                           (n, self.frame.symbols_per_frame)))
            frames = [
                np.exp(2j * pi * self.rng.uniform(0, 1, (self.num_streams, 1)))
                @ self.frame.modulate(Symbols(symbols[f, :])).samples
                for f in range(n)
            ]

            signal = np.empty((self.num_streams, 0), dtype=complex)
            for frame in frames:

                signal = np.concatenate(
                    (signal, np.zeros(
                        (self.num_streams, d), dtype=complex), frame),
                    axis=1)

            channel_state = ChannelStateInformation.Ideal(
                len(signal), self.num_streams)

            synchronization = self.synchronization.synchronize(
                signal, channel_state)

            self.assertEqual(n, len(synchronization))
            for frame, (synchronized_frame, _) in zip(frames, synchronization):
                assert_array_equal(frame, synchronized_frame)
Beispiel #4
0
    def test_modulate_demodulate_no_dc_suppression(self) -> None:
        """Modulating and subsequently de-modulating an OFDM symbol section should yield correct symbols."""

        expected_symbols = np.exp(
            2j * self.rnd.uniform(0, pi, self.section.num_symbols))

        modulated_signal = self.section.modulate(expected_symbols)
        channel_state = ChannelStateInformation.Ideal(
            num_samples=modulated_signal.shape[0])
        ofdm_grid, _ = self.section.demodulate(modulated_signal, channel_state)

        symbols = ofdm_grid.T[self.section.resource_mask[
            ElementType.DATA.value].T]
        assert_array_almost_equal(expected_symbols, symbols)
Beispiel #5
0
    def test_synchronize(self) -> None:
        """Default synchronization should properly split signals into frame-sections."""

        num_streams = 3
        num_frames = 5
        num_offset_samples = 2
        num_samples = num_frames * self.waveform_generator.samples_in_frame + num_offset_samples

        signal = np.exp(
            2j * self.rng.uniform(0, pi, (num_streams, 1))) @ np.exp(
                2j * self.rng.uniform(0, pi, (1, num_samples)))
        csi = ChannelStateInformation.Ideal(num_samples, num_streams)

        frames = self.synchronization.synchronize(signal, csi)
        self.assertEqual(num_frames, len(frames))
Beispiel #6
0
    def test_modulate_demodulate(self) -> None:
        """Modulating and subsequently de-modulating a data frame should yield identical symbols."""

        expected_symbols = Symbols(
            np.exp(2j *
                   self.rng.uniform(0, pi, self.generator.symbols_per_frame)) *
            np.arange(1, 1 + self.generator.symbols_per_frame))

        baseband_signal = self.generator.modulate(expected_symbols)
        channel_state = ChannelStateInformation.Ideal(
            num_samples=baseband_signal.num_samples)
        symbols, _, _ = self.generator.demodulate(
            baseband_signal.samples[0, :], channel_state)

        assert_array_almost_equal(expected_symbols.raw, symbols.raw)
Beispiel #7
0
    def test_reference_based_channel_estimation(self) -> None:
        """Reference-based channel estimation should properly estimate channel at reference points."""

        self.generator.channel_estimation_algorithm = ChannelEstimation.REFERENCE

        expected_bits = self.rng.integers(0, 2, self.generator.bits_per_frame)
        expected_symbols = self.generator.map(expected_bits)
        signal = self.generator.modulate(expected_symbols)
        expected_csi = ChannelStateInformation.Ideal(signal.num_samples)

        symbols, csi, _ = self.generator.demodulate(signal.samples[0, :],
                                                    expected_csi)

        assert_array_almost_equal(np.ones(csi.state.shape, dtype=complex),
                                  csi.state)
    def test_rx_signal_properly_demodulated(self) -> None:
        """Verify the proper demodulation of received signals."""

        rx_signal = self.__read_saved_results_from_file('rx_signal.npy')
        channel_state = ChannelStateInformation.Ideal(
            num_samples=rx_signal.shape[0])
        noise_variance = 0.0

        received_symbols, _, _ = self.generator.demodulate(
            rx_signal, channel_state, noise_variance)
        received_bits = self.generator.unmap(received_symbols)

        received_bits_expected = self.__read_saved_results_from_file(
            'received_bits.npy').ravel()
        np.testing.assert_array_equal(
            received_bits[:len(received_bits_expected)],
            received_bits_expected)
    def test_decode_validation(self) -> None:
        """Decoding should result in a RuntimeError, if multiple streams result."""

        num_samples = 10
        num_streams = 2

        precoder = Mock()
        precoder.decode = lambda symbols, channels, noise: (symbols, channels,
                                                            noise)

        self.precoding[0] = precoder
        input_symbols = self.generator.random((num_streams, num_samples))
        input_channel = ChannelStateInformation.Ideal(
            num_samples=num_samples, num_receive_streams=num_streams)
        input_noise = self.generator.random((num_streams, num_samples))

        with self.assertRaises(RuntimeError):
            _ = self.precoding.decode(input_symbols, input_channel,
                                      input_noise)

        with self.assertRaises(ValueError):
            _ = self.precoding.decode(input_symbols[0, :], input_channel,
                                      input_noise)
    def demodulate(
        self, baseband_signal: np.ndarray,
        channel_state: ChannelStateInformation, noise_variance: float
    ) -> Tuple[Symbols, ChannelStateInformation, np.ndarray]:

        # Assess number of frames contained within this signal
        samples_in_chirp = self.samples_in_chirp
        samples_in_pilot_section = samples_in_chirp * self.num_pilot_chirps
        prototypes, _ = self._prototypes()

        data_frame = baseband_signal[samples_in_pilot_section:]

        symbol_signals = data_frame.reshape(-1, self.samples_in_chirp)
        symbol_metrics = abs(symbol_signals @ prototypes.T.conj())

        # ToDo: Unfortunately the demodulation-scheme is non-linear. Is there a better way?
        symbols = np.argmax(symbol_metrics, axis=1)
        channel_state = ChannelStateInformation.Ideal(
            len(symbols), channel_state.num_transmit_streams,
            channel_state.num_receive_streams)
        noises = np.repeat(noise_variance, self.num_data_chirps)

        return Symbols(symbols), channel_state, noises
Beispiel #11
0
    def receive(self) -> Tuple[Signal, Symbols, np.ndarray]:

        signal = self._receiver.signal.resample(self.waveform_generator.sampling_rate)
        if signal is None:
            raise RuntimeError("No signal received by modem")
            # signal = Signal.empty(sampling_rate=self.device.sampling_rate)

        csi = self._receiver.csi
        if csi is None:
            csi = ChannelStateInformation.Ideal(signal.num_samples)

        # Workaround for non-matching csi and signal model pairs
        elif signal.num_samples > (csi.num_samples + csi.num_delay_taps - 1):
            csi = ChannelStateInformation.Ideal(signal.num_samples)

        # Pull signal and channel state from the registered device slot
        noise_power = signal.noise_power
        num_samples = signal.num_samples

        # Number of frames within the received samples
        frames_per_stream = int(floor(num_samples / self.waveform_generator.samples_in_frame))

        # Number of code bits required to generate all frames for all streams
        num_code_bits = int(self.waveform_generator.bits_per_frame * frames_per_stream / self.precoding.rate)

        # Data bits required by the bit encoder to generate the input bits for the waveform generator
        num_data_bits = self.encoder_manager.required_num_data_bits(num_code_bits)

        # Apply stream decoding, for instance beam-forming
        # TODO: Not yet supported.

        # Synchronize all streams into frames
        synchronized_frames = self.waveform_generator.synchronization.synchronize(signal.samples, csi)

        # Abort at this point if no frames have been detected
        if len(synchronized_frames) < 1:
            return signal, Symbols(), np.empty(0, dtype=complex)
        
        # Demodulate signal frame by frame
        decoded_raw_symbols = np.empty(0, dtype=complex)
        for frame_samples, frame_csi in synchronized_frames:
            
            stream_symbols: List[np.ndarray] = []
            stream_csis: List[ChannelStateInformation] = []
            stream_noises: List[np.ndarray] = []
            
            # Demodulate each stream within each frame independently
            for stream_samples, stream_csi in zip(frame_samples, frame_csi.received_streams()):
                
                symbols, csi, noise_powers = self.waveform_generator.demodulate(stream_samples, stream_csi, noise_power)
                stream_symbols.append(symbols.raw)
                stream_csis.append(csi)
                stream_noises.append(noise_powers)
                
            frame_symbols = np.array(stream_symbols, dtype=complex)
            frame_csi = ChannelStateInformation.concatenate(stream_csis,
                                                            ChannelStateDimension.RECEIVE_STREAMS)
            frame_noises = np.array(stream_noises, dtype=float)
            
            decoded_frame_symbols = self.precoding.decode(frame_symbols, frame_csi, frame_noises)
            decoded_raw_symbols = np.append(decoded_raw_symbols, decoded_frame_symbols)

        # Convert decoded symbols to from array to symbols
        decoded_symbols = Symbols(decoded_raw_symbols)

        # Map the symbols to code bits
        code_bits = self.waveform_generator.unmap(decoded_symbols)

        # Decode the coded bit stream to plain data bits
        data_bits = self.encoder_manager.decode(code_bits, num_data_bits)

        # Cache receptions
        self.__received_bits = data_bits
        self.__received_symbols = decoded_symbols

        # We're finally done, blow the fanfares, throw confetti, etc.
        return signal, decoded_symbols, data_bits
Beispiel #12
0
    def test_demodulate(self) -> None:
        """Demodulation should return an empty tuple."""

        _ = self.section.demodulate(np.empty(0, dtype=complex),
                                    ChannelStateInformation.Ideal(0))