peregrine/iqgen/bits/tcxo_factory.py
class ObjectFactory(object):
  def __init__(self):
  def toMapForm(self, obj):
  def fromMapForm(self, data):
  def __PolyTcxo_ToMap(self, obj):
  def __SineTcxo_ToMap(self, obj):
  def __MapTo_PolyTcxo(self, data):
  def __MapTo_SineTcxo(self, data):
peregrine/iqgen/bits/satellite_factory.py
class ObjectFactory(object):
  def __init__(self):
  def toMapForm(self, obj):
  def fromMapForm(self, data):
  def __GPSSatellite_ToMap(self, obj):
  def __MapTo_GPSSatellite(self, data):
peregrine/iqgen/bits/tcxo_poly.py
class TCXOPoly(TCXOBase):
  '''
  Polynomial TCXO control class.
  '''

  def __init__(self, coeffs):
    '''
    Constructs TCXO control object.

    Parameters
    ----------
    coeffs : array-like
      Coefficients for TCXO polynome. These coeffificens define a TCXO drift
      over time in ppm.
    '''
    super(TCXOPoly, self).__init__()
    self.coeffs = tuple([x for x in coeffs])
    if coeffs:
      # Recompute drift coefficients from speed of drift into distance of drift
      new_coeffs = []
      power_c = len(coeffs)
      for idx, val in enumerate(coeffs):
        power = power_c - idx
        new_coeffs.append(val * 1e-6 / power)
      new_coeffs.append(0)
      self.poly = numpy.poly1d(new_coeffs)
    else:
      self.poly = None

  def __str__(self, *args, **kwargs):
    '''
    Provides string representation of the object
    '''
    return "TCXOPoly: coeffs=%s" % str(self.coeffs)

  def computeTcxoTime(self, fromSample, toSample, outputConfig):
    '''
    Method generates time vector for the given sample index range depending on
    TCXO behaviour.

    Parameters
    ----------
    fromSample : int
      Index of the first sample.
    toSample: int
      Index of the last sample plus 1.
    outputConfig : object
      Output configuration

    Returns
    -------
    numpy.ndarray(shape=(toSample - fromSample), dtype=numpy.float)
      Vector of the shifted time stamps for the given TCXO controller.
    '''
    poly = self.poly

    if poly:
      time0_s = fromSample / outputConfig.SAMPLE_RATE_HZ
      timeX_s = toSample / outputConfig.SAMPLE_RATE_HZ
      timeAll_s = numpy.linspace(time0_s,
                                 timeX_s,
                                 toSample - fromSample,
                                 endpoint=False,
                                 dtype=numpy.float)
      result = poly(timeAll_s)
    else:
      result = None

    return result
peregrine/iqgen/bits/amplitude_poly.py
class AmplitudePoly(AmplitudeBase):
  '''
  Amplitude control with polynomial dependency over time.
  '''

  def __init__(self, units, coeffs):
    '''
    Constructs polynomial amplitude control object.

    Parameters
    coeffs : array-like
      Polynomial coefficients
    '''
    super(AmplitudePoly, self).__init__(units)

    self.coeffs = tuple([x for x in coeffs])
    if len(coeffs) > 0:
      self.poly = numpy.poly1d(coeffs)
    else:
      self.poly = None

  def __str__(self):
    '''
    Constructs literal presentation of object.

    Returns
    -------
    string
      Literal presentation of object
    '''
    return "AmplitudePoly(units={}, c={})".format(self.units, self.coeffs)

  def applyAmplitude(self, signal, userTimeAll_s, noiseParams):
    '''
    Applies amplitude modulation to signal.

    This method applies polynomial modulation.

    Parameters
    ----------
    signal : numpy.ndarray
      Signal sample vector. Each element defines signal amplitude in range
      [-1; +1]. This vector is modified in place.
    userTimeAll_s : numpy.ndarray
      Sample time vector. Each element defines sample time in seconds.
    noiseParams : NoiseParameters
      Noise parameters to adjust signal amplitude level.

    Returns
    -------
    numpy.ndarray
      Array with output samples
    '''

    poly = self.poly
    if poly is not None:
      amplitudeVector = poly(userTimeAll_s)
      amplitudeVector = AmplitudeBase.convertUnits2Amp(amplitudeVector,
                                                       self.units,
                                                       noiseParams)
      signal *= amplitudeVector
    else:
      amplitude = AmplitudeBase.convertUnits2Amp(1.,
                                                 self.units,
                                                 noiseParams)
      signal *= amplitude

    return signal

  def computeSNR(self, noiseParams):
    '''
    Computes signal to noise ratio in dB.

    noiseParams : NoiseParameters
      Noise parameter container

    Returns
    -------
    float
      SNR in dB
    '''
    poly = self.poly
    if poly is not None:
      value = poly(0.)
    else:
      value = 1.

    return AmplitudeBase.convertUnits2SNR(value, self.units, noiseParams)
