# Path: thrifty/toads_data.py
# class DetectionResult(object):
#     def __init__(self, timestamp, block, soa, carrier_info, corr_info,
#                  rxid=None, txid=None):
#     def serialize(self):
#     def deserialize(cls, string, with_rxid=False, with_txid=False):
# def _load_toads(stream, with_rxid=True, with_txid=True):
# def load_toad(stream):
# def load_toads(stream):
# def toads_array(detections, with_ids=True):
# 
# Path: thrifty/signal_utils.py
# class Signal(np.ndarray):
#     """Representation of signal with caching and on-demand calculations."""
#     # Based on https://docs.scipy.org/doc/numpy/user/basics.subclassing.html
# 
#     def __new__(cls, input_array, fft=None, ifft=None):
#         obj = np.asarray(input_array).view(cls)
# 
#         if fft is not None:
#             obj._fft = fft
#             fft._ifft = obj
# 
#         if ifft is not None:
#             obj._ifft = ifft
#             ifft._fft = obj
# 
#         return obj
# 
#     def __array_finalize__(self, obj):
#         if obj is None:
#             return
#         self._reset()
# 
#     def __array_wrap__(self, obj, context=None):
#         # http://stackoverflow.com/questions/19223926/
# 
#         if obj.shape == ():
#             return obj[()]  # if ufunc output is scalar, return it
#         else:
#             ret = np.ndarray.__array_wrap__(self, obj, context)
#             if isinstance(ret, Signal):
#                 ret._reset()
#             return ret
# 
#     def _reset(self):
#         self._fft = None
#         self._ifft = None
#         self._rms = None
#         self._mag = None
#         self._power = None
#         self._conj = None
# 
#     @property
#     def fft(self):
#         """FFT of signal."""
#         if self._fft is None:
#             self._fft = Signal(compute_fft(self), ifft=self)
#         return self._fft
# 
#     @property
#     def ifft(self):
#         """IFFT of signal."""
#         if self._ifft is None:
#             self._ifft = Signal(compute_ifft(self), fft=self)
#         return self._ifft
# 
#     @property
#     def mag(self):
#         """Calculate the magnitude of the samples."""
#         if self._mag is None:
#             if self._power is not None:
#                 self._mag = np.sqrt(self._power)
#             else:
#                 self._mag = np.abs(self)  # TODO: Convert to Signal?
#         return self._mag
# 
#     @property
#     def power(self):
#         """Calculate the power (magnitude^2) of the samples."""
#         if self._power is None:
#             # Alternative: self._power = (samples * np.conj(samples)).real
#             # or: self._power = np.abs(self)**2
#             self._power = self.mag**2
#         return self._power
# 
#     @property
#     def rms(self):
#         """Calculate signal's root mean square value."""
#         if self._rms is None:
#             if self._fft is not None and self._fft._rms is not None:
#                 self._rms = self._fft._rms / np.sqrt(len(self))
#             elif self._ifft is not None and self._ifft._rms is not None:
#                 self._rms = self._ifft._rms * np.sqrt(len(self))
#             else:
#                 self._rms = np.sqrt(np.mean(self.power))
#         return self._rms
# 
#     @property
#     def conj(self):
#         """Calculate and cache the complex conjugate."""
#         if self._conj is None:
#             self._conj = np.ndarray.conj(self)
#             self._conj._conj = self
#         return self._conj


import logging
import numpy as np
from thrifty import toads_data
from thrifty.signal_utils import Signal

class SoaEstimator(object):
    """A SoA estimator that uses the default algorithms.

    The default despreader, detector and interpolator algorithms will be used:
     - Despreader: correlate using FFT
     - Detector: Simple threshold detector.
     - Interpolator: Parabolic interpolator.

    Parameters
    ----------
    template : :class:`numpy.ndarray`
        Template signal.
    thresh_coeffs : (float, float, float) tuple
        Coefficients of threshold formula: (constant, snr, stddev).
    block_len : int
        Size of data blocks.
    history_len : int
        Number of samples at the end of each block that are repeated in the
        next block.
    """

    def __init__(self, template, thresh_coeffs, block_len, history_len):
        self.template = Signal(template)
        self.template_energy = np.sum(self.template.power)

        template_len = len(template)
        self.corr_len = block_len - template_len + 1
        self.template_padded = np.concatenate([self.template,
                                               np.zeros(self.corr_len-1)])

