Skip to content

Commit

Permalink
Introduce OTFS
Browse files Browse the repository at this point in the history
  • Loading branch information
adlerjan committed Feb 27, 2024
1 parent e9148df commit 4ae4a30
Show file tree
Hide file tree
Showing 14 changed files with 338 additions and 17 deletions.
4 changes: 4 additions & 0 deletions docssource/api/modem.waveform._table.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
- Yes
- Yes
- Yes
* - :doc:`OTFS<modem.waveforms.orthogonal.otfs>`
- Yes
- Yes
- Yes
* - :doc:`Chirp FSK<modem.waveform.chirp_fsk>`
- Yes
- No
Expand Down
3 changes: 1 addition & 2 deletions docssource/api/modem.waveforms.orthogonal.ocdm.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ Orthogonal Chirp Division Multiplexing (OCDM) is a method of encoding
digital data into multiple orthogonal chirps, i.e. waveforms with zero
cross-correlation.


Considering a simplex-link scenario of two modems communicating over a 3GPP 5G TDL channel

.. literalinclude:: ../scripts/examples/modem_waveforms_ocdm.py
Expand All @@ -26,7 +25,7 @@ grid onto which the transmitted data and pilot symbols are placed:
:lines: 23-37

The grid considers :math:`128` orthogonal subcarriers each modulated with a unique symbol,
with `128` repetitions in time-domain, so that overall :math:`16384` symbols are transmitted per frame.
with :math:`128` repetitions in time-domain, so that overall :math:`16384` symbols are transmitted per frame.
The grid alternates between two types of symbol sections, one carrying a reference element on every :math:`8`-th subcarrier
and one consisting only of data symbols.

Expand Down
43 changes: 43 additions & 0 deletions docssource/api/modem.waveforms.orthogonal.otfs.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
====
OTFS
====

.. inheritance-diagram:: hermespy.modem.waveforms.orthogonal.otfs.OTFSWaveform
:parts: 1

Orthogonal Time Frequency Space (OTFS) modulation is a modulation scheme that is designed to provide high spectral efficiency and low latency.
It is particularly well suited for high mobility scenarios, such as satellite and mobile communications.
Within HermesPy, OTFS is implemented as type of :class:`OrthogonalWaveform<hermespy.modem.waveforms.orthogonal.waveform.OrthogonalWaveform>`
and a precoding of :class:`OFDM<hermespy.modem.waveforms.orthogonal.ofdm.OFDMWaveform>`.

Considering a simplex-link scenario of two modems communicating over a 3GPP 5G TDL channel

.. literalinclude:: ../scripts/examples/modem_waveforms_otfs.py
:language: python
:linenos:
:lines: 10-21

configuring an OTFS waveform requires the specification of the resource-time
grid onto which the transmitted data and pilot symbols are placed:

.. literalinclude:: ../scripts/examples/modem_waveforms_otfs.py
:language: python
:linenos:
:lines: 23-37

The grid considers :math:`128` orthogonal subcarriers each modulated with a unique symbol,
with :math:`128` repetitions in time-domain, so that overall :math:`16384` symbols are transmitted per frame.
The grid alternates between two types of symbol sections, one carrying a reference element on every :math:`8`-th subcarrier
and one consisting only of data symbols.

Additionally, post-processing routines for channel estimation and channel equalization
may be specified on the waveform level

.. literalinclude:: ../scripts/examples/modem_waveforms_otfs.py
:language: python
:linenos:
:lines: 39-45

.. autoclass:: hermespy.modem.waveforms.orthogonal.otfs.OTFSWaveform

.. footbibliography::
1 change: 1 addition & 0 deletions docssource/api/modem.waveforms.orthogonal.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ and has to be replaced by one of the available implementations such as OFDM and