peregrine/iqgen/bits/satellite_gps.py
class GPSSatellite(Satellite):
  '''
  GPS satellite object.
  '''

  def __init__(self, prnNo):
    '''
    Constructs satellite object

    Parameters
    ----------
    prnNo : int
      GPS satellite number for selecting PRN.
    '''
    super(GPSSatellite, self).__init__("GPS{}".format(prnNo))
    self.prn = prnNo
    self.l2clCodeType = '01'
    self.l1caCode = GPS_L1CA_Code(prnNo)
    self.l2cCode = GPS_L2C_Code(prnNo, self.l2clCodeType)
    self.l1caEnabled = False
    self.l2cEnabled = False
    self.l1caMessage = DEFAULT_MESSAGE
    self.l2cMessage = DEFAULT_MESSAGE
    self.time0S = 0.
    self.pr0M = 0.
    self.phaseShift = 0.

  def setL1CAEnabled(self, enableFlag):
    '''
    Enables or disable GPS L1 C/A sample generation

    Parameters
    ----------
    enableFlag : boolean
      Flag to enable (True) or disable (False) GPS L1 C/A samples
    '''
    self.l1caEnabled = enableFlag

  def isL1CAEnabled(self):
    '''
    Tests if L1 C/A signal generation is enabled

    Returns
    -------
    bool
      True, when L1 C/A signal generation is enabled, False otherwise
    '''
    return self.l1caEnabled

  def setL2CEnabled(self, enableFlag):
    '''
    Enables or disable GPS L2 C sample generation

    Parameters
    ----------
    enableFlag : boolean
      Flag to enable (True) or disable (False) GPS L2 C samples
    '''
    self.l2cEnabled = enableFlag

  def isL2CEnabled(self):
    '''
    Tests if L2 C signal generation is enabled

    Returns
    -------
    bool
      True, when L2 C signal generation is enabled, False otherwise
    '''
    return self.l2cEnabled

  def setL2CLCodeType(self, clCodeType):
    '''
    Change L1 CL code type if needed

    Parameters
    ----------
    clCodeType : string
      L2 CL code type: '00', '01' or '11'
    '''
    if self.l2clCodeType != clCodeType:
      self.l2cCode = GPS_L2C_Code(self.prn, clCodeType)
      self.l2clCodeType = clCodeType

  def getL2CLCodeType(self):
    '''
    Get L2 CL code type

    Returns
    -------
    string
      L2 CL code type: '00', '01' or '11'
    '''
    return self.l2clCodeType

  def setL1CAMessage(self, message):
    '''
    Configures data source for L1 C/A signal.

    Parameters
    ----------
    message : object
      Message object that will provide symbols for L1 C/A signal.
    '''
    self.l1caMessage = message

  def setL2CMessage(self, message):
    '''
    Configures data source for L2 C signal.

    Parameters
    ----------
    message : object
      Message object that will provide symbols for L2 C signal.
    '''
    self.l2cMessage = message

  def getL1CAMessage(self):
    '''
    Returns configured message object for GPS L1 C/A band

    Returns
    -------
    object
      L1 C/A message object
    '''
    return self.l1caMessage

  def getL2CMessage(self):
    '''
    Returns configured message object for GPS L2 C band

    Returns
    -------
    object
      L2 C message object
    '''
    return self.l2cMessage

  def getBatchSignals(self,
                      userTimeAll_s,
                      samples,
                      outputConfig,
                      noiseParams,
                      band,
                      debug):
    '''
    Generates signal samples.

    Parameters
    ----------
    userTimeAll_s : numpy.ndarray(n_samples, dtype=numpy.float64)
      Vector of observer's timestamps in seconds for the interval start.
    samples : numpy.ndarray((4, n_samples))
      Array to which samples are added.
    outputConfig : object
      Output configuration object.
    noiseParams : NoiseParameters
      Noise parameters object
    band : Band
      Band description object.
    debug : bool
      Debug flag

    Returns
    -------
    list
      Debug information
    '''
    result = []
    if (self.l1caEnabled and band == outputConfig.GPS.L1):
      intermediateFrequency_hz = band.INTERMEDIATE_FREQUENCY_HZ
      values = self.doppler.computeBatch(userTimeAll_s,
                                         self.amplitude,
                                         noiseParams,
                                         signals.GPS.L1CA,
                                         intermediateFrequency_hz,
                                         self.l1caMessage,
                                         self.l1caCode,
                                         outputConfig,
                                         debug)
      numpy.add(samples[band.INDEX],
                values[0],
                out=samples[band.INDEX])
      debugData = {'type': "GPSL1", 'doppler': values[1]}
      result.append(debugData)
    if (self.l2cEnabled and band == outputConfig.GPS.L2):
      intermediateFrequency_hz = band.INTERMEDIATE_FREQUENCY_HZ
      values = self.doppler.computeBatch(userTimeAll_s,
                                         self.amplitude,
                                         noiseParams,
                                         signals.GPS.L2C,
                                         intermediateFrequency_hz,
                                         self.l2cMessage,
                                         self.l2cCode,
                                         outputConfig,
                                         debug)
      numpy.add(samples[band.INDEX],
                values[0],
                out=samples[band.INDEX])
      debugData = {'type': "GPSL2", 'doppler': values[1]}
      result.append(debugData)
    return result

  def isBandEnabled(self, band, outputConfig):
    '''
    Checks if particular band is supported and enabled.

    Parameters
    ----------
    band : Band
      Band description object.
    outputConfig : object
      Output configuration

    Returns:
    bool
      True, if the band is supported and enabled; False otherwise.
    '''
    result = None
    if band == outputConfig.GPS.L1:
      result = self.isL1CAEnabled()
    elif band == outputConfig.GPS.L2:
      result = self.isL2CEnabled()
    else:
      result = False
    return result