# Path: thrifty/toads_data.py
# class DetectionResult(object):
#     def __init__(self, timestamp, block, soa, carrier_info, corr_info,
#                  rxid=None, txid=None):
#     def serialize(self):
#     def deserialize(cls, string, with_rxid=False, with_txid=False):
# def _load_toads(stream, with_rxid=True, with_txid=True):
# def load_toad(stream):
# def load_toads(stream):
# def toads_array(detections, with_ids=True):
# 
# Path: thrifty/signal_utils.py
# class Signal(np.ndarray):
#     """Representation of signal with caching and on-demand calculations."""
#     # Based on https://docs.scipy.org/doc/numpy/user/basics.subclassing.html
# 
#     def __new__(cls, input_array, fft=None, ifft=None):
#         obj = np.asarray(input_array).view(cls)
# 
#         if fft is not None:
#             obj._fft = fft
#             fft._ifft = obj
# 
#         if ifft is not None:
#             obj._ifft = ifft
#             ifft._fft = obj
# 
#         return obj
# 
#     def __array_finalize__(self, obj):
#         if obj is None:
#             return
#         self._reset()
# 
#     def __array_wrap__(self, obj, context=None):
#         # http://stackoverflow.com/questions/19223926/
# 
#         if obj.shape == ():
#             return obj[()]  # if ufunc output is scalar, return it
#         else:
#             ret = np.ndarray.__array_wrap__(self, obj, context)
#             if isinstance(ret, Signal):
#                 ret._reset()
#             return ret
# 
#     def _reset(self):
#         self._fft = None
#         self._ifft = None
#         self._rms = None
#         self._mag = None
#         self._power = None
#         self._conj = None
# 
#     @property
#     def fft(self):
#         """FFT of signal."""
#         if self._fft is None:
#             self._fft = Signal(compute_fft(self), ifft=self)
#         return self._fft
# 
#     @property
#     def ifft(self):
#         """IFFT of signal."""
#         if self._ifft is None:
#             self._ifft = Signal(compute_ifft(self), fft=self)
#         return self._ifft
# 
#     @property
#     def mag(self):
#         """Calculate the magnitude of the samples."""
#         if self._mag is None:
#             if self._power is not None:
#                 self._mag = np.sqrt(self._power)
#             else:
#                 self._mag = np.abs(self)  # TODO: Convert to Signal?
#         return self._mag
# 
#     @property
#     def power(self):
#         """Calculate the power (magnitude^2) of the samples."""
#         if self._power is None:
#             # Alternative: self._power = (samples * np.conj(samples)).real
#             # or: self._power = np.abs(self)**2
#             self._power = self.mag**2
#         return self._power
# 
#     @property
#     def rms(self):
#         """Calculate signal's root mean square value."""
#         if self._rms is None:
#             if self._fft is not None and self._fft._rms is not None:
#                 self._rms = self._fft._rms / np.sqrt(len(self))
#             elif self._ifft is not None and self._ifft._rms is not None:
#                 self._rms = self._ifft._rms * np.sqrt(len(self))
#             else:
#                 self._rms = np.sqrt(np.mean(self.power))
#         return self._rms
# 
#     @property
#     def conj(self):
#         """Calculate and cache the complex conjugate."""
#         if self._conj is None:
#             self._conj = np.ndarray.conj(self)
#             self._conj._conj = self
#         return self._conj


# Path: thrifty/soa_estimator.py
import logging
import numpy as np
from thrifty import toads_data
from thrifty.signal_utils import Signal


class SoaEstimator(object):
    """A SoA estimator that uses the default algorithms.

    The default despreader, detector and interpolator algorithms will be used:
     - Despreader: correlate using FFT
     - Detector: Simple threshold detector.
     - Interpolator: Parabolic interpolator.

    Parameters
    ----------
    template : :class:`numpy.ndarray`
        Template signal.
    thresh_coeffs : (float, float, float) tuple
        Coefficients of threshold formula: (constant, snr, stddev).
    block_len : int
        Size of data blocks.
    history_len : int
        Number of samples at the end of each block that are repeated in the
        next block.
    """

    def __init__(self, template, thresh_coeffs, block_len, history_len):
        self.template = Signal(template)
        self.template_energy = np.sum(self.template.power)

        template_len = len(template)
        self.corr_len = block_len - template_len + 1
        self.template_padded = np.concatenate([self.template,
                                               np.zeros(self.corr_len-1)])

        self.template_padded = Signal(self.template_padded)