def modulate(edges, carrier_freq, duty_cycle=0.5, polarity=IRConfig.IdleLow):
    '''Modulate an edge stream

    This is a generator function.

    edges (edge stream)
        The edge stream to modulate

    carrier_freq (float)
        The modulation frequency

    duty_cycle (float)
        The duty cycle of the modulation

    polarity (infrared.IRConfig)
        Set the polarity (idle state high or low)

    Yields an edge stream.
    '''

    duty_cycle = max(min(duty_cycle, 1.0), 0.0) # constrain to 0.0 - 1.0

    # Invert edge polarity if idle-high
    if polarity == IRConfig.IdleHigh:
        edges = ((t, 1 - e) for t, e in edges)

    mod_period = (1.0 / carrier_freq)
    high_time = mod_period * duty_cycle
    low_time = mod_period * (1.0 - duty_cycle)

    es = EdgeSequence(edges, mod_period)

    yield (es.cur_time, es.cur_state()) # initial state

    while not es.at_end():
        es.advance_to_edge()

        while es.cur_state() == 1:
            yield (es.cur_time, 1)
            es.advance(high_time)
            yield (es.cur_time, 0)
            es.advance(low_time)

    yield (es.cur_time, es.cur_state()) # final state