peregrine/iqgen/bits/tcxo_sine.py
class TCXOSine(TCXOBase):
  '''
  Sine TCXO control class.
  '''

  def __init__(self, initial_ppm, amplitude_ppm, period_s):
    '''
    Constructs TCXO control object.

    Parameters
    ----------
    initial_ppm : float
      Initial drift in ppm
    amplitude_ppm : float
      Drift amplitude in ppm
    period_s : float
      Drift period in seconds
    '''
    super(TCXOSine, self).__init__()

    self.initial_ppm = initial_ppm
    self.amplitude_ppm = amplitude_ppm
    self.period_s = period_s
    self.c0 = -amplitude_ppm * 1e-6 * self.period_s / (2. * scipy.constants.pi)
    self.c1 = 2. * scipy.constants.pi / period_s
    self.c2 = initial_ppm * 1e-6

  def __str__(self, *args, **kwargs):
    '''
    Provides string representation of the object
    '''
    return "TCXOSine: initial_ppm=%f amplitude_ppm=%f period_s=%f" % \
           (self.initial_ppm, self.amplitude_ppm, self.period_s)

  def computeTcxoTime(self, fromSample, toSample, outputConfig):
    '''
    Method generates time vector for the given sample index range depending on
    TCXO behaviour.

    Parameters
    ----------
    fromSample : int
      Index of the first sample.
    toSample: int
      Index of the last sample plus 1.
    outputConfig : object
      Output configuration

    Returns
    -------
    numpy.ndarray(shape=(toSample - fromSample), dtype=numpy.float)
      Vector of the shifted time stamps for the given TCXO controller.
    '''
    c0 = self.c0
    c1 = self.c1
    c2 = self.c2
    time0_s = fromSample / outputConfig.SAMPLE_RATE_HZ
    timeX_s = toSample / outputConfig.SAMPLE_RATE_HZ

    timeAll_s = numpy.linspace(time0_s,
                               timeX_s,
                               toSample - fromSample,
                               endpoint=False,
                               dtype=numpy.float)

    result = numpy.cos(timeAll_s * c1)
    result += -1.
    result *= c0
    if c2:
      result += timeAll_s * c2

    return result
peregrine/iqgen/bits/message_factory.py
class ObjectFactory(object):
  def __init__(self):
  def toMapForm(self, obj):
  def fromMapForm(self, data):
  def __BlockMessage_ToMap(self, obj):
  def __CNAVMessage_ToMap(self, obj):
  def __ConstMessage_ToMap(self, obj):
  def __LNAVMessage_ToMap(self, obj):
  def __ZeroOneMessage_ToMap(self, obj):
  def __MapTo_BlockMessage(self, data):
  def __MapTo_CNAVMessage(self, data):
  def __MapTo_ConstMessage(self, data):
  def __MapTo_LNAVMessage(self, data):
  def __MapTo_ZeroOneMessage(self, data):
peregrine/iqgen/bits/message_zeroone.py
class Message(object):
  '''
  Message that contains zeros and ones
  '''

  def __init__(self):
    '''
    Constructs object.
    '''
    super(Message, self).__init__()
    self.bits = numpy.asarray([0, 1], dtype=numpy.uint8)

  def __str__(self, *args, **kwargs):
    '''
    Formats object as string literal

    Returns
    -------
    string
      String representation of the object
    '''
    return "ZeroOne"

  def getDataBits(self, dataAll_idx):
    '''
    Generates vector of data bits corresponding to input index

    Parameters
    ----------
    dataAll_idx : numpy.ndarray(dtype=numpy.int64)
      Vector of bit indexes

    Returns
    -------
    numpy.ndarray(dtype=numpy.uint8)
      Vector of data bits
    '''
    # numpy.take degrades performance a lot over time.
    # return numpy.take(self.bits, dataAll_idx , mode='wrap')
    return self.bits[dataAll_idx & 1]
