# 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 base64
import time
import numpy as np
from thrifty.signal_utils import Signal
def card_reader(stream):
    """Read blocks from .card file.

    A .card ("CARrier Detection") file generally contains blocks of data for
    which a carrier has been detected.

    Parameters
    ----------
    stream : file-like object

    Yields
    ------
    timestamp : float
        Approximate time at which the data was captured.
    block_idx : int
        Block index, starting from zero for the first block that was captured.
    data : :class:`numpy.ndarray`
        Complex-valued array with the block's samples.
    """
    while True:
        line = stream.readline()
        if len(line) == 0:
            break
        if line[0] == '#' or line[0] == '\n':
            continue
        if line.startswith('Using Volk machine:') or line.startswith('linux;'):
            continue
        timestamp, idx, encoded = line.rstrip('\n').split(' ')
        raw = np.fromstring(base64.b64decode(encoded), dtype='uint8')
        data = raw_to_complex(raw)

# 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/block_data.py
import base64
import time
import numpy as np
from thrifty.signal_utils import Signal

def card_reader(stream):
    """Read blocks from .card file.

    A .card ("CARrier Detection") file generally contains blocks of data for
    which a carrier has been detected.

    Parameters
    ----------
    stream : file-like object

    Yields
    ------
    timestamp : float
        Approximate time at which the data was captured.
    block_idx : int
        Block index, starting from zero for the first block that was captured.
    data : :class:`numpy.ndarray`
        Complex-valued array with the block's samples.
    """
    while True:
        line = stream.readline()
        if len(line) == 0:
            break
        if line[0] == '#' or line[0] == '\n':
            continue
        if line.startswith('Using Volk machine:') or line.startswith('linux;'):
            continue
        timestamp, idx, encoded = line.rstrip('\n').split(' ')
        raw = np.fromstring(base64.b64decode(encoded), dtype='uint8')
        data = raw_to_complex(raw)

        yield float(timestamp), int(idx), Signal(data)