modem.waveforms.orthogonal.ofdm
modem.waveforms.orthogonal.ocdm
modem.waveforms.orthogonal.otfs
modem.waveforms.orthogonal.OrthogonalWaveform
modem.waveforms.orthogonal.GridElement
modem.waveforms.orthogonal.GridResource
Expand Down
2 changes: 1 addition & 1 deletion docssource/notebooks/channel.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
"It is expected to return a four-dimensional numpy tensor modeling a channel impulse sampled *num_samples* times at frequency *sampling_rate*.\n",
"The first tensor dimension denotes the number of time-domain impulse response samples, the second and third dimension the number of transmit and receive antennas, and the fourth dimension the impulse response of each sample instance, respectively.\n",
"\n",
"We can now plug the newly generated channel model into a simulation scenario evaluating an [OFDM waveform](../api/modem.waveform.ofdm.OFDMWaveform.rst) with access to ideal channel state information, equalizing the channel by [zero forcing](../api/modem.waveform.ofdm.OFDMZeroForcingChannelEqualization.rst):"
"We can now plug the newly generated channel model into a simulation scenario evaluating an [OFDM waveform](../api/modem.waveforms.orthogonal.ofdm.OFDMWaveform.rst) with access to ideal channel state information, equalizing the channel by [zero forcing](../api/modem.waveform.ZeroForcingChannelEqualization.rst):"
]
},
{
Expand Down
49 changes: 49 additions & 0 deletions docssource/scripts/examples/modem_waveforms_otfs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-

import matplotlib.pyplot as plt

from hermespy.channel import MultipathFading5GTDL, TDLType
from hermespy.modem import OTFSWaveform, OrthogonalLeastSquaresChannelEstimation, PilotSection, CorrelationSynchronization, ZeroForcingChannelEqualization, PrefixType, SimplexLink, GridResource, GridElement, GuardSection, ElementType, SymbolSection
from hermespy.simulation import Simulation


# Initialize a simulation with two dedicated devices for transmission and reception
carrier_frequency = 3.7e9
simulation = Simulation()
tx_device = simulation.new_device(carrier_frequency=carrier_frequency)
rx_device = simulation.new_device(carrier_frequency=carrier_frequency)

# Assume a 5G TDL channel model
channel = MultipathFading5GTDL(TDLType.A, 1e-7, doppler_frequency=10)
simulation.set_channel(tx_device, rx_device, channel)

# Link the devices
link = SimplexLink(tx_device, rx_device)

# Configure an orthogonal waveform featuring 128 subcarriers
grid_resources = [
GridResource(16, PrefixType.CYCLIC, .1, [GridElement(ElementType.DATA, 7), GridElement(ElementType.REFERENCE, 1)]),
GridResource(128, PrefixType.CYCLIC, .1, [GridElement(ElementType.DATA, 1)]),
]
grid_structure = [
SymbolSection(64, [0, 1])
]
waveform = OTFSWaveform(
grid_resources=grid_resources,
grid_structure=grid_structure,
num_subcarriers=128,
subcarrier_spacing=1e3,
)
link.waveform = waveform

# Configure channel estimation and equalization
waveform.channel_estimation = OrthogonalLeastSquaresChannelEstimation()
waveform.channel_equalization = ZeroForcingChannelEqualization()

# Configure frame synchronization
waveform.pilot_section = PilotSection()
waveform.synchronization = CorrelationSynchronization()

# Visualize the resource grid
waveform.plot_grid()
plt.show()
3 changes: 3 additions & 0 deletions hermespy/modem/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
OCDMWaveform,
OFDMCorrelationSynchronization,
OFDMWaveform,
OTFSWaveform,
PilotSection,
SchmidlCoxPilotSection,
SchmidlCoxSynchronization,
Expand Down Expand Up @@ -158,10 +159,12 @@
"OrthogonalZeroForcingChannelEqualization",
"OrthogonalWaveform",
"OCDMWaveform",
"OTFSWaveform",
"OFDMCorrelationSynchronization",
"SchmidlCoxSynchronization",
"ReferencePosition",
"Synchronization",
"OTFSWaveform",
"Alamouti",
"Ganesan",
"SymbolPrecoding",
Expand Down
4 changes: 1 addition & 3 deletions hermespy/modem/waveform.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,13 +244,11 @@ class CommunicationWaveform(ABC, Serializable):
symbol_type: type = np.complex_
"""Symbol type."""

# Modem this waveform generator is attached to
__modem: Optional[BaseModem]
__synchronization: Synchronization # Synchronization routine
__channel_estimation: ChannelEstimation # Channel estimation routine
__channel_equalization: ChannelEqualization # Channel equalization routine
__oversampling_factor: int # Oversampling factor
# Cardinality of the set of communication symbols
__modulation_order: int

def __init__(
Expand All @@ -273,7 +271,7 @@ def __init__(
modulation_order (int, optional):
Order of modulation.
Must be a non-negative power of two.
s
channel_estimation (ChannelEstimation, optional):
Channel estimation algorithm. If not specified, an ideal channel is assumed.
Expand Down
2 changes: 2 additions & 0 deletions hermespy/modem/waveforms/orthogonal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
SchmidlCoxPilotSection,
SchmidlCoxSynchronization,
)
from .otfs import OTFSWaveform
from .waveform import (
ElementType,
GuardSection,
Expand Down Expand Up @@ -45,6 +46,7 @@
"PilotSection",
"SchmidlCoxPilotSection",
"SchmidlCoxSynchronization",
"OTFSWaveform",
"ElementType",
"GuardSection",
"GridElement",
Expand Down
49 changes: 49 additions & 0 deletions hermespy/modem/waveforms/orthogonal/otfs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-

from __future__ import annotations

import numpy as np
from scipy.fft import fft, ifft

from .ofdm import OFDMWaveform

__author__ = "Jan Adler"
__copyright__ = "Copyright 2024, Barkhausen Institut gGmbH"
__credits__ = ["Jan Adler"]
__license__ = "AGPLv3"
__version__ = "1.2.0"
__maintainer__ = "Jan Adler"
__email__ = "jan.adler@barkhauseninstitut.org"
__status__ = "Prototype"


class OTFSWaveform(OFDMWaveform):
"""Orthogonal Time Frequency Space (OTFS) waveform."""

def _forward_transformation(self, symbol_grid: np.ndarray) -> np.ndarray:
# Initial step: ISFFT
delay_doppler_symbols = fft(ifft(symbol_grid, axis=-1, norm="ortho"), axis=-2, norm="ortho")

# Second step: Heisenberg transform, i.e. the regular OFDM treatment
sample_sections = OFDMWaveform._forward_transformation(self, delay_doppler_symbols)

return sample_sections

def _backward_transformation(
self, sample_sections: np.ndarray, normalize: bool = True
) -> np.ndarray:
# Initial step: Inverse Heisenberg transform, i.e. the regular OFDM treatment
delay_doppler_symbols = OFDMWaveform._backward_transformation(
self, sample_sections, normalize,
)

# Second step: SFFT
symbol_grid = ifft(
fft(delay_doppler_symbols, axis=-1, norm="ortho"), axis=-2, norm="ortho"
)

# Normalize the symbol grid
if normalize:
symbol_grid /= np.sqrt(np.prod(symbol_grid.shape[:-2]))

return symbol_grid
Loading

0 comments on commit 4ae4a30

Please sign in to comment.