peregrine/iqgen/bits/message_lnav.py
class Message(object):
  '''
  GPS LNAV message generator
  '''

  def __init__(self, prn, tow0=1, n_msg=0, n_prefixBits=50):
    '''
    Constructs message object.

    Parameters
    ----------
    prn : int
      Satellite PRN
    tow0 : int
      Time of week in 6-second units for the first message
    n_msg : int, optional
      Number of messages to pre-generate for output
    n_prefixBits : int, optional
      Number of bits to issue before the first message
    '''
    super(Message, self).__init__()
    self.prn = prn
    self.n_prefixBits = n_prefixBits
    self.n_msg0 = n_msg
    self.tow0 = tow0
    self.messageCount = 0
    self.messageLen = n_prefixBits
    self.nextTow = tow0
    self.nextMsgId = 0
    self.messageBits = numpy.zeros(n_prefixBits, dtype=numpy.uint8)
    words = (n_prefixBits + 29) / 30
    if words:
      tmp = numpy.zeros(words * 30, dtype=numpy.uint8)
      tmp[1::2] = 1
      if words > 1:
        self.updateParity(tmp[0:30])
        for i in range(1, words - 1):
          self.updateParity(tmp[i * 30 - 2: i * 30 + 30])
        self.updateParity(tmp[words * 30 - 32: words * 30], True)
      else:
        self.updateParity(tmp[0: 30], True)
      self.messageBits[:] = tmp[-n_prefixBits:]
    self.a8 = numpy.ndarray(1, dtype=numpy.uint8)
    self.a32 = numpy.ndarray(1, dtype=numpy.dtype('>u4'))
    self.addMessages(n_msg)

  def __str__(self, *args, **kwargs):
    '''
    Formats object as string literal

    Returns
    -------
    string
      String representation of the object
    '''
    return "GPS LNAV: prn=%d pref=%d tow=%d" % \
           (self.prn, self.n_prefixBits, self.nextTow)

  def getDataBits(self, dataAll_idx):
    '''
    Generates vector of data bits corresponding to input index

    Parameters
    ----------
    dataAll_idx : numpy.ndarray(dtype=numpy.int64)
      Vector of bit indexes

    Returns
    -------
    numpy.ndarray(dtype=numpy.uint8)
      Vector of data bits
    '''

    lastIdx = dataAll_idx[-1]
    if lastIdx >= self.messageLen:
      # Grow data bits
      delta = lastIdx - self.messageLen + 1
      newMsgCount = delta / 300
      if delta % 300:
        newMsgCount += 1
      self.addMessages(newMsgCount)

    # numpy.take degrades performance a lot over time.
    # return numpy.take(self.symbolData, dataAll_idx , mode='wrap')
    return self.messageBits[dataAll_idx]

  def addMessages(self, newMsgCount):
    '''
    Generate additional LNAV messages

    This method generates and encodes additional LNAV messages. The message
    contents is added to the internal buffer.

    Parameters
    ----------
    newMsgCount : int
      Number of messages to generate
    '''
    if newMsgCount == 0:
      return

    newMessageLen = newMsgCount * 300 + self.messageLen
    newMessageData = numpy.ndarray(newMessageLen, dtype=numpy.uint8)
    newMessageData[:self.messageLen] = self.messageBits
    for i in range(self.messageLen, newMessageLen, 300):
      logger.info("Generating LNAV message: prn=%d tow=%d msg_id=%d" %
                  (self.prn, self.nextTow, self.nextMsgId))
      lnav_msg = self.generateLNavMessage()
      newMessageData[i:i + 300] = lnav_msg
    self.messageLen = newMessageLen
    self.messageBits = newMessageData
    self.messageCount += newMsgCount

    return

  def generateLNavMessage(self):
    '''
    Produces additional GPS LNAV message.

    Returns
    -------
    numpy.ndarray(shape=300, dtype=numpy.uint8)
      Message bits.
    '''
    msgData = numpy.zeros(300, dtype=numpy.uint8)
    msgData[1::2] = 1  # Zero + one everywhere

    # TLM word
    self.fillTlmWord(msgData[0:30], self.nextMsgId)
    self.nextMsgId += 1
    self.updateParity(msgData[0:30])
    # logger.debug("TLM: %s" % msgData[0:30])

    # TOW word
    self.fillTowWord(msgData[30:60], self.nextTow)
    self.nextTow += 1
    if self.nextTow == 7 * 24 * 60 * 10:
      self.nextTow = 0
    self.updateParity(msgData[28:60], True)
    # logger.debug("TOW: %s" % msgData[30:60])

    self.updateParity(msgData[58:90])
    self.updateParity(msgData[88:120])
    self.updateParity(msgData[118:150])
    self.updateParity(msgData[148:180])
    self.updateParity(msgData[178:210])
    self.updateParity(msgData[208:240])
    self.updateParity(msgData[238:270])
    self.updateParity(msgData[268:300], True)

    return msgData

  def getBits(self, value, nBits):
    '''
    Converts integer into bit array

    Parameters
    ----------
    value : int
      Integer value
    nBits : number of bits to produce

    Returns
    -------
    numpy.ndarray(shape=(`nBits`), dtype=numpy.uint8)
      Parameter `value` represented as a bit array
    '''
    if nBits <= 8:
      self.a8[0] = value
      result = numpy.unpackbits(self.a8)
    else:
      self.a32[0] = value
      result = numpy.unpackbits(self.a32.view(dtype=numpy.uint8))
    return result[-nBits:]

  def fillTlmWord(self, wordBits, msgId=0):
    '''
    Fills in TLM word contents.

    Parameters
    ----------
    wordBits : numpy.ndarray(shape=30, type=numpy.uint8)
      Destination array
    '''
    wordBits[0:8] = self.getBits(0b10001011, 8)  # Preamble
    wordBits[8:22] = self.getBits(msgId, 14)  # TML message
    wordBits[22] = 0  # Reserved
    wordBits[23] = 0  # Integrity
    return

  def fillTowWord(self, wordBits, tow):
    '''
    Fills in TOW word contents.

    Parameters
    ----------
    wordBits : numpy.ndarray(shape=30, type=numpy.uint8)
      Destination array
    '''
    wordBits[0:17] = self.getBits(tow, 17)  # TOW count in 6 second units
    wordBits[17] = 0  # Alert Flag
    wordBits[18] = 0  # Anti-Spoof flag
    wordBits[19:22] = self.getBits(0, 3)  # Sub-frame ID
    return

  def updateParity(self, dataBits, resolve=False):
    '''
    Updates data bits and computes parity.

    When 32 bits are provided, they are used for parity computation and for
    bit inversion.

    Parameters
    ----------
    dataBits : numpy.ndarray(dtype=numpy.uint8)
      30 or 32 element array
    resolve: bool, optional
      When specified, bits d23 and d24 of the GPS word are updated to ensure
      that parity bits d29 and d30 are zeros.
    '''
    packed = numpy.packbits(dataBits)
    acc = (packed[0] << 24) | (packed[1] << 16) | \
          (packed[2] << 8) | packed[3]
    if len(dataBits) == 30:
      acc >>= 2
    elif acc & 0x40000000:
      acc ^= 0x3FFFFFC0
      dataBits[-30:-6] ^= 1

    # D29 = D30*^d1^d3^d5^d6^d7^d9^d10^d14^d15^d16^d17^d18^d21^d22^d24
    d29 = parity(acc & 0b01101011101100011111001101000000)
    # D30 = D29*^d3^d5^d6^d8^d9^d10^d11^d13^d15^d19^d22^d23^d24
    d30 = parity(acc & 0b10001011011110101000100111000000)

    if resolve:
      if d29:
        acc ^= 0x80
        d29 = False
        d30 = not d30
        dataBits[-8] ^= 1
      if d30:
        acc ^= 0x40
        d30 = False
        dataBits[-7] ^= 1

    # D25 = D29*^d1^d2^d3^d5^d6^d10^d11^d12^d13^d14^d17^d18^d20^d23
    dataBits[-6] = parity(acc & 0b10111011000111110011010010000000)
    # D26 = D30*^d2^d3^d4^d6^d7^d11^d12^d13^d14^d15^d18^d19^d21^d24
    dataBits[-5] = parity(acc & 0b01011101100011111001101001000000)
    # D27 = D29*^d1^d3^d4^d5^d7^d8^d12^d13^d14^d15^d16^d19^d20^d22
    dataBits[-4] = parity(acc & 0b10101110110001111100111000000000)
    # D28 = D30*^d2^d4^d5^d6^d8^d9^d13^d14^d15^d16^d17^d20^d21^d23
    dataBits[-3] = parity(acc & 0b01010111011000111110011010000000)
    # D29 = D30*^d1^d3^d5^d6^d7^d9^d10^d14^d15^d16^d17^d18^d21^d22^d24
    dataBits[-2] = d29
    # D30 = D29*^d3^d5^d6^d8^d9^d10^d11^d13^d15^d19^d22^d23^d24
    dataBits[-1] = d30

    return
