Skip to main content

Otfs

Struct Otfs 

Source
pub struct Otfs {
    pub n_fft: usize,
    pub n_symbols: usize,
    pub n_cp: usize,
    pub sample_rate: Hertz,
}
Expand description

An OTFS modem: n_fft delay bins $M$ (the subcarriers), n_symbols Doppler bins $N$ (the symbols of a frame) and one prefix of n_cp samples per frame (see the module).

Fields§

§n_fft: usize

Delay bins $M$, the subcarriers of the Heisenberg transform.

§n_symbols: usize

Doppler bins $N$, the symbols of a frame.

§n_cp: usize

The cyclic prefix of a frame, in samples (OTFS has no oversampling: one sample per delay bin).

§sample_rate: Hertz

The rate of the samples, $M \Delta f$: the rate of the Signal of Modulate and the rate Demodulate expects; 1 Hz by default (time in samples).

Implementations§

Source§

impl Otfs

Source

pub fn new(n_fft: usize, n_symbols: usize) -> Result<Otfs>

$M$ delay bins and $N$ Doppler bins, no prefix, at the sample rate 1 Hz.

§Errors

Error::Config for an empty grid.

Source

pub fn check(&self) -> Result<()>

Validate the configuration.

§Errors

Error::Config for an empty grid, a prefix longer than the frame or a sample rate that is not positive and finite.

Source

pub fn block_len(&self) -> usize

Samples per frame, $MN$ plus the prefix.

Source

pub fn modulate(&self, x: &[C64]) -> Result<Vec<C64>>

Modulate the frames of x, each $MN$ symbols $x[nM + m] = X[m, n]$ (the columns of $X$ one after the other), with the prefix.

§Errors

Error::Config for an invalid configuration, Error::Shape unless x holds whole frames.

Source

pub fn demodulate(&self, r: &[C64]) -> Result<Vec<C64>>

Demodulate whole frames: drop the prefix, DFT along the Doppler axis.

§Errors

Error::Config for an invalid configuration, Error::Shape unless r holds whole frames.

Source

pub fn effective_channel(&self, paths: &[Path]) -> Result<EffectiveChannel>

The effective delay–Doppler channel of paths at Otfs::sample_rate: the $MN \times MN$ matrix of Theorem 1 of Raviteja et al. (2019), eq. (12), with $z = e^{j 2\pi/(MN)}$ and the Doppler shifts in bins $k_{i} = \nu_{i} MN / f_{s}$ (of either sign),

$$ T^{(i)}(p, q) = \begin{cases} e^{-j 2\pi n/N} z^{k_{i}([m - l_{i}]{M})} & m < l{i}, \\ z^{k_{i}([m - l_{i}]{M})} & m \ge l{i}, \end{cases} \qquad q = [m - l_{i}]{M} + M[n - k{i}]_{N}, $$

$p = nM + m$, zero elsewhere, summed with the gains: one entry per path in every row (EffectiveChannel). The time of a path counts from the first sample of the frame, its prefix included, as in comnum_channel::multipath::DelayDoppler: the gain of eq. (6), whose time starts after the prefix, is $h_{i} e^{j 2\pi k_{i} N_{\mathrm{cp}}/(MN)}$. The model assumes a prefix at least as long as the largest delay.

§Errors

Error::Config for an invalid configuration, a delay of $M$ samples or more, or a Doppler shift that is not a whole number of bins (Otfs::transform_channel takes any channel).

§Examples
use comnum_waveform::otfs::Otfs;
use comnum_core::channel::Path;
use comnum_core::units::Hertz;
use comnum_math::C64;
// M N = 32 samples at 32 kHz: one Doppler bin is 1 kHz
let o = Otfs { sample_rate: Hertz(32e3), ..Otfs::new(8, 4)? };
let h = o.effective_channel(&[Path { gain: C64::new(1.0, 0.0), delay: 1, doppler_hz: 1e3 }])?;
assert_eq!((h.size(), h.n_paths()), (32, 1));
let x: Vec<C64> = (0..32).map(|i| C64::new(i as f64, 0.0)).collect();
let y = h.apply(&x)?;
let dense = h.to_dense();
assert!((0..32).all(|p| ((0..32).map(|q| dense[[p, q]] * x[q]).sum::<C64>() - y[p]).norm() < 1e-12));
Source

pub fn transform_channel(&self, h: &CMat) -> Result<CMat>

The effective channel $(F_{N} \otimes I_{M}) H (F_{N}^{H} \otimes I_{M})$ of a time-domain channel matrix h ($MN \times MN$, acting on the frame without its prefix), for any delays and Doppler shifts (eq. (13)).

$F_{N}^{H} \otimes I_{M}$ is symmetric, so the right product transforms every row of $H$ and the left one every column of the result, each by $M$ FFTs of $N$ points: $O((MN)^{2} \log N)$ operations.

§Errors

Error::Shape unless h is $MN \times MN$.

Trait Implementations§

Source§

impl Clone for Otfs

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for Otfs

Source§

impl Debug for Otfs

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Demodulate for Otfs

OTFS as a Demodulate block: one stream of samples at Otfs::sample_rate.

Source§

fn demodulate_into(&mut self, y: &Signal, out: &mut Symbols) -> Result<()>

§Errors

Error::Config for several streams or a signal at another sample rate, and the errors of Otfs::demodulate.

Source§

fn demodulate(&mut self, y: &Signal) -> Result<Out, Error>
where Out: Default,

Demodulate y (allocates, calls Demodulate::demodulate_into). Read more
Source§

impl Modulate for Otfs

OTFS as a Modulate block: one stream of delay–Doppler symbols into one stream of samples at Otfs::sample_rate.

Source§

fn modulate_into(&self, x: &Symbols, out: &mut Signal) -> Result<()>

Modulate x into out. Read more
Source§

fn modulate(&self, x: &In) -> Result<Signal, Error>

Modulate x (allocates, calls Modulate::modulate_into). Read more
Source§

impl PartialEq for Otfs

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for Otfs

Auto Trait Implementations§

§

impl Forget for Otfs

§

impl Freeze for Otfs

§

impl RefUnwindSafe for Otfs

§

impl Send for Otfs

§

impl Sync for Otfs

§

impl Unpin for Otfs

§

impl UnsafeUnpin for Otfs

§

impl UnwindSafe for Otfs

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.