peregrine/iqgen/bits/amplitude_factory.py
class ObjectFactory(object):
  def __init__(self):
  def toMapForm(self, obj):
  def fromMapForm(self, data):
  def __PolyAmplitude_ToMap(self, obj):
  def __SineAmplitude_ToMap(self, obj):
  def __MapTo_PolyAmplitude(self, data):
  def __MapTo_SineAmplitude(self, data):
peregrine/iqgen/bits/doppler_sine.py
class Doppler(DopplerBase):
  '''
  Doppler control for an object that has peridic acceleration.
  '''

  TWO_PI = scipy.constants.pi * 2.

  def __init__(self, distance0_m,  tec_epm2, speed0_mps, amplutude_mps, period_s):
    '''
    Constructs doppler control object for linear acceleration.

    Parameters
    ----------
    distance0_m : float
      Distance to object in meters at time 0.
    tec_epm2 : float
      Total free electron content integrated along line of sight to the object
      in electrons per m^2.
    speed0_mps : float
      Speed of satellite at time 0 in meters per second.
    amplutude_mps : float
      Amplitude of change
    period_s : float
      Period of change
    '''
    super(Doppler, self).__init__(distance0_m=distance0_m,
                                  tec_epm2=tec_epm2)
    self.speed0_mps = speed0_mps
    self.amplutude_mps = amplutude_mps
    self.period_s = period_s

  def __str__(self):
    '''
    Constructs literal presentation of object.

    Returns
    -------
    string
      Literal presentation of object
    '''
    return "SineDoppler(distance0_m={}, tec_epm2={}," \
           " speed0_mps={}, amplitude_mps={}, period_s={}," \
           " codeDopplerIgnored={})".\
        format(self.distance0_m, self.tec_epm2, self.speed0_mps,
               self.amplutude_mps, self.period_s, self.codeDopplerIgnored)

  def computeDistanceM(self, svTime_s):
    '''
    Computes doppler shift in meters.

    Parameters
    ----------
    svTime_s : float
      Time in seconds at which distance is computed. Please note that  is not
      a time of the observer.

    Returns
    -------
    float
      Distance to satellite in meters.
    '''
    return self.distance0_m + self.speed0_mps * svTime_s + \
        self.amplutude_mps * \
        (1 - numpy.cos(Doppler.TWO_PI * svTime_s / self.period_s))

  def computeSpeedMps(self, svTime_s):
    '''
    Computes speed along the vector to satellite in meters per second.

    Parameters
    ----------
    svTime_s : float
      Time in seconds at which speed is computed. Please note that  is not
      a time of the observer.

    Returns
    -------
    float
      Speed of satellite in meters per second.
    '''
    return self.speed0_mps + self.amplutude_mps * \
        numpy.sin(Doppler.TWO_PI * svTime_s / self.period_s)

  def computeDopplerShiftM(self, userTimeAll_s):
    '''
    Method to compute metric doppler shift

    Parameters
    ----------
    userTimeAll_s : numpy.ndarray(shape=(1, nSamples), dtype=numpy.float)
      Time vector for sample timestamps in seconds

    Returns
    -------
    numpy.ndarray(shape=(1, nSamples), dtype=numpy.float)
      Computed doppler shift in meters
    '''
    D_0 = self.speed0_mps
    D_1 = self.amplutude_mps * self.period_s / self.twoPi
    D_2 = self.twoPi / self.period_s

    doppler_m = numpy.cos(D_2 * userTimeAll_s)
    doppler_m -= 1.
    doppler_m *= -D_1
    if D_0:
      doppler_m += D_0 * userTimeAll_s

    return doppler_m

  def computeDopplerShiftHz(self, userTimeAll_s, carrierSignal):
    '''
    Method to compute doppler shift in Hz.

    Parameters
    ----------
    userTimeAll_s : numpy.ndarray(shape=(1, nSamples), dtype=numpy.float)
      Time vector for sample timestamps in seconds
    carrierSignal : object
      Carrier signal parameters

    Returns
    -------
    numpy.ndarray(shape=(1, nSamples), dtype=numpy.float)
      Computed doppler frquency shift in hertz
    '''
    D_0 = self.speed0_mps
    D_1 = self.amplutude_mps
    D_2 = self.twoPi / self.period_s

    doppler_hz = numpy.sin(D_2 * userTimeAll_s) * D_1
    if D_0:
      doppler_hz += D_0
    doppler_hz *= -float(carrierSignal.CENTER_FREQUENCY_HZ) / scipy.constants.c
    return doppler_hz
peregrine/iqgen/bits/message_const.py
class Message(object):
  '''
  Message consisting of same bits
  '''

  def __init__(self, bitValue):
    '''
    Initializes object.

    Parameters
    ----------
    bitValue : int
      Value for the bits. 1 for 0 bits, -1 for 1 bits.
    '''
    super(Message, self).__init__()
    self.bitValue = bitValue
    self.binValue = 1 if bitValue < 0 else 0

  def __str__(self, *args, **kwargs):
    '''
    Formats object as string literal

    Returns
    -------
    string
      String representation of the object
    '''
    return "Const: bit value=%d" % self.binValue

  def getDataBits(self, dataAll_idx):
    '''
    Generates vector of data bits corresponding to input index

    Parameters
    ----------
    dataAll_idx : numpy.ndarray(dtype=numpy.int64)
      Vector of bit indexes

    Returns
    -------
    numpy.ndarray(dtype=numpy.uint8)
      Vector of data bits
    '''
    result = numpy.ndarray(len(dataAll_idx), dtype=numpy.uint8)
    result.fill(self.binValue)
    return result
peregrine/iqgen/bits/message_cnav.py
class Message(object):
  '''
  GPS LNAV message block.

  The object provides proper-formatted CNAV messages with random contents.
  '''

  def __init__(self, prn, tow0=2, n_msg=0, n_prefixBits=50):
    '''
    Constructs message object.

    Parameters
    ----------
    prn : int
      Satellite PRN
    tow0 : int
      Time of week in 6-second units for the first message
    n_msg : int, optional
      Number of messages to generate for output
    n_prefixBits : int, optional
      Number of bits to issue before the first message
    '''
    super(Message, self).__init__()

    if tow0 & 1:
      logger.error("Initial ToW is not multiple of 2")

    self.prn = prn
    self.tow0 = tow0
    self.n_msg0 = n_msg
    self.n_prefixBits = n_prefixBits

    self.encoder = ConvEncoder27()
    self.messageCount = 0
    self.messageLen = n_prefixBits * 2
    self.symbolData = numpy.zeros(self.messageLen, dtype=numpy.uint8)

    prefixBits = numpy.zeros(self.n_prefixBits, dtype=numpy.uint8)
    prefixBits[0::2] = 1
    self.symbolData[:] = self.encoder.encode(prefixBits)
    self.nextTow = tow0
    self.addMessages(n_msg)

  def __str__(self, *args, **kwargs):
    '''
    Formats object as string literal

    Returns
    -------
    string
      String representation of the object
    '''
    return "GPS CNAV: prn=%d pref=%d tow=%d" % \
           (self.prn, self.n_prefixBits, self.nextTow)

  def getDataBits(self, dataAll_idx):
    '''
    Generates vector of data bits corresponding to input index

    Parameters
    ----------
    dataAll_idx : numpy.ndarray(dtype=numpy.int64)
      Vector of bit indexes

    Returns
    -------
    numpy.ndarray(dtype=numpy.uint8)
      Vector of data bits
    '''

    lastIdx = dataAll_idx[-1]
    if lastIdx >= self.messageLen:
      # Grow data bits
      delta = lastIdx - self.messageLen + 1
      newMsgCount = delta / 600
      if delta % 600:
        newMsgCount += 1
      self.addMessages(newMsgCount)

    # numpy.take degrades performance a lot over time.
    # return numpy.take(self.symbolData, dataAll_idx , mode='wrap')
    return self.symbolData[dataAll_idx]

  def addMessages(self, newMsgCount):
    '''
    Generate additional CNAV messages

    This method generates and encodes additional CNAV messages. The message
    contents is encoded using 2-7 convolution encoder and added to the internal
    buffer.

    Parameters
    ----------
    newMsgCount : int
      Number of messages to generate
    '''
    newMessageLen = newMsgCount * 600 + self.messageLen
    newSymbolData = numpy.ndarray(newMessageLen, dtype=numpy.uint8)
    newSymbolData[:self.messageLen] = self.symbolData
    for i in range(self.messageLen, newMessageLen, 600):
      logger.info("Generating CNAV message: prn=%d tow=%d msg_id=%d" %
                  (self.prn, self.nextTow, 0))
      cnav_msg = CNavRawMsg.generate(self.prn, 0, self.nextTow)
      self.nextTow += 2
      if self.nextTow == 7 * 24 * 60 * 10:
        self.nextTow = 0
      encoded = self.encoder.encode(cnav_msg)
      newSymbolData[i:i + 600] = encoded
    self.messageLen = newMessageLen
    self.symbolData = newSymbolData
    self.messageCount += newMsgCount
peregrine/iqgen/bits/doppler_factory.py
class ObjectFactory(object):
  def __init__(self):
  def toMapForm(self, obj):
  def fromMapForm(self, data):
  def __PolyDoppler_ToMap(self, obj):
  def __SineDoppler_ToMap(self, obj):
  def __MapTo_PolyDoppler(self, data):
  def __MapTo_SineDoppler(self, data):
peregrine/iqgen/bits/amplitude_sine.py
class AmplitudeSine(AmplitudeBase):
  '''
  Amplitude control with sine modulation over time.
  '''

  def __init__(self, units, initial, amplitude, period_s):
    '''
    Constructs sine amplitude control object.

    Parameters
    initial : float
      Initial amplitude value (median)
    amplitude : float
      Amplitude of change
    period_s : float
      Period of change in seconds
    '''
    super(AmplitudeSine, self).__init__(units)
    self.initial = initial
    self.amplitude = amplitude
    self.period_s = period_s
    self.c = 2. * scipy.constants.pi / period_s

  def __str__(self):
    '''
    Constructs literal presentation of object.

    Returns
    -------
    string
      Literal presentation of object
    '''
    return "AmplitudeSine(units={}, base={}, amp={}, p={} s)".\
        format(self.units, self.initial, self.amplitude, self.period_s)

  def applyAmplitude(self, signal, userTimeAll_s, noiseParams):
    '''
    Applies amplitude modulation to signal.

    Parameters
    ----------
    signal : numpy.ndarray
      Signal sample vector. Each element defines signal amplitude in range
      [-1; +1]. This vector is modified in place.
    userTimeAll_s : numpy.ndarray
      Sample time vector. Each element defines sample time in seconds.
    noiseParams : NoiseParameters
      Noise parameters to adjust signal amplitude level.

    Returns
    -------
    numpy.ndarray
      Array with output samples
    '''

    ampAll = numpy.sin(userTimeAll_s * self.c) * self.amplitude + self.initial

    ampAll = AmplitudeBase.convertUnits2Amp(ampAll,
                                            self.units,
                                            noiseParams)
    signal *= ampAll

    return signal

  def computeSNR(self, noiseParams):
    '''
    Computes signal to noise ratio in dB.

    noiseParams : NoiseParameters
      Noise parameter container

    Returns
    -------
    float
      SNR in dB
    '''
    value = self.initial
    return AmplitudeBase.convertUnits2SNR(value, self.units, noiseParams)
peregrine/iqgen/bits/message_block.py
class Message(object):
  '''
  Message that is a block of bits
  '''

  def __init__(self, messageData):
    '''
    Constructs message object.

    Parameters
    ----------
    messageData : array-like
      Array with message bits. Bit 0 is encoded with 1, bit 1 is encoded with -1
    '''
    super(Message, self).__init__()
    bits = numpy.asarray(messageData) < 0
    self.messageLen = bits.shape[0]
    self.bits = bits.astype(numpy.uint8)

  def __str__(self, *args, **kwargs):
    '''
    Formats object as string literal

    Returns
    -------
    string
      String representation of the object
    '''
    return "Block: length=%d" % len(self.bits)

  def getDataBits(self, dataAll_idx):
    '''
    Generates vector of data bits corresponding to input index

    Parameters
    ----------
    dataAll_idx : numpy.ndarray(dtype=numpy.int64)
      Vector of bit indexes

    Returns
    -------
    numpy.ndarray(dtype=numpy.uint8)
      Vector of data bits
    '''
    # numpy.take degrades performance a lot over time.
    # return numpy.take(self.bits, dataAll_idx , mode='wrap')
    return self.bits[dataAll_idx % self.messageLen]
peregrine/iqgen/bits/doppler_poly.py
class Doppler(DopplerBase):
  '''
  Doppler control for an object that has constant acceleration. Such signal has
  constant doppler value with a possible sign invert.
  '''

  TWO_PI = scipy.constants.pi * 2

  def __init__(self, distance0_m, tec_epm2, coeffs):
    '''
    Constructs doppler control object for linear acceleration.

    Parameters
    ----------
    distance0_m : float
      Distance to object in meters at time 0.
    tec_epm2 : float
      Total free electron content integrated along line of sight to the object
      in electrons per m^2.
    coeffs : array-like
      Phase shift coefficients. Phase chift will be computed as:
      C_n*t^n + C_(n-1)^(n-1) + ... + C_2*t^2 + C_1*t + C_0
      C_n..C_0 - values for speed of light
    '''
    super(Doppler, self).__init__(distance0_m=distance0_m,
                                  tec_epm2=tec_epm2)
    self.coeffs = tuple([x for x in coeffs])
    self.n_coeffs = len(coeffs)
    self.speedPoly = None
    self.distancePoly = None
    if self.n_coeffs > 0:
      new_coeffs = []
      self.n_coeffs += 1
      for idx, c in enumerate(coeffs):
        order = self.n_coeffs - idx - 1
        new_coeffs.append(c / order)
      new_coeffs.append(0.)
      self.distancePoly = numpy.poly1d(new_coeffs)
      self.distanceCoeffs = new_coeffs
      if self.n_coeffs > 1:
        self.speedPoly = numpy.poly1d(coeffs)
    else:
      self.distanceCoeffs = None

  def __str__(self):
    '''
    Constructs literal presentation of object.

    Returns
    -------
    string
      Literal presentation of object
    '''
    return "DopplerPoly(coeffs={}, distance0_m={}," \
           " tec_epm2={} codeDopplerIgnored={})". \
           format(self.coeffs, self.distance0_m,
                  self.tec_epm2, self.codeDopplerIgnored)

  def computeDistanceM(self, svTime_s):
    '''
    Computes doppler shift in meters.

    Parameters
    ----------
    svTime_s : float
      Time in seconds at which distance is computed. Please note that  is not
      a time of the observer.

    Returns
    -------
    float
      Distance to satellite in meters.
    '''
    poly = self.distancePoly
    if poly is not None:
      return poly(svTime_s)  # self.coeffs[cnt - 1]
    else:
      return 0.

  def computeSpeedMps(self, svTime_s):
    '''
    Computes speed along the vector to satellite in meters per second.

    Parameters
    ----------
    svTime_s : float
      Time in seconds at which speed is computed. Please note that  is not
      a time of the observer.

    Returns
    -------
    float
      Speed of satellite in meters per second.
    '''
    poly = self.speedPoly
    if poly is not None:
      return poly(svTime_s)
    else:
      return 0.

  def computeDopplerShiftM(self, userTimeAll_s):
    '''
    Method to compute metric doppler shift

    Parameters
    ----------
    userTimeAll_s : numpy.ndarray(shape=(1, nSamples), dtype=numpy.float)
      Time vector for sample timestamps in seconds

    Returns
    -------
    numpy.ndarray(shape=(1, nSamples), dtype=numpy.float)
      Computed doppler shift in meters
    '''
    distancePoly = self.distancePoly
    if distancePoly is not None:
      # Slower, but simple
      doppler_m = distancePoly(userTimeAll_s)
    else:
      # No phase shift
      doppler_m = numpy.zeros_like(userTimeAll_s)
    return doppler_m

  def computeDopplerShiftHz(self, userTimeAll_s, carrierSignal):
    '''
    Method to compute doppler shift in Hz.

    Parameters
    ----------
    userTimeAll_s : numpy.ndarray(shape=(1, nSamples), dtype=numpy.float)
      Time vector for sample timestamps in seconds
    carrierSignal : object
      Carrier signal parameters

    Returns
    -------
    numpy.ndarray(shape=(1, nSamples), dtype=numpy.float)
      Computed doppler frquency shift in hertz
    '''
    speedPoly = self.speedPoly
    if speedPoly is not None:
      # Slower, but simple
      c0 = -float(carrierSignal.CENTER_FREQUENCY_HZ) / scipy.constants.c
      doppler_hz = speedPoly(userTimeAll_s) * c0
    else:
      # No phase shift
      doppler_hz = numpy.zeros_like(userTimeAll_s)
    return doppler_hz


Question: from peregrine.iqgen.bits.amplitude_factory import factoryObject as AFO
from peregrine.iqgen.bits.doppler_factory import factoryObject as DFO
from peregrine.iqgen.bits.message_factory import factoryObject as MFO
from peregrine.iqgen.bits.satellite_factory import factoryObject as SFO
from peregrine.iqgen.bits.tcxo_factory import factoryObject as TFO
from peregrine.iqgen.bits.amplitude_base import AmplitudeBase
from peregrine.iqgen.bits.amplitude_poly import AmplitudePoly
from peregrine.iqgen.bits.amplitude_sine import AmplitudeSine
from peregrine.iqgen.bits.doppler_poly import Doppler as DopplerPoly
from peregrine.iqgen.bits.doppler_sine import Doppler as DopplerSine
from peregrine.iqgen.bits.message_block import Message as BlockMessage
from peregrine.iqgen.bits.message_cnav import Message as CNAVMessage
from peregrine.iqgen.bits.message_const import Message as ConstMessage
from peregrine.iqgen.bits.message_lnav import Message as LNAVMessage
from peregrine.iqgen.bits.message_zeroone import Message as ZeroOneMessage
from peregrine.iqgen.bits.satellite_gps import GPSSatellite
from peregrine.iqgen.bits.tcxo_poly import TCXOPoly as PolyTcxo
from peregrine.iqgen.bits.tcxo_sine import TCXOSine as SineTcxo
from peregrine.iqgen.iqgen_main import prepareArgsParser
import numpy as np
import os
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Swift Navigation Inc.
#
# Contact: Pasi Miettinen <pasi.miettinen@exafore.com>
# This source is subject to the license found in the file 'LICENSE' which must
# be be distributed together with this source. All other rights reserved.
#
# THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
# EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

'''
Unit tests for IQgen factories
'''










class Dummy(object):
  pass


def to_map_and_back(factory, instance):
  mapped = factory.fromMapForm(factory.toMapForm(instance))

  assert isinstance(mapped, instance.__class__)
  for key, value in mapped.__dict__.iteritems():
    if isinstance(mapped.__dict__[key], np.ndarray):
      assert (mapped.__dict__[key] == mapped.__dict__[key]).all()
    else:
      assert mapped.__dict__[key] == mapped.__dict__[key]


def value_error(factory):
  try:
    factory.toMapForm(Dummy())
    assert False
  except ValueError:
    pass

  try:
    factory.fromMapForm({'type': 'Dummy'})
    assert False
  except ValueError:
    pass


def test_factories():
  '''
  Test factories
  '''
  to_map_and_back(AFO, AmplitudePoly(AmplitudeBase.UNITS_AMPLITUDE, (1, )))

Answer:   to_map_and_back(AFO, AmplitudeSine(AmplitudeBase.UNITS_AMPLITUDE, 1., 2., 1.))