def convert_frames_to_video(tar_file_path, output_path="output.mp4", framerate=60, overwrite=False):
    """
    Try to convert a tar file containing a sequence of frames saved by the
    meshcat viewer into a single video file.

    This relies on having `ffmpeg` installed on your system.
    """
    output_path = os.path.abspath(output_path)
    if os.path.isfile(output_path) and not overwrite:
        raise ValueError("The output path {:s} already exists. To overwrite that file, you can pass overwrite=True to this function.".format(output_path))
    with tempfile.TemporaryDirectory() as tmp_dir:
        with tarfile.open(tar_file_path) as tar:
            tar.extractall(tmp_dir)
        args = ["ffmpeg",
                "-r", str(framerate),
                "-i", r"%07d.png",
                "-vcodec", "libx264",
                "-preset", "slow",
                "-crf", "18"]
        if overwrite:
            args.append("-y")
        args.append(output_path)
        try:
            subprocess.check_call(args, cwd=tmp_dir)
        except subprocess.CalledProcessError as e:
            print("""
Could not call `ffmpeg` to convert your frames into a video.
If you want to convert the frames manually, you can extract the
.tar archive into a directory, cd to that directory, and run:
ffmpeg -r 60 -i %07d.png \\\n\t -vcodec libx264 \\\n\t -preset slow \\\n\t -crf 18 \\\n\t output.mp4
                """)
            raise
    print("Saved output as {:s}".format(output_path))
    return output_path

Try to convert a tar file containing a sequence of frames saved by the meshcat viewer into a single video file .


def toJSON(self):
        """Get a json dict of the attributes of this object."""
        return {"id": self.id,
                "compile": self.compile,
                "position": self.position,
                "version": self.version}

def toJSON ( self ) : return { "id" : self . id , "compile" : self . compile , "position" : self . position , "version" : self . version }

Get a json dict of the attributes of this object .


def from_string(cls, content, position=1, file_id=None):
        """
        Convenience method to create a file from a string.

        This file object's metadata will have the id 'inlined_input'.

        Inputs
        ------
        content -- the content of the file (a string).
        position -- (default 1) rank among all files of the model while parsing
            see FileMetadata
        file_id -- (default 'inlined_input') the file_id that will be used by
            kappa.
        """
        if file_id is None:
            file_id = 'inlined_input'
        return cls(FileMetadata(file_id, position), content)

def from_string ( cls , content , position = 1 , file_id = None ) : if file_id is None : file_id = 'inlined_input' return cls ( FileMetadata ( file_id , position ) , content )

Convenience method to create a file from a string .


def from_file(cls, fpath, position=1, file_id=None):
        """
        Convience method to create a kappa file object from a file on disk

        Inputs
        ------
        fpath -- path to the file on disk
        position -- (default 1) rank among all files of the model while parsing
            see FileMetadata
        file_id -- (default = fpath) the file_id that will be used by kappa.
        """
        if file_id is None:
            file_id = fpath
        with open(fpath) as f:
            code = f.read()
            file_content = str(code)
            file_metadata = FileMetadata(file_id, position)
            return cls(file_metadata, file_content)

def from_file ( cls , fpath , position = 1 , file_id = None ) : if file_id is None : file_id = fpath with open ( fpath ) as f : code = f . read ( ) file_content = str ( code ) file_metadata = FileMetadata ( file_id , position ) return cls ( file_metadata , file_content )


def _fix_docs(this_abc, child_class):
        """Make api method docs inheritted.

        Specifically, insepect.getdoc will return values inheritted from this
        abc for standardized api methods.
        """
        # After python 3.5, this is basically handled automatically
        if sys.version_info >= (3, 5):
            return child_class

        if not issubclass(child_class, this_abc):
            raise KappaError('Cannot fix docs of class that is not decendent.')

        # This method is modified from solution given in
        # https://stackoverflow.com/a/8101598/8863865
        for name, child_func in vars(child_class).items():
            if callable(child_func) and not child_func.__doc__:
                if name in this_abc.__abstractmethods__:
                    parent_func = getattr(this_abc, name)
                    child_func.__doc__ = parent_func.__doc__
        return child_class


def add_model_string(self, model_str, position=1, file_id=None):
        """Add a kappa model given in a string to the project."""
        if file_id is None:
            file_id = self.make_unique_id('inlined_input')
        ret_data = self.file_create(File.from_string(model_str, position,
                                                     file_id))
        return ret_data

def add_model_string ( self , model_str , position = 1 , file_id = None ) : if file_id is None : file_id = self . make_unique_id ( 'inlined_input' ) ret_data = self . file_create ( File . from_string ( model_str , position , file_id ) ) return ret_data

Add a kappa model given in a string to the project .


def add_model_file(self, model_fpath, position=1, file_id=None):
        """Add a kappa model from a file at given path to the project."""
        if file_id is None:
            file_id = self.make_unique_id('file_input')
        ret_data = self.file_create(File.from_file(model_fpath, position,
                                                   file_id))
        return ret_data

def add_model_file ( self , model_fpath , position = 1 , file_id = None ) : if file_id is None : file_id = self . make_unique_id ( 'file_input' ) ret_data = self . file_create ( File . from_file ( model_fpath , position , file_id ) ) return ret_data

Add a kappa model from a file at given path to the project .


def set_default_sim_param(self, *args, **kwargs):
        """Set the simulation default simulation parameters.

        You can pass one of two things in as input:
        - a kappa_common.SimulationParameter instance
        - the arguments and keyword argument to create such an instance.

        The parameters you specify will be used by default in simulations run
        by this client.
        """
        if len(args) is 1 and isinstance(args[0], SimulationParameter):
            self.__default_param = args[0]
        else:
            self.__default_param = SimulationParameter(*args, **kwargs)
        return

def set_default_sim_param ( self , * args , ** kwargs ) : if len ( args ) is 1 and isinstance ( args [ 0 ] , SimulationParameter ) : self . __default_param = args [ 0 ] else : self . __default_param = SimulationParameter ( * args , ** kwargs ) return

Set the simulation default simulation parameters .


def get_is_sim_running(self):
        """Check if the current simulation is running."""
        sim_info = self.simulation_info()
        try:
            progress_info = sim_info['simulation_info_progress']
            ret = progress_info['simulation_progress_is_running']
        except KeyError:  # Simulation has not been created.
            ret = False
        return ret

def get_is_sim_running ( self ) : sim_info = self . simulation_info ( ) try : progress_info = sim_info [ 'simulation_info_progress' ] ret = progress_info [ 'simulation_progress_is_running' ] except KeyError : ret = False return ret

Check if the current simulation is running .


def wait_for_simulation_stop(self, timeout=None):
        """Block until the simulation is done or timeout seconds exceeded.

        If the simulation stops before timeout, siminfo is returned.
        """
        start = datetime.now()
        while self.get_is_sim_running():
            sleep(0.5)
            if timeout is not None:
                if (datetime.now() - start).seconds >= timeout:
                    ret = None
                    break
        else:
            ret = self.simulation_info()
        return ret

def wait_for_simulation_stop ( self , timeout = None ) : start = datetime . now ( ) while self . get_is_sim_running ( ) : sleep ( 0.5 ) if timeout is not None : if ( datetime . now ( ) - start ) . seconds >= timeout : ret = None break else : ret = self . simulation_info ( ) return ret

Block until the simulation is done or timeout seconds exceeded .


def in_out_check(self):
        """
        Checks the input and output to see if they are valid

        """
        devices = available_devices()
        if not self.in_idx in devices:
            raise OSError("Input device is unavailable")
        in_check = devices[self.in_idx]
        if not self.out_idx in devices:
            raise OSError("Output device is unavailable")
        out_check = devices[self.out_idx]
        if((in_check['inputs'] == 0) and (out_check['outputs']==0)):
            raise StandardError('Invalid input and output devices')
        elif(in_check['inputs'] == 0):
            raise ValueError('Selected input device has no inputs')
        elif(out_check['outputs'] == 0):
            raise ValueError('Selected output device has no outputs')
        return True

def
in_out_check
(
self
)
:
devices
=
available_devices
(
)
if
not
self
.
in_idx
in
devices
:
raise
OSError
(
"Input device is unavailable"
)
in_check
=
devices
[
self
.
in_idx
]
if
not
self
.
out_idx
in
devices
:
raise
OSError
(
"Output device is unavailable"
)
out_check
=
devices
[
self
.
out_idx
]
if
(
(
in_check
[
'inputs'
]
==
0
)
and
(
out_check
[
'outputs'
]
==
0
)
)
:
raise
StandardError
(
'Invalid input and output devices'
)
elif
(
in_check
[
'inputs'
]
==
0
)
:
raise
ValueError
(
'Selected input device has no inputs'
)
elif
(
out_check
[
'outputs'
]
==
0
)
:
raise
ValueError
(
'Selected output device has no outputs'
)
return
True


def DSP_capture_add_samples(self,new_data):
        """
        Append new samples to the data_capture array and increment the sample counter
        If length reaches Tcapture, then the newest samples will be kept. If Tcapture = 0
        then new values are not appended to the data_capture array.

        """
        self.capture_sample_count += len(new_data)
        if self.Tcapture > 0:
            self.data_capture = np.hstack((self.data_capture,new_data))
            if (self.Tcapture > 0) and (len(self.data_capture) > self.Ncapture):
                self.data_capture = self.data_capture[-self.Ncapture:]

def DSP_capture_add_samples ( self , new_data ) : self . capture_sample_count += len ( new_data ) if self . Tcapture > 0 : self . data_capture = np . hstack ( ( self . data_capture , new_data ) ) if ( self . Tcapture > 0 ) and ( len ( self . data_capture ) > self . Ncapture ) : self . data_capture = self . data_capture [ - self . Ncapture : ]

Append new samples to the data_capture array and increment the sample counter If length reaches Tcapture then the newest samples will be kept . If Tcapture = 0 then new values are not appended to the data_capture array .


def DSP_capture_add_samples_stereo(self,new_data_left,new_data_right):
        """
        Append new samples to the data_capture_left array and the data_capture_right
        array and increment the sample counter. If length reaches Tcapture, then the
        newest samples will be kept. If Tcapture = 0 then new values are not appended
        to the data_capture array.

        """
        self.capture_sample_count = self.capture_sample_count + len(new_data_left) + len(new_data_right)
        if self.Tcapture > 0:
            self.data_capture_left = np.hstack((self.data_capture_left,new_data_left))
            self.data_capture_right = np.hstack((self.data_capture_right,new_data_right))
            if (len(self.data_capture_left) > self.Ncapture):
                self.data_capture_left = self.data_capture_left[-self.Ncapture:]
            if (len(self.data_capture_right) > self.Ncapture):
                self.data_capture_right = self.data_capture_right[-self.Ncapture:]

def DSP_capture_add_samples_stereo ( self , new_data_left , new_data_right ) : self . capture_sample_count = self . capture_sample_count + len ( new_data_left ) + len ( new_data_right ) if self . Tcapture > 0 : self . data_capture_left = np . hstack ( ( self . data_capture_left , new_data_left ) ) self . data_capture_right = np . hstack ( ( self . data_capture_right , new_data_right ) ) if ( len ( self . data_capture_left ) > self . Ncapture ) : self . data_capture_left = self . data_capture_left [ - self . Ncapture : ] if ( len ( self . data_capture_right ) > self . Ncapture ) : self . data_capture_right = self . data_capture_right [ - self . Ncapture : ]

Append new samples to the data_capture_left array and the data_capture_right array and increment the sample counter . If length reaches Tcapture then the newest samples will be kept . If Tcapture = 0 then new values are not appended to the data_capture array .


def DSP_callback_tic(self):
        """
        Add new tic time to the DSP_tic list. Will not be called if
        Tcapture = 0.

        """
        if self.Tcapture > 0:
            self.DSP_tic.append(time.time()-self.start_time)

def DSP_callback_tic ( self ) : if self . Tcapture > 0 : self . DSP_tic . append ( time . time ( ) - self . start_time )

Add new tic time to the DSP_tic list . Will not be called if Tcapture = 0 .


def DSP_callback_toc(self):
        """
        Add new toc time to the DSP_toc list. Will not be called if
        Tcapture = 0.

        """
        if self.Tcapture > 0:
            self.DSP_toc.append(time.time()-self.start_time)

def DSP_callback_toc ( self ) : if self . Tcapture > 0 : self . DSP_toc . append ( time . time ( ) - self . start_time )

Add new toc time to the DSP_toc list . Will not be called if Tcapture = 0 .


def IIR_bsf(f_pass1, f_stop1, f_stop2, f_pass2, Ripple_pass, Atten_stop,
            fs = 1.00, ftype = 'butter'):
    """
    Design an IIR bandstop filter using scipy.signal.iirdesign.
    The filter order is determined based on
    f_pass Hz, f_stop Hz, and the desired stopband attenuation
    d_stop in dB, all relative to a sampling rate of fs Hz.

    Mark Wickert October 2016
    """

    b,a = signal.iirdesign([2*float(f_pass1)/fs, 2*float(f_pass2)/fs],
                           [2*float(f_stop1)/fs, 2*float(f_stop2)/fs],
                           Ripple_pass, Atten_stop,
                           ftype = ftype, output='ba')
    sos = signal.iirdesign([2*float(f_pass1)/fs, 2*float(f_pass2)/fs],
                           [2*float(f_stop1)/fs, 2*float(f_stop2)/fs],
                           Ripple_pass, Atten_stop,
                           ftype =ftype, output='sos')
    tag = 'IIR ' + ftype + ' order'
    print('%s = %d.' % (tag,len(a)-1))
    return b, a, sos

def
IIR_bsf
(
f_pass1
,
f_stop1
,
f_stop2
,
f_pass2
,
Ripple_pass
,
Atten_stop
,
fs
=
1.00
,
ftype
=
'butter'
)
:
b
,
a
=
signal
.
iirdesign
(
[
2
*
float
(
f_pass1
)
/
fs
,
2
*
float
(
f_pass2
)
/
fs
]
,
[
2
*
float
(
f_stop1
)
/
fs
,
2
*
float
(
f_stop2
)
/
fs
]
,
Ripple_pass
,
Atten_stop
,
ftype
=
ftype
,
output
=
'ba'
)
sos
=
signal
.
iirdesign
(
[
2
*
float
(
f_pass1
)
/
fs
,
2
*
float
(
f_pass2
)
/
fs
]
,
[
2
*
float
(
f_stop1
)
/
fs
,
2
*
float
(
f_stop2
)
/
fs
]
,
Ripple_pass
,
Atten_stop
,
ftype
=
ftype
,
output
=
'sos'
)
tag
=
'IIR '
+
ftype
+
' order'
print
(
'%s = %d.'
%
(
tag
,
len
(
a
)
1
)
)
return
b
,
a
,
sos

Design an IIR bandstop filter using scipy . signal . iirdesign . The filter order is determined based on f_pass Hz f_stop Hz and the desired stopband attenuation d_stop in dB all relative to a sampling rate of fs Hz . Mark Wickert October 2016


def freqz_cas(sos,w):
    """
    Cascade frequency response

    Mark Wickert October 2016
    """
    Ns,Mcol = sos.shape
    w,Hcas = signal.freqz(sos[0,:3],sos[0,3:],w)
    for k in range(1,Ns):
        w,Htemp = signal.freqz(sos[k,:3],sos[k,3:],w)
        Hcas *= Htemp
    return w, Hcas

def freqz_cas ( sos , w ) : Ns , Mcol = sos . shape w , Hcas = signal . freqz ( sos [ 0 , : 3 ] , sos [ 0 , 3 : ] , w ) for k in range ( 1 , Ns ) : w , Htemp = signal . freqz ( sos [ k , : 3 ] , sos [ k , 3 : ] , w ) Hcas *= Htemp return w , Hcas


def unique_cpx_roots(rlist,tol = 0.001):
    """

    The average of the root values is used when multiplicity
    is greater than one.

    Mark Wickert October 2016
    """
    uniq = [rlist[0]]
    mult = [1]
    for k in range(1,len(rlist)):
        N_uniq = len(uniq)
        for m in range(N_uniq):
            if abs(rlist[k]-uniq[m]) <= tol:
                mult[m] += 1
                uniq[m] = (uniq[m]*(mult[m]-1) + rlist[k])/float(mult[m])
                break
        uniq = np.hstack((uniq,rlist[k]))
        mult = np.hstack((mult,[1]))
    return np.array(uniq), np.array(mult)

def unique_cpx_roots ( rlist , tol = 0.001 ) : uniq = [ rlist [ 0 ] ] mult = [ 1 ] for k in range ( 1 , len ( rlist ) ) : N_uniq = len ( uniq ) for m in range ( N_uniq ) : if abs ( rlist [ k ] - uniq [ m ] ) <= tol : mult [ m ] += 1 uniq [ m ] = ( uniq [ m ] * ( mult [ m ] - 1 ) + rlist [ k ] ) / float ( mult [ m ] ) break uniq = np . hstack ( ( uniq , rlist [ k ] ) ) mult = np . hstack ( ( mult , [ 1 ] ) ) return np . array ( uniq ) , np . array ( mult )

The average of the root values is used when multiplicity is greater than one . Mark Wickert October 2016


def firwin_bpf(N_taps, f1, f2, fs = 1.0, pass_zero=False):
    """
    Design a windowed FIR bandpass filter in terms of passband
    critical frequencies f1 < f2 in Hz relative to sampling rate
    fs in Hz. The number of taps must be provided.

    Mark Wickert October 2016
    """
    return signal.firwin(N_taps,2*(f1,f2)/fs,pass_zero=pass_zero)

def firwin_bpf ( N_taps , f1 , f2 , fs = 1.0 , pass_zero = False ) : return signal . firwin ( N_taps , 2 * ( f1 , f2 ) / fs , pass_zero = pass_zero )

Design a windowed FIR bandpass filter in terms of passband critical frequencies f1 < f2 in Hz relative to sampling rate fs in Hz . The number of taps must be provided . Mark Wickert October 2016


def fir_remez_lpf(f_pass, f_stop, d_pass, d_stop, fs = 1.0, N_bump=5):
    """
    Design an FIR lowpass filter using remez with order
    determination. The filter order is determined based on
    f_pass Hz, fstop Hz, and the desired passband ripple
    d_pass dB and stopband attenuation d_stop dB all
    relative to a sampling rate of fs Hz.

    Mark Wickert October 2016, updated October 2018
    """
    n, ff, aa, wts = lowpass_order(f_pass, f_stop, d_pass, d_stop, fsamp=fs)
    # Bump up the order by N_bump to bring down the final d_pass & d_stop
    N_taps = n
    N_taps += N_bump
    b = signal.remez(N_taps, ff, aa[0::2], wts,Hz=2)
    print('Remez filter taps = %d.' % N_taps)
    return b

def
fir_remez_lpf
(
f_pass
,
f_stop
,
d_pass
,
d_stop
,
fs
=
1.0
,
N_bump
=
5
)
:
n
,
ff
,
aa
,
wts
=
lowpass_order
(
f_pass
,
f_stop
,
d_pass
,
d_stop
,
fsamp
=
fs
)
N_taps
=
n
N_taps
+=
N_bump
b
=
signal
.
remez
(
N_taps
,
ff
,
aa
[
0
:
:
2
]
,
wts
,
Hz
=
2
)
print
(
'Remez filter taps = %d.'
%
N_taps
)
return
b

Design an FIR lowpass filter using remez with order determination . The filter order is determined based on f_pass Hz fstop Hz and the desired passband ripple d_pass dB and stopband attenuation d_stop dB all relative to a sampling rate of fs Hz . Mark Wickert October 2016 updated October 2018


def fir_remez_hpf(f_stop, f_pass, d_pass, d_stop, fs = 1.0, N_bump=5):
    """
    Design an FIR highpass filter using remez with order
    determination. The filter order is determined based on
    f_pass Hz, fstop Hz, and the desired passband ripple
    d_pass dB and stopband attenuation d_stop dB all
    relative to a sampling rate of fs Hz.

    Mark Wickert October 2016, updated October 2018
    """
    # Transform HPF critical frequencies to lowpass equivalent
    f_pass_eq = fs/2. - f_pass
    f_stop_eq = fs/2. - f_stop
    # Design LPF equivalent
    n, ff, aa, wts = lowpass_order(f_pass_eq, f_stop_eq, d_pass, d_stop, fsamp=fs)
    # Bump up the order by N_bump to bring down the final d_pass & d_stop
    N_taps = n
    N_taps += N_bump
    b = signal.remez(N_taps, ff, aa[0::2], wts,Hz=2)
    # Transform LPF equivalent to HPF
    n = np.arange(len(b))
    b *= (-1)**n
    print('Remez filter taps = %d.' % N_taps)
    return b

def
fir_remez_hpf
(
f_stop
,
f_pass
,
d_pass
,
d_stop
,
fs
=
1.0
,
N_bump
=
5
)
:
f_pass_eq
=
fs
/
2.
f_pass
f_stop_eq
=
fs
/
2.
f_stop
n
,
ff
,
aa
,
wts
=
lowpass_order
(
f_pass_eq
,
f_stop_eq
,
d_pass
,
d_stop
,
fsamp
=
fs
)
N_taps
=
n
N_taps
+=
N_bump
b
=
signal
.
remez
(
N_taps
,
ff
,
aa
[
0
:
:
2
]
,
wts
,
Hz
=
2
)
n
=
np
.
arange
(
len
(
b
)
)
b
*=
(
1
)
**
n
print
(
'Remez filter taps = %d.'
%
N_taps
)
return
b

Design an FIR highpass filter using remez with order determination . The filter order is determined based on f_pass Hz fstop Hz and the desired passband ripple d_pass dB and stopband attenuation d_stop dB all relative to a sampling rate of fs Hz . Mark Wickert October 2016 updated October 2018


def fir_remez_bpf(f_stop1, f_pass1, f_pass2, f_stop2, d_pass, d_stop,
                  fs = 1.0, N_bump=5):
    """
    Design an FIR bandpass filter using remez with order
    determination. The filter order is determined based on
    f_stop1 Hz, f_pass1 Hz, f_pass2 Hz, f_stop2 Hz, and the
    desired passband ripple d_pass dB and stopband attenuation
    d_stop dB all relative to a sampling rate of fs Hz.

    Mark Wickert October 2016, updated October 2018
    """
    n, ff, aa, wts = bandpass_order(f_stop1, f_pass1, f_pass2, f_stop2,
                                  d_pass, d_stop, fsamp=fs)
    # Bump up the order by N_bump to bring down the final d_pass & d_stop
    N_taps = n
    N_taps += N_bump
    b = signal.remez(N_taps, ff, aa[0::2], wts,Hz=2)
    print('Remez filter taps = %d.' % N_taps)
    return b

def
fir_remez_bpf
(
f_stop1
,
f_pass1
,
f_pass2
,
f_stop2
,
d_pass
,
d_stop
,
fs
=
1.0
,
N_bump
=
5
)
:
n
,
ff
,
aa
,
wts
=
bandpass_order
(
f_stop1
,
f_pass1
,
f_pass2
,
f_stop2
,
d_pass
,
d_stop
,
fsamp
=
fs
)
N_taps
=
n
N_taps
+=
N_bump
b
=
signal
.
remez
(
N_taps
,
ff
,
aa
[
0
:
:
2
]
,
wts
,
Hz
=
2
)
print
(
'Remez filter taps = %d.'
%
N_taps
)
return
b

Design an FIR bandpass filter using remez with order determination . The filter order is determined based on f_stop1 Hz f_pass1 Hz f_pass2 Hz f_stop2 Hz and the desired passband ripple d_pass dB and stopband attenuation d_stop dB all relative to a sampling rate of fs Hz . Mark Wickert October 2016 updated October 2018


def fir_remez_bsf(f_pass1, f_stop1, f_stop2, f_pass2, d_pass, d_stop,
                  fs = 1.0, N_bump=5):
    """
    Design an FIR bandstop filter using remez with order
    determination. The filter order is determined based on
    f_pass1 Hz, f_stop1 Hz, f_stop2 Hz, f_pass2 Hz, and the
    desired passband ripple d_pass dB and stopband attenuation
    d_stop dB all relative to a sampling rate of fs Hz.

    Mark Wickert October 2016, updated October 2018
    """
    n, ff, aa, wts = bandstop_order(f_pass1, f_stop1, f_stop2, f_pass2,
                                    d_pass, d_stop, fsamp=fs)
    # Bump up the order by N_bump to bring down the final d_pass & d_stop
    # Initially make sure the number of taps is even so N_bump needs to be odd
    if np.mod(n,2) != 0:
        n += 1
    N_taps = n
    N_taps += N_bump
    b = signal.remez(N_taps, ff, aa[0::2], wts, Hz=2,
                     maxiter = 25, grid_density = 16)
    print('N_bump must be odd to maintain odd filter length')
    print('Remez filter taps = %d.' % N_taps)
    return b

Design an FIR bandstop filter using remez with order determination . The filter order is determined based on f_pass1 Hz f_stop1 Hz f_stop2 Hz f_pass2 Hz and the desired passband ripple d_pass dB and stopband attenuation d_stop dB all relative to a sampling rate of fs Hz . Mark Wickert October 2016 updated October 2018


def position_CD(Ka,out_type = 'fb_exact'):
    """
    CD sled position control case study of Chapter 18.

    The function returns the closed-loop and open-loop
    system function for a CD/DVD sled position control
    system. The loop amplifier gain is the only variable
    that may be changed. The returned system function can
    however be changed.

    Parameters
    ----------
    Ka : loop amplifier gain, start with 50.
    out_type : 'open_loop' for open loop system function
    out_type : 'fb_approx' for closed-loop approximation
    out_type : 'fb_exact' for closed-loop exact

    Returns
    -------
    b : numerator coefficient ndarray
    a : denominator coefficient ndarray

    Notes
    -----
    With the exception of the loop amplifier gain, all
    other parameters are hard-coded from Case Study example.

    Examples
    --------
    >>> b,a = position_CD(Ka,'fb_approx')
    >>> b,a = position_CD(Ka,'fb_exact')
    """
    rs = 10/(2*np.pi)
    # Load b and a ndarrays with the coefficients
    if out_type.lower() == 'open_loop':
        b = np.array([Ka*4000*rs])
        a = np.array([1,1275,31250,0])
    elif out_type.lower() == 'fb_approx':
        b = np.array([3.2*Ka*rs])
        a = np.array([1, 25, 3.2*Ka*rs])
    elif out_type.lower() == 'fb_exact':
        b = np.array([4000*Ka*rs])
        a = np.array([1, 1250+25, 25*1250, 4000*Ka*rs])
    else:
        raise ValueError('out_type must be: open_loop, fb_approx, or fc_exact')
    return b, a

CD sled position control case study of Chapter 18 .


def cruise_control(wn,zeta,T,vcruise,vmax,tf_mode='H'):
    """
    Cruise control with PI controller and hill disturbance.

    This function returns various system function configurations
    for a the cruise control Case Study example found in
    the supplementary article. The plant model is obtained by the
    linearizing the equations of motion and the controller contains a
    proportional and integral gain term set via the closed-loop parameters
    natuarl frequency wn (rad/s) and damping zeta.

    Parameters
    ----------
    wn : closed-loop natural frequency in rad/s, nominally 0.1
    zeta : closed-loop damping factor, nominally 1.0
    T : vehicle time constant, nominally 10 s
    vcruise : cruise velocity set point, nominally 75 mph
    vmax : maximum vehicle velocity, nominally 120 mph
    tf_mode : 'H', 'HE', 'HVW', or 'HED' controls the system function returned by the function
    'H'   : closed-loop system function V(s)/R(s)
    'HE'  : closed-loop system function E(s)/R(s)
    'HVW' : closed-loop system function V(s)/W(s)
    'HED' : closed-loop system function E(s)/D(s), where D is the hill disturbance input

    Returns
    -------
    b : numerator coefficient ndarray
    a : denominator coefficient ndarray

    Examples
    --------
    >>> # return the closed-loop system function output/input velocity
    >>> b,a = cruise_control(wn,zeta,T,vcruise,vmax,tf_mode='H')
    >>> # return the closed-loop system function loop error/hill disturbance
    >>> b,a = cruise_control(wn,zeta,T,vcruise,vmax,tf_mode='HED')
    """
    tau = T/2.*vmax/vcruise
    g = 9.8
    g *= 3*60**2/5280. # m/s to mph conversion
    Kp = T*(2*zeta*wn-1/tau)/vmax
    Ki = T*wn**2./vmax
    K = Kp*vmax/T
    print('wn = ', np.sqrt(K/(Kp/Ki)))
    print('zeta = ', (K + 1/tau)/(2*wn))
    a = np.array([1, 2*zeta*wn, wn**2])
    if tf_mode == 'H':
        b = np.array([K, wn**2])
    elif tf_mode == 'HE':
        b = np.array([1, 2*zeta*wn-K, 0.])
    elif tf_mode == 'HVW':
        b = np.array([ 1, wn**2/K+1/tau, wn**2/(K*tau)])
        b *= Kp
    elif tf_mode == 'HED':
        b = np.array([g, 0])
    else:
        raise ValueError('tf_mode must be: H, HE, HVU, or HED')
    return b, a

def
cruise_control
(
wn
,
zeta
,
T
,
vcruise
,
vmax
,
tf_mode
=
'H'
)
:
tau
=
T
/
2.
*
vmax
/
vcruise
g
=
9.8
g
*=
3
*
60
**
2
/
5280.
Kp
=
T
*
(
2
*
zeta
*
wn
1
/
tau
)
/
vmax
Ki
=
T
*
wn
**
2.
/
vmax
K
=
Kp
*
vmax
/
T
print
(
'wn = '
,
np
.
sqrt
(
K
/
(
Kp
/
Ki
)
)
)
print
(
'zeta = '
,
(
K
+
1
/
tau
)
/
(
2
*
wn
)
)
a
=
np
.
array
(
[
1
,
2
*
zeta
*
wn
,
wn
**
2
]
)
if
tf_mode
==
'H'
:
b
=
np
.
array
(
[
K
,
wn
**
2
]
)
elif
tf_mode
==
'HE'
:
b
=
np
.
array
(
[
1
,
2
*
zeta
*
wn
K
,
0.
]
)
elif
tf_mode
==
'HVW'
:
b
=
np
.
array
(
[
1
,
wn
**
2
/
K
+
1
/
tau
,
wn
**
2
/
(
K
*
tau
)
]
)
b
*=
Kp
elif
tf_mode
==
'HED'
:
b
=
np
.
array
(
[
g
,
0
]
)
else
:
raise
ValueError
(
'tf_mode must be: H, HE, HVU, or HED'
)
return
b
,
a

Cruise control with PI controller and hill disturbance .


def stereo_FM(x,fs=2.4e6,file_name='test.wav'):
    """
    Stereo demod from complex baseband at sampling rate fs.
    Assume fs is 2400 ksps

    Mark Wickert July 2017
    """
    N1 = 10
    b = signal.firwin(64,2*200e3/float(fs))
    # Filter and decimate (should be polyphase)
    y = signal.lfilter(b,1,x)
    z = ss.downsample(y,N1)
    # Apply complex baseband discriminator
    z_bb = discrim(z)
    # Work with the (3) stereo multiplex signals:
    # Begin by designing a lowpass filter for L+R and DSP demoded (L-R)
    # (fc = 12 KHz)
    b12 = signal.firwin(128,2*12e3/(float(fs)/N1))
    # The L + R term is at baseband, we just lowpass filter to remove
    # other terms above 12 kHz.
    y_lpr = signal.lfilter(b12,1,z_bb)
    b19 = signal.firwin(128,2*1e3*np.array([19-5,19+5])/(float(fs)/N1),
                        pass_zero=False);
    z_bb19 = signal.lfilter(b19,1,z_bb)
    # Lock PLL to 19 kHz pilot
    # A type 2 loop with bandwidth Bn = 10 Hz and damping zeta = 0.707
    # The VCO quiescent frequency is set to 19000 Hz.
    theta, phi_error = pilot_PLL(z_bb19,19000,fs/N1,2,10,0.707)
    # Coherently demodulate the L - R subcarrier at 38 kHz.
    # theta is the PLL output phase at 19 kHz, so to double multiply
    # by 2 and wrap with cos() or sin().
    # First bandpass filter
    b38 = signal.firwin(128,2*1e3*np.array([38-5,38+5])/(float(fs)/N1),
                        pass_zero=False);
    x_lmr = signal.lfilter(b38,1,z_bb)
    # Coherently demodulate using the PLL output phase
    x_lmr = 2*np.sqrt(2)*np.cos(2*theta)*x_lmr
    # Lowpass at 12 kHz to recover the desired DSB demod term
    y_lmr = signal.lfilter(b12,1,x_lmr)
    # Matrix the y_lmr and y_lpr for form right and left channels:
    y_left = y_lpr + y_lmr
    y_right = y_lpr - y_lmr

    # Decimate by N2 (nominally 5)
    N2 = 5
    fs2 = float(fs)/(N1*N2) # (nominally 48 ksps)
    y_left_DN2 = ss.downsample(y_left,N2)
    y_right_DN2 = ss.downsample(y_right,N2)
    # Deemphasize with 75 us time constant to 'undo' the preemphasis
    # applied at the transmitter in broadcast FM.
    # A 1-pole digital lowpass works well here.
    a_de = np.exp(-2.1*1e3*2*np.pi/fs2)
    z_left = signal.lfilter([1-a_de],[1, -a_de],y_left_DN2)
    z_right = signal.lfilter([1-a_de],[1, -a_de],y_right_DN2)
    # Place left and righ channels as side-by-side columns in a 2D array
    z_out = np.hstack((np.array([z_left]).T,(np.array([z_right]).T)))

    ss.to_wav(file_name, 48000, z_out/2)
    print('Done!')
    #return z_bb, z_out
    return z_bb, theta, y_lpr, y_lmr, z_out

def stereo_FM ( x , fs = 2.4e6 , file_name = 'test.wav' ) : N1 = 10 b = signal . firwin ( 64 , 2 * 200e3 / float ( fs ) ) y = signal . lfilter ( b , 1 , x ) z = ss . downsample ( y , N1 ) z_bb = discrim ( z ) b12 = signal . firwin ( 128 , 2 * 12e3 / ( float ( fs ) / N1 ) ) y_lpr = signal . lfilter ( b12 , 1 , z_bb ) b19 = signal . firwin ( 128 , 2 * 1e3 * np . array ( [ 19 - 5 , 19 + 5 ] ) / ( float ( fs ) / N1 ) , pass_zero = False ) z_bb19 = signal . lfilter ( b19 , 1 , z_bb ) theta , phi_error = pilot_PLL ( z_bb19 , 19000 , fs / N1 , 2 , 10 , 0.707 ) b38 = signal . firwin ( 128 , 2 * 1e3 * np . array ( [ 38 - 5 , 38 + 5 ] ) / ( float ( fs ) / N1 ) , pass_zero = False ) x_lmr = signal . lfilter ( b38 , 1 , z_bb ) x_lmr = 2 * np . sqrt ( 2 ) * np . cos ( 2 * theta ) * x_lmr y_lmr = signal . lfilter ( b12 , 1 , x_lmr ) y_left = y_lpr + y_lmr y_right = y_lpr - y_lmr N2 = 5 fs2 = float ( fs ) / ( N1 * N2 ) y_left_DN2 = ss . downsample ( y_left , N2 ) y_right_DN2 = ss . downsample ( y_right , N2 ) a_de = np . exp ( - 2.1 * 1e3 * 2 * np . pi / fs2 ) z_left = signal . lfilter ( [ 1 - a_de ] , [ 1 , - a_de ] , y_left_DN2 ) z_right = signal . lfilter ( [ 1 - a_de ] , [ 1 , - a_de ] , y_right_DN2 ) z_out = np . hstack ( ( np . array ( [ z_left ] ) . T , ( np . array ( [ z_right ] ) . T ) ) ) ss . to_wav ( file_name , 48000 , z_out / 2 ) print ( 'Done!' ) return z_bb , theta , y_lpr , y_lmr , z_out

Stereo demod from complex baseband at sampling rate fs . Assume fs is 2400 ksps Mark Wickert July 2017


def FIR_header(fname_out, h):
    """
    Write FIR Filter Header Files

    Mark Wickert February 2015
    """
    M = len(h)
    N = 3  # Coefficients per line
    f = open(fname_out, 'wt')
    f.write('//define a FIR coefficient Array\n\n')
    f.write('#include <stdint.h>\n\n')
    f.write('#ifndef M_FIR\n')
    f.write('#define M_FIR %d\n' % M)
    f.write('#endif\n')
    f.write('/************************************************************************/\n');
    f.write('/*                         FIR Filter Coefficients                      */\n');
    f.write('float32_t h_FIR[M_FIR] = {')
    kk = 0;
    for k in range(M):
        # k_mod = k % M
        if (kk < N - 1) and (k < M - 1):
            f.write('%15.12f,' % h[k])
            kk += 1
        elif (kk == N - 1) & (k < M - 1):
            f.write('%15.12f,\n' % h[k])
            if k < M:
                f.write('                          ')
                kk = 0
        else:
            f.write('%15.12f' % h[k])
    f.write('};\n')
    f.write('/************************************************************************/\n')
    f.close()

'/*                         FIR Filter Coefficients                      */\n'


def FIR_fix_header(fname_out, h):
    """
    Write FIR Fixed-Point Filter Header Files

    Mark Wickert February 2015
    """
    M = len(h)
    hq = int16(rint(h * 2 ** 15))
    N = 8  # Coefficients per line
    f = open(fname_out, 'wt')
    f.write('//define a FIR coefficient Array\n\n')
    f.write('#include <stdint.h>\n\n')
    f.write('#ifndef M_FIR\n')
    f.write('#define M_FIR %d\n' % M)
    f.write('#endif\n')
    f.write('/************************************************************************/\n');
    f.write('/*                         FIR Filter Coefficients                      */\n');
    f.write('int16_t h_FIR[M_FIR] = {')
    kk = 0;
    for k in range(M):
        # k_mod = k % M
        if (kk < N - 1) and (k < M - 1):
            f.write('%5d,' % hq[k])
            kk += 1
        elif (kk == N - 1) & (k < M - 1):
            f.write('%5d,\n' % hq[k])
            if k < M:
                f.write('                        ')
                kk = 0
        else:
            f.write('%5d' % hq[k])
    f.write('};\n')
    f.write('/************************************************************************/\n')
    f.close()

Write FIR Fixed - Point Filter Header Files Mark Wickert February 2015


def IIR_sos_header(fname_out, SOS_mat):
    """
    Write IIR SOS Header Files
    File format is compatible with CMSIS-DSP IIR
    Directform II Filter Functions

    Mark Wickert March 2015-October 2016
    """
    Ns, Mcol = SOS_mat.shape
    f = open(fname_out, 'wt')
    f.write('//define a IIR SOS CMSIS-DSP coefficient array\n\n')
    f.write('#include <stdint.h>\n\n')
    f.write('#ifndef STAGES\n')
    f.write('#define STAGES %d\n' % Ns)
    f.write('#endif\n')
    f.write('/*********************************************************/\n');
    f.write('/*                     IIR SOS Filter Coefficients       */\n');
    f.write('float32_t ba_coeff[%d] = { //b0,b1,b2,a1,a2,... by stage\n' % (5 * Ns))
    for k in range(Ns):
        if (k < Ns - 1):
            f.write('    %+-13e, %+-13e, %+-13e,\n' % \
                    (SOS_mat[k, 0], SOS_mat[k, 1], SOS_mat[k, 2]))
            f.write('    %+-13e, %+-13e,\n' % \
                    (-SOS_mat[k, 4], -SOS_mat[k, 5]))
        else:
            f.write('    %+-13e, %+-13e, %+-13e,\n' % \
                    (SOS_mat[k, 0], SOS_mat[k, 1], SOS_mat[k, 2]))
            f.write('    %+-13e, %+-13e\n' % \
                    (-SOS_mat[k, 4], -SOS_mat[k, 5]))
    # for k in range(Ns):
    #     if (k < Ns-1):
    #         f.write('    %15.12f, %15.12f, %15.12f,\n' % \
    #                 (SOS_mat[k,0],SOS_mat[k,1],SOS_mat[k,2]))
    #         f.write('    %15.12f, %15.12f,\n' % \
    #                 (-SOS_mat[k,4],-SOS_mat[k,5]))
    #     else:
    #         f.write('    %15.12f, %15.12f, %15.12f,\n' % \
    #                 (SOS_mat[k,0],SOS_mat[k,1],SOS_mat[k,2]))
    #         f.write('    %15.12f, %15.12f\n' % \
    #                 (-SOS_mat[k,4],-SOS_mat[k,5]))
    f.write('};\n')
    f.write('/*********************************************************/\n')
    f.close()

'/*                     IIR SOS Filter Coefficients       */\n'

Write IIR SOS Header Files File format is compatible with CMSIS - DSP IIR Directform II Filter Functions Mark Wickert March 2015 - October 2016


def eye_plot(x,L,S=0):
    """
    Eye pattern plot of a baseband digital communications waveform.

    The signal must be real, but can be multivalued in terms of the underlying
    modulation scheme. Used for BPSK eye plots in the Case Study article.

    Parameters
    ----------
    x : ndarray of the real input data vector/array
    L : display length in samples (usually two symbols)
    S : start index

    Returns
    -------
    None : A plot window opens containing the eye plot

    Notes
    -----
    Increase S to eliminate filter transients.

    Examples
    --------
    1000 bits at 10 samples per bit with 'rc' shaping.

    >>> import matplotlib.pyplot as plt
    >>> from sk_dsp_comm import digitalcom as dc
    >>> x,b, data = dc.NRZ_bits(1000,10,'rc')
    >>> dc.eye_plot(x,20,60)
    >>> plt.show()
    """
    plt.figure(figsize=(6,4))
    idx = np.arange(0,L+1)
    plt.plot(idx,x[S:S+L+1],'b')
    k_max = int((len(x) - S)/L)-1
    for k in range(1,k_max):
         plt.plot(idx,x[S+k*L:S+L+1+k*L],'b')
    plt.grid()
    plt.xlabel('Time Index - n')
    plt.ylabel('Amplitude')
    plt.title('Eye Plot')
    return 0

def
eye_plot
(
x
,
L
,
S
=
0
)
:
plt
.
figure
(
figsize
=
(
6
,
4
)
)
idx
=
np
.
arange
(
0
,
L
+
1
)
plt
.
plot
(
idx
,
x
[
S
:
S
+
L
+
1
]
,
'b'
)
k_max
=
int
(
(
len
(
x
)
S
)
/
L
)
1
for
k
in
range
(
1
,
k_max
)
:
plt
.
plot
(
idx
,
x
[
S
+
k
*
L
:
S
+
L
+
1
+
k
*
L
]
,
'b'
)
plt
.
grid
(
)
plt
.
xlabel
(
'Time Index - n'
)
plt
.
ylabel
(
'Amplitude'
)
plt
.
title
(
'Eye Plot'
)
return
0

Eye pattern plot of a baseband digital communications waveform .


def scatter(x,Ns,start):
    """
    Sample a baseband digital communications waveform at the symbol spacing.

    Parameters
    ----------
    x : ndarray of the input digital comm signal
    Ns : number of samples per symbol (bit)
    start : the array index to start the sampling

    Returns
    -------
    xI : ndarray of the real part of x following sampling
    xQ : ndarray of the imaginary part of x following sampling

    Notes
    -----
    Normally the signal is complex, so the scatter plot contains
    clusters at point  in the complex plane. For a binary signal
    such as BPSK, the point centers are nominally +/-1 on the real
    axis. Start is used to eliminate transients from the FIR
    pulse shaping filters from appearing in the scatter plot.

    Examples
    --------
    >>> import matplotlib.pyplot as plt
    >>> from sk_dsp_comm import digitalcom as dc
    >>> x,b, data = dc.NRZ_bits(1000,10,'rc')

    Add some noise so points are now scattered about +/-1.

    >>> y = dc.cpx_AWGN(x,20,10)
    >>> yI,yQ = dc.scatter(y,10,60)
    >>> plt.plot(yI,yQ,'.')
    >>> plt.grid()
    >>> plt.xlabel('In-Phase')
    >>> plt.ylabel('Quadrature')
    >>> plt.axis('equal')
    >>> plt.show()
    """
    xI = np.real(x[start::Ns])
    xQ = np.imag(x[start::Ns])
    return xI, xQ

def scatter ( x , Ns , start ) : xI = np . real ( x [ start : : Ns ] ) xQ = np . imag ( x [ start : : Ns ] ) return xI , xQ

Sample a baseband digital communications waveform at the symbol spacing .


def MPSK_bb(N_symb,Ns,M,pulse='rect',alpha = 0.25,MM=6):
    """
    Generate a complex baseband MPSK signal with pulse shaping.

    Parameters
    ----------
    N_symb : number of MPSK symbols to produce
    Ns : the number of samples per bit,
    M : MPSK modulation order, e.g., 4, 8, 16, ...
    pulse_type : 'rect' , 'rc', 'src' (default 'rect')
    alpha : excess bandwidth factor(default 0.25)
    MM : single sided pulse duration (default = 6)

    Returns
    -------
    x : ndarray of the MPSK signal values
    b : ndarray of the pulse shape
    data : ndarray of the underlying data bits

    Notes
    -----
    Pulse shapes include 'rect' (rectangular), 'rc' (raised cosine),
    'src' (root raised cosine). The actual pulse length is 2*M+1 samples.
    This function is used by BPSK_tx in the Case Study article.

    Examples
    --------
    >>> from sk_dsp_comm import digitalcom as dc
    >>> import scipy.signal as signal
    >>> import matplotlib.pyplot as plt
    >>> x,b,data = dc.MPSK_bb(500,10,8,'src',0.35)
    >>> # Matched filter received signal x
    >>> y = signal.lfilter(b,1,x)
    >>> plt.plot(y.real[12*10:],y.imag[12*10:])
    >>> plt.xlabel('In-Phase')
    >>> plt.ylabel('Quadrature')
    >>> plt.axis('equal')
    >>> # Sample once per symbol
    >>> plt.plot(y.real[12*10::10],y.imag[12*10::10],'r.')
    >>> plt.show()
    """
    data = np.random.randint(0,M,N_symb)
    xs = np.exp(1j*2*np.pi/M*data)
    x = np.hstack((xs.reshape(N_symb,1),np.zeros((N_symb,int(Ns)-1))))
    x =x.flatten()
    if pulse.lower() == 'rect':
        b = np.ones(int(Ns))
    elif pulse.lower() == 'rc':
        b = rc_imp(Ns,alpha,MM)
    elif pulse.lower() == 'src':
        b = sqrt_rc_imp(Ns,alpha,MM)
    else:
        raise ValueError('pulse type must be rec, rc, or src')
    x = signal.lfilter(b,1,x)
    if M == 4:
        x = x*np.exp(1j*np.pi/4); # For QPSK points in quadrants
    return x,b/float(Ns),data

def
MPSK_bb
(
N_symb
,
Ns
,
M
,
pulse
=
'rect'
,
alpha
=
0.25
,
MM
=
6
)
:
data
=
np
.
random
.
randint
(
0
,
M
,
N_symb
)
xs
=
np
.
exp
(
1j
*
2
*
np
.
pi
/
M
*
data
)
x
=
np
.
hstack
(
(
xs
.
reshape
(
N_symb
,
1
)
,
np
.
zeros
(
(
N_symb
,
int
(
Ns
)
1
)
)
)
)
x
=
x
.
flatten
(
)
if
pulse
.
lower
(
)
==
'rect'
:
b
=
np
.
ones
(
int
(
Ns
)
)
elif
pulse
.
lower
(
)
==
'rc'
:
b
=
rc_imp
(
Ns
,
alpha
,
MM
)
elif
pulse
.
lower
(
)
==
'src'
:
b
=
sqrt_rc_imp
(
Ns
,
alpha
,
MM
)
else
:
raise
ValueError
(
'pulse type must be rec, rc, or src'
)
x
=
signal
.
lfilter
(
b
,
1
,
x
)
if
M
==
4
:
x
=
x
*
np
.
exp
(
1j
*
np
.
pi
/
4
)
return
x
,
b
/
float
(
Ns
)
,
data

Generate a complex baseband MPSK signal with pulse shaping .


def QPSK_rx(fc,N_symb,Rs,EsN0=100,fs=125,lfsr_len=10,phase=0,pulse='src'):
    """
    This function generates
    """
    Ns = int(np.round(fs/Rs))
    print('Ns = ', Ns)
    print('Rs = ', fs/float(Ns))
    print('EsN0 = ', EsN0, 'dB')
    print('phase = ', phase, 'degrees')
    print('pulse = ', pulse)
    x, b, data = QPSK_bb(N_symb,Ns,lfsr_len,pulse)
    # Add AWGN to x
    x = cpx_AWGN(x,EsN0,Ns)
    n = np.arange(len(x))
    xc = x*np.exp(1j*2*np.pi*fc/float(fs)*n) * np.exp(1j*phase)
    return xc, b, data

def
QPSK_rx
(
fc
,
N_symb
,
Rs
,
EsN0
=
100
,
fs
=
125
,
lfsr_len
=
10
,
phase
=
0
,
pulse
=
'src'
)
:
Ns
=
int
(
np
.
round
(
fs
/
Rs
)
)
print
(
'Ns = '
,
Ns
)
print
(
'Rs = '
,
fs
/
float
(
Ns
)
)
print
(
'EsN0 = '
,
EsN0
,
'dB'
)
print
(
'phase = '
,
phase
,
'degrees'
)
print
(
'pulse = '
,
pulse
)
x
,
b
,
data
=
QPSK_bb
(
N_symb
,
Ns
,
lfsr_len
,
pulse
)
x
=
cpx_AWGN
(
x
,
EsN0
,
Ns
)
n
=
np
.
arange
(
len
(
x
)
)
xc
=
x
*
np
.
exp
(
1j
*
2
*
np
.
pi
*
fc
/
float
(
fs
)
*
n
)
*
np
.
exp
(
1j
*
phase
)
return
xc
,
b
,
data


def rc_imp(Ns,alpha,M=6):
    """
    A truncated raised cosine pulse used in digital communications.

    The pulse shaping factor :math:`0 < \\alpha < 1` is required as well as the
    truncation factor M which sets the pulse duration to be :math:`2*M*T_{symbol}`.

    Parameters
    ----------
    Ns : number of samples per symbol
    alpha : excess bandwidth factor on (0, 1), e.g., 0.35
    M : equals RC one-sided symbol truncation factor

    Returns
    -------
    b : ndarray containing the pulse shape

    See Also
    --------
    sqrt_rc_imp

    Notes
    -----
    The pulse shape b is typically used as the FIR filter coefficients
    when forming a pulse shaped digital communications waveform.

    Examples
    --------
    Ten samples per symbol and :math:`\\alpha = 0.35`.

    >>> import matplotlib.pyplot as plt
    >>> from sk_dsp_comm.digitalcom import rc_imp
    >>> from numpy import arange
    >>> b = rc_imp(10,0.35)
    >>> n = arange(-10*6,10*6+1)
    >>> plt.stem(n,b)
    >>> plt.show()
    """
    # Design the filter
    n = np.arange(-M*Ns,M*Ns+1)
    b = np.zeros(len(n))
    a = alpha
    Ns *= 1.0
    for i in range(len(n)):
        if (1 - 4*(a*n[i]/Ns)**2) == 0:
            b[i] = np.pi/4*np.sinc(1/(2.*a))
        else:
            b[i] = np.sinc(n[i]/Ns)*np.cos(np.pi*a*n[i]/Ns)/(1 - 4*(a*n[i]/Ns)**2)
    return b

def rc_imp ( Ns , alpha , M = 6 ) : n = np . arange ( - M * Ns , M * Ns + 1 ) b = np . zeros ( len ( n ) ) a = alpha Ns *= 1.0 for i in range ( len ( n ) ) : if ( 1 - 4 * ( a * n [ i ] / Ns ) ** 2 ) == 0 : b [ i ] = np . pi / 4 * np . sinc ( 1 / ( 2. * a ) ) else : b [ i ] = np . sinc ( n [ i ] / Ns ) * np . cos ( np . pi * a * n [ i ] / Ns ) / ( 1 - 4 * ( a * n [ i ] / Ns ) ** 2 ) return b

A truncated raised cosine pulse used in digital communications .


def sqrt_rc_imp(Ns,alpha,M=6):
    """
    A truncated square root raised cosine pulse used in digital communications.

    The pulse shaping factor :math:`0 < \\alpha < 1` is required as well as the
    truncation factor M which sets the pulse duration to be :math:`2*M*T_{symbol}`.

    Parameters
    ----------
    Ns : number of samples per symbol
    alpha : excess bandwidth factor on (0, 1), e.g., 0.35
    M : equals RC one-sided symbol truncation factor

    Returns
    -------
    b : ndarray containing the pulse shape

    Notes
    -----
    The pulse shape b is typically used as the FIR filter coefficients
    when forming a pulse shaped digital communications waveform. When
    square root raised cosine (SRC) pulse is used to generate Tx signals and
    at the receiver used as a matched filter (receiver FIR filter), the
    received signal is now raised cosine shaped, thus having zero
    intersymbol interference and the optimum removal of additive white
    noise if present at the receiver input.

    Examples
    --------
    Ten samples per symbol and :math:`\\alpha = 0.35`.

    >>> import matplotlib.pyplot as plt
    >>> from numpy import arange
    >>> from sk_dsp_comm.digitalcom import sqrt_rc_imp
    >>> b = sqrt_rc_imp(10,0.35)
    >>> n = arange(-10*6,10*6+1)
    >>> plt.stem(n,b)
    >>> plt.show()
    """
    # Design the filter
    n = np.arange(-M*Ns,M*Ns+1)
    b = np.zeros(len(n))
    Ns *= 1.0
    a = alpha
    for i in range(len(n)):
       if abs(1 - 16*a**2*(n[i]/Ns)**2) <= np.finfo(np.float).eps/2:
           b[i] = 1/2.*((1+a)*np.sin((1+a)*np.pi/(4.*a))-(1-a)*np.cos((1-a)*np.pi/(4.*a))+(4*a)/np.pi*np.sin((1-a)*np.pi/(4.*a)))
       else:
           b[i] = 4*a/(np.pi*(1 - 16*a**2*(n[i]/Ns)**2))
           b[i] = b[i]*(np.cos((1+a)*np.pi*n[i]/Ns) + np.sinc((1-a)*n[i]/Ns)*(1-a)*np.pi/(4.*a))
    return b

A truncated square root raised cosine pulse used in digital communications .


def my_psd(x,NFFT=2**10,Fs=1):
    """
    A local version of NumPy's PSD function that returns the plot arrays.

    A mlab.psd wrapper function that returns two ndarrays;
    makes no attempt to auto plot anything.

    Parameters
    ----------
    x : ndarray input signal
    NFFT : a power of two, e.g., 2**10 = 1024
    Fs : the sampling rate in Hz

    Returns
    -------
    Px : ndarray of the power spectrum estimate
    f : ndarray of frequency values

    Notes
    -----
    This function makes it easier to overlay spectrum plots because
    you have better control over the axis scaling than when using psd()
    in the autoscale mode.

    Examples
    --------
    >>> import matplotlib.pyplot as plt
    >>> from sk_dsp_comm import digitalcom as dc
    >>> from numpy import log10
    >>> x,b, data = dc.NRZ_bits(10000,10)
    >>> Px,f = dc.my_psd(x,2**10,10)
    >>> plt.plot(f, 10*log10(Px))
    >>> plt.show()
    """
    Px,f = pylab.mlab.psd(x,NFFT,Fs)
    return Px.flatten(), f

def my_psd ( x , NFFT = 2 ** 10 , Fs = 1 ) : Px , f = pylab . mlab . psd ( x , NFFT , Fs ) return Px . flatten ( ) , f

A local version of NumPy s PSD function that returns the plot arrays .


def to_bin(data, width):
    """
    Convert an unsigned integer to a numpy binary array with the first
    element the MSB and the last element the LSB.
    """
    data_str = bin(data & (2**width-1))[2:].zfill(width)
    return [int(x) for x in tuple(data_str)]

def to_bin ( data , width ) : data_str = bin ( data & ( 2 ** width - 1 ) ) [ 2 : ] . zfill ( width ) return [ int ( x ) for x in tuple ( data_str ) ]

Convert an unsigned integer to a numpy binary array with the first element the MSB and the last element the LSB .


def from_bin(bin_array):
    """
    Convert binary array back a nonnegative integer. The array length is
    the bit width. The first input index holds the MSB and the last holds the LSB.
    """
    width = len(bin_array)
    bin_wgts = 2**np.arange(width-1,-1,-1)
    return int(np.dot(bin_array,bin_wgts))

def from_bin ( bin_array ) : width = len ( bin_array ) bin_wgts = 2 ** np . arange ( width - 1 , - 1 , - 1 ) return int ( np . dot ( bin_array , bin_wgts ) )

Convert binary array back a nonnegative integer . The array length is the bit width . The first input index holds the MSB and the last holds the LSB .


def filter(self,x):
        """
        Filter the signal
        """
        y = signal.lfilter(self.b,[1],x)
        return y

def filter ( self , x ) : y = signal . lfilter ( self . b , [ 1 ] , x ) return y


def filter(self,x):
        """
        Filter the signal using second-order sections
        """
        y = signal.sosfilt(self.sos,x)
        return y

def filter ( self , x ) : y = signal . sosfilt ( self . sos , x ) return y

Filter the signal using second - order sections


def freq_resp(self, mode= 'dB', fs = 8000, ylim = [-100,2]):
        """
        Frequency response plot
        """
        iir_d.freqz_resp_cas_list([self.sos],mode,fs=fs)
        pylab.grid()
        pylab.ylim(ylim)

def freq_resp ( self , mode = 'dB' , fs = 8000 , ylim = [ - 100 , 2 ] ) : iir_d . freqz_resp_cas_list ( [ self . sos ] , mode , fs = fs ) pylab . grid ( ) pylab . ylim ( ylim )


def _select_manager(backend_name):
    """Select the proper LockManager based on the current backend used by Celery.

    :raise NotImplementedError: If Celery is using an unsupported backend.

    :param str backend_name: Class name of the current Celery backend. Usually value of
        current_app.extensions['celery'].celery.backend.__class__.__name__.

    :return: Class definition object (not instance). One of the _LockManager* classes.
    """
    if backend_name == 'RedisBackend':
        lock_manager = _LockManagerRedis
    elif backend_name == 'DatabaseBackend':
        lock_manager = _LockManagerDB
    else:
        raise NotImplementedError
    return lock_manager

def _select_manager ( backend_name ) : if backend_name == 'RedisBackend' : lock_manager = _LockManagerRedis elif backend_name == 'DatabaseBackend' : lock_manager = _LockManagerDB else : raise NotImplementedError return lock_manager

Select the proper LockManager based on the current backend used by Celery .


def single_instance(func=None, lock_timeout=None, include_args=False):
    """Celery task decorator. Forces the task to have only one running instance at a time.

    Use with binded tasks (@celery.task(bind=True)).

    Modeled after:
    http://loose-bits.com/2010/10/distributed-task-locking-in-celery.html
    http://blogs.it.ox.ac.uk/inapickle/2012/01/05/python-decorators-with-optional-arguments/

    Written by @Robpol86.

    :raise OtherInstanceError: If another instance is already running.

    :param function func: The function to decorate, must be also decorated by @celery.task.
    :param int lock_timeout: Lock timeout in seconds plus five more seconds, in-case the task crashes and fails to
        release the lock. If not specified, the values of the task's soft/hard limits are used. If all else fails,
        timeout will be 5 minutes.
    :param bool include_args: Include the md5 checksum of the arguments passed to the task in the Redis key. This allows
        the same task to run with different arguments, only stopping a task from running if another instance of it is
        running with the same arguments.
    """
    if func is None:
        return partial(single_instance, lock_timeout=lock_timeout, include_args=include_args)

    @wraps(func)
    def wrapped(celery_self, *args, **kwargs):
        """Wrapped Celery task, for single_instance()."""
        # Select the manager and get timeout.
        timeout = (
            lock_timeout or celery_self.soft_time_limit or celery_self.time_limit
            or celery_self.app.conf.get('CELERYD_TASK_SOFT_TIME_LIMIT')
            or celery_self.app.conf.get('CELERYD_TASK_TIME_LIMIT')
            or (60 * 5)
        )
        manager_class = _select_manager(celery_self.backend.__class__.__name__)
        lock_manager = manager_class(celery_self, timeout, include_args, args, kwargs)

        # Lock and execute.
        with lock_manager:
            ret_value = func(*args, **kwargs)
        return ret_value
    return wrapped

def single_instance ( func = None , lock_timeout = None , include_args = False ) : if func is None : return partial ( single_instance , lock_timeout = lock_timeout , include_args = include_args ) @ wraps ( func ) def wrapped ( celery_self , * args , ** kwargs ) : timeout = ( lock_timeout or celery_self . soft_time_limit or celery_self . time_limit or celery_self . app . conf . get ( 'CELERYD_TASK_SOFT_TIME_LIMIT' ) or celery_self . app . conf . get ( 'CELERYD_TASK_TIME_LIMIT' ) or ( 60 * 5 ) ) manager_class = _select_manager ( celery_self . backend . __class__ . __name__ ) lock_manager = manager_class ( celery_self , timeout , include_args , args , kwargs ) with lock_manager : ret_value = func ( * args , ** kwargs ) return ret_value return wrapped

Celery task decorator . Forces the task to have only one running instance at a time .


def reset_lock(self):
        """Removed the lock regardless of timeout."""
        redis_key = self.CELERY_LOCK.format(task_id=self.task_identifier)
        self.celery_self.backend.client.delete(redis_key)

def reset_lock ( self ) : redis_key = self . CELERY_LOCK . format ( task_id = self . task_identifier ) self . celery_self . backend . client . delete ( redis_key )

Removed the lock regardless of timeout .


def init_app(self, app):
        """Actual method to read celery settings from app configuration and initialize the celery instance.

        :param app: Flask application instance.
        """
        _state._register_app = self.original_register_app  # Restore Celery app registration function.
        if not hasattr(app, 'extensions'):
            app.extensions = dict()
        if 'celery' in app.extensions:
            raise ValueError('Already registered extension CELERY.')
        app.extensions['celery'] = _CeleryState(self, app)

        # Instantiate celery and read config.
        super(Celery, self).__init__(app.import_name, broker=app.config['CELERY_BROKER_URL'])

        # Set result backend default.
        if 'CELERY_RESULT_BACKEND' in app.config:
            self._preconf['CELERY_RESULT_BACKEND'] = app.config['CELERY_RESULT_BACKEND']

        self.conf.update(app.config)
        task_base = self.Task

        # Add Flask app context to celery instance.
        class ContextTask(task_base):
            def __call__(self, *_args, **_kwargs):
                with app.app_context():
                    return task_base.__call__(self, *_args, **_kwargs)
        setattr(ContextTask, 'abstract', True)
        setattr(self, 'Task', ContextTask)

def
init_app
(
self
,
app
)
:
_state
.
_register_app
=
self
.
original_register_app
if
not
hasattr
(
app
,
'extensions'
)
:
app
.
extensions
=
dict
(
)
if
'celery'
in
app
.
extensions
:
raise
ValueError
(
'Already registered extension CELERY.'
)
app
.
extensions
[
'celery'
]
=
_CeleryState
(
self
,
app
)
super
(
Celery
,
self
)
.
__init__
(
app
.
import_name
,
broker
=
app
.
config
[
'CELERY_BROKER_URL'
]
)
if
'CELERY_RESULT_BACKEND'
in
app
.
config
:
self
.
_preconf
[
'CELERY_RESULT_BACKEND'
]
=
app
.
config
[
'CELERY_RESULT_BACKEND'
]
self
.
conf
.
update
(
app
.
config
)
task_base
=
self
.
Task
class
ContextTask
(
task_base
)
:
def
__call__
(
self
,
*
_args
,
**
_kwargs
)
:
with
app
.
app_context
(
)
:
return
task_base
.
__call__
(
self
,
*
_args
,
**
_kwargs
)
setattr
(
ContextTask
,
'abstract'
,
True
)
setattr
(
self
,
'Task'
,
ContextTask
)

Actual method to read celery settings from app configuration and initialize the celery instance .


def iter_chunksize(num_samples, chunksize):
    """Iterator used to iterate in chunks over an array of size `num_samples`.
    At each iteration returns `chunksize` except for the last iteration.
    """
    last_chunksize = int(np.mod(num_samples, chunksize))
    chunksize = int(chunksize)
    for _ in range(int(num_samples) // chunksize):
        yield chunksize
    if last_chunksize > 0:
        yield last_chunksize

def iter_chunksize ( num_samples , chunksize ) : last_chunksize = int ( np . mod ( num_samples , chunksize ) ) chunksize = int ( chunksize ) for _ in range ( int ( num_samples ) // chunksize ) : yield chunksize if last_chunksize > 0 : yield last_chunksize

Iterator used to iterate in chunks over an array of size num_samples . At each iteration returns chunksize except for the last iteration .


def reduce_chunk(func, array):
    """Reduce with `func`, chunk by chunk, the passed pytable `array`.
    """
    res = []
    for slice in iter_chunk_slice(array.shape[-1], array.chunkshape[-1]):
        res.append(func(array[..., slice]))
    return func(res)

def reduce_chunk ( func , array ) : res = [ ] for slice in iter_chunk_slice ( array . shape [ - 1 ] , array . chunkshape [ - 1 ] ) : res . append ( func ( array [ ... , slice ] ) ) return func ( res )

Reduce with func chunk by chunk the passed pytable array .


def map_chunk(func, array, out_array):
    """Map with `func`, chunk by chunk, the input pytable `array`.
    The result is stored in the output pytable array `out_array`.
    """
    for slice in iter_chunk_slice(array.shape[-1], array.chunkshape[-1]):
        out_array.append(func(array[..., slice]))
    return out_array

def map_chunk ( func , array , out_array ) : for slice in iter_chunk_slice ( array . shape [ - 1 ] , array . chunkshape [ - 1 ] ) : out_array . append ( func ( array [ ... , slice ] ) ) return out_array

Map with func chunk by chunk the input pytable array . The result is stored in the output pytable array out_array .


def merge_ph_times(times_list, times_par_list, time_block):
    """Build an array of timestamps joining the arrays in `ph_times_list`.
    `time_block` is the duration of each array of timestamps.
    """
    offsets = np.arange(len(times_list)) * time_block
    cum_sizes = np.cumsum([ts.size for ts in times_list])
    times = np.zeros(cum_sizes[-1])
    times_par = np.zeros(cum_sizes[-1], dtype='uint8')
    i1 = 0
    for i2, ts, ts_par, offset in zip(cum_sizes, times_list, times_par_list,
                                      offsets):
        times[i1:i2] = ts + offset
        times_par[i1:i2] = ts_par
        i1 = i2
    return times, times_par

def merge_ph_times ( times_list , times_par_list , time_block ) : offsets = np . arange ( len ( times_list ) ) * time_block cum_sizes = np . cumsum ( [ ts . size for ts in times_list ] ) times = np . zeros ( cum_sizes [ - 1 ] ) times_par = np . zeros ( cum_sizes [ - 1 ] , dtype = 'uint8' ) i1 = 0 for i2 , ts , ts_par , offset in zip ( cum_sizes , times_list , times_par_list , offsets ) : times [ i1 : i2 ] = ts + offset times_par [ i1 : i2 ] = ts_par i1 = i2 return times , times_par

Build an array of timestamps joining the arrays in ph_times_list . time_block is the duration of each array of timestamps .


def merge_DA_ph_times(ph_times_d, ph_times_a):
    """Returns a merged timestamp array for Donor+Accept. and bool mask for A.
    """
    ph_times = np.hstack([ph_times_d, ph_times_a])
    a_em = np.hstack([np.zeros(ph_times_d.size, dtype=np.bool),
                      np.ones(ph_times_a.size, dtype=np.bool)])
    index_sort = ph_times.argsort()
    return ph_times[index_sort], a_em[index_sort]

def merge_DA_ph_times ( ph_times_d , ph_times_a ) : ph_times = np . hstack ( [ ph_times_d , ph_times_a ] ) a_em = np . hstack ( [ np . zeros ( ph_times_d . size , dtype = np . bool ) , np . ones ( ph_times_a . size , dtype = np . bool ) ] ) index_sort = ph_times . argsort ( ) return ph_times [ index_sort ] , a_em [ index_sort ]

Returns a merged timestamp array for Donor + Accept . and bool mask for A .


def load_PSFLab_file(fname):
    """Load the array `data` in the .mat file `fname`."""
    if os.path.exists(fname) or os.path.exists(fname + '.mat'):
        return loadmat(fname)['data']
    else:
        raise IOError("Can't find PSF file '%s'" % fname)

def
load_PSFLab_file
(
fname
)
:
if
os
.
path
.
exists
(
fname
)
or
os
.
path
.
exists
(
fname
+
'.mat'
)
:
return
loadmat
(
fname
)
[
'data'
]
else
:
raise
IOError
(
"Can't find PSF file '%s'"
%
fname
)

Load the array data in the . mat file fname .


def hash(self):
        """Return an hash string computed on the PSF data."""
        hash_list = []
        for key, value in sorted(self.__dict__.items()):
            if not callable(value):
                if isinstance(value, np.ndarray):
                    hash_list.append(value.tostring())
                else:
                    hash_list.append(str(value))
        return hashlib.md5(repr(hash_list).encode()).hexdigest()

def hash ( self ) : hash_list = [ ] for key , value in sorted ( self . __dict__ . items ( ) ) : if not callable ( value ) : if isinstance ( value , np . ndarray ) : hash_list . append ( value . tostring ( ) ) else : hash_list . append ( str ( value ) ) return hashlib . md5 ( repr ( hash_list ) . encode ( ) ) . hexdigest ( )

Return an hash string computed on the PSF data .


def git_path_valid(git_path=None):
    """
    Check whether the git executable is found.
    """
    if git_path is None and GIT_PATH is None:
        return False
    if git_path is None: git_path = GIT_PATH
    try:
        call([git_path, '--version'])
        return True
    except OSError:
        return False

def git_path_valid ( git_path = None ) : if git_path is None and GIT_PATH is None : return False if git_path is None : git_path = GIT_PATH try : call ( [ git_path , '--version' ] ) return True except OSError : return False

Check whether the git executable is found .


def get_git_version(git_path=None):
    """
    Get the Git version.
    """
    if git_path is None: git_path = GIT_PATH
    git_version = check_output([git_path, "--version"]).split()[2]
    return git_version

def get_git_version ( git_path = None ) : if git_path is None : git_path = GIT_PATH git_version = check_output ( [ git_path , "--version" ] ) . split ( ) [ 2 ] return git_version


def check_clean_status(git_path=None):
    """
    Returns whether there are uncommitted changes in the working dir.
    """
    output = get_status(git_path)
    is_unmodified = (len(output.strip()) == 0)
    return is_unmodified

def check_clean_status ( git_path = None ) : output = get_status ( git_path ) is_unmodified = ( len ( output . strip ( ) ) == 0 ) return is_unmodified

Returns whether there are uncommitted changes in the working dir .


def get_last_commit_line(git_path=None):
    """
    Get one-line description of HEAD commit for repository in current dir.
    """
    if git_path is None: git_path = GIT_PATH
    output = check_output([git_path, "log", "--pretty=format:'%ad %h %s'",
                           "--date=short", "-n1"])
    return output.strip()[1:-1]

def
get_last_commit_line
(
git_path
=
)
:
if
git_path
is
:
git_path
=
GIT_PATH
output
=
check_output
(
[
git_path
,
"log"
,
"--pretty=format:'%ad %h %s'"
,
"--date=short"
,
"-n1"
]
)
return
output
.
strip
(
)
[
1
:
1
]

Get one - line description of HEAD commit for repository in current dir .


def get_last_commit(git_path=None):
    """
    Get the HEAD commit SHA1 of repository in current dir.
    """
    if git_path is None: git_path = GIT_PATH
    line = get_last_commit_line(git_path)
    revision_id = line.split()[1]
    return revision_id

def get_last_commit ( git_path = None ) : if git_path is None : git_path = GIT_PATH line = get_last_commit_line ( git_path ) revision_id = line . split ( ) [ 1 ] return revision_id

Get the HEAD commit SHA1 of repository in current dir .


def print_summary(string='Repository', git_path=None):
    """
    Print the last commit line and eventual uncommitted changes.
    """
    if git_path is None: git_path = GIT_PATH

    # If git is available, check fretbursts version
    if not git_path_valid():
        print('\n%s revision unknown (git not found).' % string)
    else:
        last_commit = get_last_commit_line()
        print('\n{} revision:\n {}\n'.format(string, last_commit))
        if not check_clean_status():
            print('\nWARNING -> Uncommitted changes:')
            print(get_status())

Print the last commit line and eventual uncommitted changes .


def get_bromo_fnames_da(d_em_kHz, d_bg_kHz, a_em_kHz, a_bg_kHz,
        ID='1+2+3+4+5+6', t_tot='480', num_p='30', pM='64',
        t_step=0.5e-6, D=1.2e-11, dir_=''):
    """Get filenames for donor and acceptor timestamps for the given parameters
    """

    clk_p = t_step/32. # with t_step=0.5us -> 156.25 ns
    E_sim = 1.*a_em_kHz/(a_em_kHz + d_em_kHz)

    FRET_val = 100.*E_sim
    print("Simulated FRET value: %.1f%%" % FRET_val)

    d_em_kHz_str = "%04d" % d_em_kHz
    a_em_kHz_str = "%04d" % a_em_kHz
    d_bg_kHz_str = "%04.1f" % d_bg_kHz
    a_bg_kHz_str = "%04.1f" % a_bg_kHz

    print("D: EM %s BG %s " % (d_em_kHz_str, d_bg_kHz_str))
    print("A: EM %s BG %s " % (a_em_kHz_str, a_bg_kHz_str))

    fname_d = ('ph_times_{t_tot}s_D{D}_{np}P_{pM}pM_'
               'step{ts_us}us_ID{ID}_EM{em}kHz_BG{bg}kHz.npy').format(
                       em=d_em_kHz_str, bg=d_bg_kHz_str, t_tot=t_tot, pM=pM,
                       np=num_p, ID=ID, ts_us=t_step*1e6, D=D)

    fname_a = ('ph_times_{t_tot}s_D{D}_{np}P_{pM}pM_'
               'step{ts_us}us_ID{ID}_EM{em}kHz_BG{bg}kHz.npy').format(
                       em=a_em_kHz_str, bg=a_bg_kHz_str, t_tot=t_tot, pM=pM,
                       np=num_p, ID=ID, ts_us=t_step*1e6, D=D)
    print(fname_d)
    print(fname_a)

    name = ('BroSim_E{:.1f}_dBG{:.1f}k_aBG{:.1f}k_'
            'dEM{:.0f}k').format(FRET_val, d_bg_kHz, a_bg_kHz, d_em_kHz)

    return dir_+fname_d, dir_+fname_a, name, clk_p, E_sim


def set_sim_params(self, nparams, attr_params):
        """Store parameters in `params` in `h5file.root.parameters`.

        `nparams` (dict)
            A dict as returned by `get_params()` in `ParticlesSimulation()`
            The format is:
            keys:
                used as parameter name
            values: (2-elements tuple)
                first element is the parameter value
                second element is a string used as "title" (description)
        `attr_params` (dict)
            A dict whole items are stored as attributes in '/parameters'
        """
        for name, value in nparams.items():
            val = value[0] if value[0] is not None else 'none'
            self.h5file.create_array('/parameters', name, obj=val,
                                     title=value[1])
        for name, value in attr_params.items():
            self.h5file.set_node_attr('/parameters', name, value)

def set_sim_params ( self , nparams , attr_params ) : for name , value in nparams . items ( ) : val = value [ 0 ] if value [ 0 ] is not None else 'none' self . h5file . create_array ( '/parameters' , name , obj = val , title = value [ 1 ] ) for name , value in attr_params . items ( ) : self . h5file . set_node_attr ( '/parameters' , name , value )

Store parameters in params in h5file . root . parameters .


def volume(self):
        """Box volume in m^3."""
        return (self.x2 - self.x1) * (self.y2 - self.y1) * (self.z2 - self.z1)

def volume ( self ) : return ( self . x2 - self . x1 ) * ( self . y2 - self . y1 ) * ( self . z2 - self . z1 )


def _generate(num_particles, D, box, rs):
        """Generate a list of `Particle` objects."""
        X0 = rs.rand(num_particles) * (box.x2 - box.x1) + box.x1
        Y0 = rs.rand(num_particles) * (box.y2 - box.y1) + box.y1
        Z0 = rs.rand(num_particles) * (box.z2 - box.z1) + box.z1
        return [Particle(D=D, x0=x0, y0=y0, z0=z0)
                for x0, y0, z0 in zip(X0, Y0, Z0)]

def _generate ( num_particles , D , box , rs ) : X0 = rs . rand ( num_particles ) * ( box . x2 - box . x1 ) + box . x1 Y0 = rs . rand ( num_particles ) * ( box . y2 - box . y1 ) + box . y1 Z0 = rs . rand ( num_particles ) * ( box . z2 - box . z1 ) + box . z1 return [ Particle ( D = D , x0 = x0 , y0 = y0 , z0 = z0 ) for x0 , y0 , z0 in zip ( X0 , Y0 , Z0 ) ]


def add(self, num_particles, D):
        """Add particles with diffusion coefficient `D` at random positions.
        """
        self._plist += self._generate(num_particles, D, box=self.box,
                                      rs=self.rs)

def add ( self , num_particles , D ) : self . _plist += self . _generate ( num_particles , D , box = self . box , rs = self . rs )

Add particles with diffusion coefficient D at random positions .


def datafile_from_hash(hash_, prefix, path):
        """Return pathlib.Path for a data-file with given hash and prefix.
        """
        pattern = '%s_%s*.h*' % (prefix, hash_)
        datafiles = list(path.glob(pattern))
        if len(datafiles) == 0:
            raise NoMatchError('No matches for "%s"' % pattern)
        if len(datafiles) > 1:
            raise MultipleMatchesError('More than 1 match for "%s"' % pattern)
        return datafiles[0]

def
datafile_from_hash
(
hash_
,
prefix
,
path
)
:
pattern
=
'%s_%s*.h*'
%
(
prefix
,
hash_
)
datafiles
=
list
(
path
.
glob
(
pattern
)
)
if
len
(
datafiles
)
==
0
:
raise
NoMatchError
(
'No matches for "%s"'
%
pattern
)
if
len
(
datafiles
)
>
1
:
raise
MultipleMatchesError
(
'More than 1 match for "%s"'
%
pattern
)
return
datafiles
[
0
]

Return pathlib . Path for a data - file with given hash and prefix .


def _get_group_randomstate(rs, seed, group):
        """Return a RandomState, equal to the input unless rs is None.

        When rs is None, try to get the random state from the
        'last_random_state' attribute in `group`. When not available,
        use `seed` to generate a random state. When seed is None the returned
        random state will have a random seed.
        """
        if rs is None:
            rs = np.random.RandomState(seed=seed)
            # Try to set the random state from the last session to preserve
            # a single random stream when simulating timestamps multiple times
            if 'last_random_state' in group._v_attrs:
                rs.set_state(group._v_attrs['last_random_state'])
                print("INFO: Random state set to last saved state in '%s'." %
                      group._v_name)
            else:
                print("INFO: Random state initialized from seed (%d)." % seed)
        return rs

Return a RandomState equal to the input unless rs is None .


def compact_name(self, hashsize=6):
        """Compact representation of all simulation parameters
        """
        # this can be made more robust for ID > 9 (double digit)
        s = self.compact_name_core(hashsize, t_max=True)
        s += "_ID%d-%d" % (self.ID, self.EID)
        return s

def compact_name ( self , hashsize = 6 ) : s = self . compact_name_core ( hashsize , t_max = True ) s += "_ID%d-%d" % ( self . ID , self . EID ) return s


def numeric_params(self):
        """A dict containing all the simulation numeric-parameters.

        The values are 2-element tuples: first element is the value and
        second element is a string describing the parameter (metadata).
        """
        nparams = dict(
            D = (self.diffusion_coeff.mean(), 'Diffusion coefficient (m^2/s)'),
            np = (self.num_particles, 'Number of simulated particles'),
            t_step = (self.t_step, 'Simulation time-step (s)'),
            t_max = (self.t_max, 'Simulation total time (s)'),
            ID = (self.ID, 'Simulation ID (int)'),
            EID = (self.EID, 'IPython Engine ID (int)'),
            pico_mol = (self.concentration() * 1e12,
                        'Particles concentration (pM)'))
        return nparams

def
numeric_params
(
self
)
:
nparams
=
dict
(
D
=
(
self
.
diffusion_coeff
.
mean
(
)
,
'Diffusion coefficient (m^2/s)'
)
,
np
=
(
self
.
num_particles
,
'Number of simulated particles'
)
,
t_step
=
(
self
.
t_step
,
'Simulation time-step (s)'
)
,
t_max
=
(
self
.
t_max
,
'Simulation total time (s)'
)
,
ID
=
(
self
.
ID
,
'Simulation ID (int)'
)
,
EID
=
(
self
.
EID
,
'IPython Engine ID (int)'
)
,
pico_mol
=
(
self
.
concentration
(
)
*
1e12
,
'Particles concentration (pM)'
)
)
return
nparams

A dict containing all the simulation numeric - parameters .


def print_sizes(self):
        """Print on-disk array sizes required for current set of parameters."""
        float_size = 4
        MB = 1024 * 1024
        size_ = self.n_samples * float_size
        em_size = size_ * self.num_particles / MB
        pos_size = 3 * size_ * self.num_particles / MB
        print("  Number of particles:", self.num_particles)
        print("  Number of time steps:", self.n_samples)
        print("  Emission array - 1 particle (float32): %.1f MB" % (size_ / MB))
        print("  Emission array (float32): %.1f MB" % em_size)
        print("  Position array (float32): %.1f MB " % pos_size)

Print on - disk array sizes required for current set of parameters .


def simulate_diffusion(self, save_pos=False, total_emission=True,
                           radial=False, rs=None, seed=1, path='./',
                           wrap_func=wrap_periodic,
                           chunksize=2**19, chunkslice='times', verbose=True):
        """Simulate Brownian motion trajectories and emission rates.

        This method performs the Brownian motion simulation using the current
        set of parameters. Before running this method you can check the
        disk-space requirements using :method:`print_sizes`.

        Results are stored to disk in HDF5 format and are accessible in
        in `self.emission`, `self.emission_tot` and `self.position` as
        pytables arrays.

        Arguments:
            save_pos (bool): if True, save the particles 3D trajectories
            total_emission (bool): if True, store only the total emission array
                containing the sum of emission of all the particles.
            rs (RandomState object): random state object used as random number
                generator. If None, use a random state initialized from seed.
            seed (uint): when `rs` is None, `seed` is used to initialize the
                random state, otherwise is ignored.
            wrap_func (function): the function used to apply the boundary
                condition (use :func:`wrap_periodic` or :func:`wrap_mirror`).
            path (string): a folder where simulation data is saved.
            verbose (bool): if False, prints no output.
        """
        if rs is None:
            rs = np.random.RandomState(seed=seed)
        self.open_store_traj(chunksize=chunksize, chunkslice=chunkslice,
                             radial=radial, path=path)
        # Save current random state for reproducibility
        self.traj_group._v_attrs['init_random_state'] = rs.get_state()

        em_store = self.emission_tot if total_emission else self.emission

        print('- Start trajectories simulation - %s' % ctime(), flush=True)
        if verbose:
            print('[PID %d] Diffusion time:' % os.getpid(), end='')
        i_chunk = 0
        t_chunk_size = self.emission.chunkshape[1]
        chunk_duration = t_chunk_size * self.t_step

        par_start_pos = self.particles.positions
        prev_time = 0
        for time_size in iter_chunksize(self.n_samples, t_chunk_size):
            if verbose:
                curr_time = int(chunk_duration * (i_chunk + 1))
                if curr_time > prev_time:
                    print(' %ds' % curr_time, end='', flush=True)
                    prev_time = curr_time

            POS, em = self._sim_trajectories(time_size, par_start_pos, rs,
                                             total_emission=total_emission,
                                             save_pos=save_pos, radial=radial,
                                             wrap_func=wrap_func)

            ## Append em to the permanent storage
            # if total_emission, data is just a linear array
            # otherwise is a 2-D array (self.num_particles, c_size)
            em_store.append(em)
            if save_pos:
                self.position.append(np.vstack(POS).astype('float32'))
            i_chunk += 1
            self.store.h5file.flush()

        # Save current random state
        self.traj_group._v_attrs['last_random_state'] = rs.get_state()
        self.store.h5file.flush()
        print('\n- End trajectories simulation - %s' % ctime(), flush=True)

def
simulate_diffusion
(
self
,
save_pos
=
False
,
total_emission
=
True
,
radial
=
False
,
rs
=
,
seed
=
1
,
path
=
'./'
,
wrap_func
=
wrap_periodic
,
chunksize
=
2
**
19
,
chunkslice
=
'times'
,
verbose
=
True
)
:
if
rs
is
:
rs
=
np
.
random
.
RandomState
(
seed
=
seed
)
self
.
open_store_traj
(
chunksize
=
chunksize
,
chunkslice
=
chunkslice
,
radial
=
radial
,
path
=
path
)
self
.
traj_group
.
_v_attrs
[
'init_random_state'
]
=
rs
.
get_state
(
)
em_store
=
self
.
emission_tot
if
total_emission
else
self
.
emission
print
(
'- Start trajectories simulation - %s'
%
ctime
(
)
,
flush
=
True
)
if
verbose
:
print
(
'[PID %d] Diffusion time:'
%
os
.
getpid
(
)
,
end
=
''
)
i_chunk
=
0
t_chunk_size
=
self
.
emission
.
chunkshape
[
1
]
chunk_duration
=
t_chunk_size
*
self
.
t_step
par_start_pos
=
self
.
particles
.
positions
prev_time
=
0
for
time_size
in
iter_chunksize
(
self
.
n_samples
,
t_chunk_size
)
:
if
verbose
:
curr_time
=
int
(
chunk_duration
*
(
i_chunk
+
1
)
)
if
curr_time
>
prev_time
:
print
(
' %ds'
%
curr_time
,
end
=
''
,
flush
=
True
)
prev_time
=
curr_time
POS
,
em
=
self
.
_sim_trajectories
(
time_size
,
par_start_pos
,
rs
,
total_emission
=
total_emission
,
save_pos
=
save_pos
,
radial
=
radial
,
wrap_func
=
wrap_func
)
em_store
.
append
(
em
)
if
save_pos
:
self
.
position
.
append
(
np
.
vstack
(
POS
)
.
astype
(
'float32'
)
)
i_chunk
+=
1
self
.
store
.
h5file
.
flush
(
)
self
.
traj_group
.
_v_attrs
[
'last_random_state'
]
=
rs
.
get_state
(
)
self
.
store
.
h5file
.
flush
(
)
print
(
'\n- End trajectories simulation - %s'
%
ctime
(
)
,
flush
=
True
)

Simulate Brownian motion trajectories and emission rates .


def _sim_timestamps(self, max_rate, bg_rate, emission, i_start, rs,
                        ip_start=0, scale=10, sort=True):
        """Simulate timestamps from emission trajectories.

        Uses attributes: `.t_step`.

        Returns:
            A tuple of two arrays: timestamps and particles.
        """
        counts_chunk = sim_timetrace_bg(emission, max_rate, bg_rate,
                                        self.t_step, rs=rs)
        nrows = emission.shape[0]
        if bg_rate is not None:
            nrows += 1
        assert counts_chunk.shape == (nrows, emission.shape[1])
        max_counts = counts_chunk.max()
        if max_counts == 0:
            return np.array([], dtype=np.int64), np.array([], dtype=np.int64)

        time_start = i_start * scale
        time_stop = time_start + counts_chunk.shape[1] * scale
        ts_range = np.arange(time_start, time_stop, scale, dtype='int64')

        # Loop for each particle to compute timestamps
        times_chunk_p = []
        par_index_chunk_p = []
        for ip, counts_chunk_ip in enumerate(counts_chunk):
            # Compute timestamps for particle ip for all bins with counts
            times_c_ip = []
            for v in range(1, max_counts + 1):
                times_c_ip.append(ts_range[counts_chunk_ip >= v])

            # Stack the timestamps from different "counts"
            t = np.hstack(times_c_ip)
            # Append current particle
            times_chunk_p.append(t)
            par_index_chunk_p.append(np.full(t.size, ip + ip_start, dtype='u1'))

        # Merge the arrays of different particles
        times_chunk = np.hstack(times_chunk_p)
        par_index_chunk = np.hstack(par_index_chunk_p)

        if sort:
            # Sort timestamps inside the merged chunk
            index_sort = times_chunk.argsort(kind='mergesort')
            times_chunk = times_chunk[index_sort]
            par_index_chunk = par_index_chunk[index_sort]

        return times_chunk, par_index_chunk

def _sim_timestamps ( self , max_rate , bg_rate , emission , i_start , rs , ip_start = 0 , scale = 10 , sort = True ) : counts_chunk = sim_timetrace_bg ( emission , max_rate , bg_rate , self . t_step , rs = rs ) nrows = emission . shape [ 0 ] if bg_rate is not None : nrows += 1 assert counts_chunk . shape == ( nrows , emission . shape [ 1 ] ) max_counts = counts_chunk . max ( ) if max_counts == 0 : return np . array ( [ ] , dtype = np . int64 ) , np . array ( [ ] , dtype = np . int64 ) time_start = i_start * scale time_stop = time_start + counts_chunk . shape [ 1 ] * scale ts_range = np . arange ( time_start , time_stop , scale , dtype = 'int64' ) times_chunk_p = [ ] par_index_chunk_p = [ ] for ip , counts_chunk_ip in enumerate ( counts_chunk ) : times_c_ip = [ ] for v in range ( 1 , max_counts + 1 ) : times_c_ip . append ( ts_range [ counts_chunk_ip >= v ] ) t = np . hstack ( times_c_ip ) times_chunk_p . append ( t ) par_index_chunk_p . append ( np . full ( t . size , ip + ip_start , dtype = 'u1' ) ) times_chunk = np . hstack ( times_chunk_p ) par_index_chunk = np . hstack ( par_index_chunk_p ) if sort : index_sort = times_chunk . argsort ( kind = 'mergesort' ) times_chunk = times_chunk [ index_sort ] par_index_chunk = par_index_chunk [ index_sort ] return times_chunk , par_index_chunk

Simulate timestamps from emission trajectories .


def simulate_timestamps_mix(self, max_rates, populations, bg_rate,
                                rs=None, seed=1, chunksize=2**16,
                                comp_filter=None, overwrite=False,
                                skip_existing=False, scale=10,
                                path=None, t_chunksize=None, timeslice=None):
        """Compute one timestamps array for a mixture of N populations.

        Timestamp data are saved to disk and accessible as pytables arrays in
        `._timestamps` and `._tparticles`.
        The background generated timestamps are assigned a
        conventional particle number (last particle index + 1).

        Arguments:
            max_rates (list): list of the peak max emission rate for each
                population.
            populations (list of slices): slices to `self.particles`
                defining each population.
            bg_rate (float, cps): rate for a Poisson background process
            rs (RandomState object): random state object used as random number
                generator. If None, use a random state initialized from seed.
            seed (uint): when `rs` is None, `seed` is used to initialize the
                random state, otherwise is ignored.
            chunksize (int): chunk size used for the on-disk timestamp array
            comp_filter (tables.Filter or None): compression filter to use
                for the on-disk `timestamps` and `tparticles` arrays.
                If None use default compression.
            overwrite (bool): if True, overwrite any pre-existing timestamps
                array. If False, never overwrite. The outcome of simulating an
                existing array is controlled by `skip_existing` flag.
            skip_existing (bool): if True, skip simulation if the same
                timestamps array is already present.
            scale (int): `self.t_step` is multiplied by `scale` to obtain the
                timestamps units in seconds.
            path (string): folder where to save the data.
            timeslice (float or None): timestamps are simulated until
                `timeslice` seconds. If None, simulate until `self.t_max`.
        """
        self.open_store_timestamp(chunksize=chunksize, path=path)
        rs = self._get_group_randomstate(rs, seed, self.ts_group)
        if t_chunksize is None:
            t_chunksize = self.emission.chunkshape[1]
        timeslice_size = self.n_samples
        if timeslice is not None:
            timeslice_size = timeslice // self.t_step

        name = self._get_ts_name_mix(max_rates, populations, bg_rate, rs=rs)
        kw = dict(name=name, clk_p=self.t_step / scale,
                  max_rates=max_rates, bg_rate=bg_rate, populations=populations,
                  num_particles=self.num_particles,
                  bg_particle=self.num_particles,
                  overwrite=overwrite, chunksize=chunksize)
        if comp_filter is not None:
            kw.update(comp_filter=comp_filter)
        try:
            self._timestamps, self._tparticles = (self.ts_store
                                                  .add_timestamps(**kw))
        except ExistingArrayError as e:
            if skip_existing:
                print(' - Skipping already present timestamps array.')
                return
            else:
                raise e

        self.ts_group._v_attrs['init_random_state'] = rs.get_state()
        self._timestamps.attrs['init_random_state'] = rs.get_state()
        self._timestamps.attrs['PyBroMo'] = __version__

        ts_list, part_list = [], []
        # Load emission in chunks, and save only the final timestamps
        bg_rates = [None] * (len(max_rates) - 1) + [bg_rate]
        prev_time = 0
        for i_start, i_end in iter_chunk_index(timeslice_size, t_chunksize):

            curr_time = np.around(i_start * self.t_step, decimals=0)
            if curr_time > prev_time:
                print(' %.1fs' % curr_time, end='', flush=True)
                prev_time = curr_time

            em_chunk = self.emission[:, i_start:i_end]

            times_chunk_s, par_index_chunk_s = \
                self._sim_timestamps_populations(
                    em_chunk, max_rates, populations, bg_rates, i_start,
                    rs, scale)

            # Save sorted timestamps (suffix '_s') and corresponding particles
            ts_list.append(times_chunk_s)
            part_list.append(par_index_chunk_s)

        for ts, part in zip(ts_list, part_list):
            self._timestamps.append(ts)
            self._tparticles.append(part)

        # Save current random state so it can be resumed in the next session
        self.ts_group._v_attrs['last_random_state'] = rs.get_state()
        self._timestamps.attrs['last_random_state'] = rs.get_state()
        self.ts_store.h5file.flush()

Compute one timestamps array for a mixture of N populations .


def merge_da(ts_d, ts_par_d, ts_a, ts_par_a):
    """Merge donor and acceptor timestamps and particle arrays.

    Parameters:
        ts_d (array): donor timestamp array
        ts_par_d (array): donor particles array
        ts_a (array): acceptor timestamp array
        ts_par_a (array): acceptor particles array

    Returns:
        Arrays: timestamps, acceptor bool mask, timestamp particle
    """
    ts = np.hstack([ts_d, ts_a])
    ts_par = np.hstack([ts_par_d, ts_par_a])
    a_ch = np.hstack([np.zeros(ts_d.shape[0], dtype=bool),
                      np.ones(ts_a.shape[0], dtype=bool)])
    index_sort = ts.argsort()
    return ts[index_sort], a_ch[index_sort], ts_par[index_sort]

def merge_da ( ts_d , ts_par_d , ts_a , ts_par_a ) : ts = np . hstack ( [ ts_d , ts_a ] ) ts_par = np . hstack ( [ ts_par_d , ts_par_a ] ) a_ch = np . hstack ( [ np . zeros ( ts_d . shape [ 0 ] , dtype = bool ) , np . ones ( ts_a . shape [ 0 ] , dtype = bool ) ] ) index_sort = ts . argsort ( ) return ts [ index_sort ] , a_ch [ index_sort ] , ts_par [ index_sort ]

Merge donor and acceptor timestamps and particle arrays .


def em_rates_from_E_DA_mix(em_rates_tot, E_values):
    """D and A emission rates for two populations.
    """
    em_rates_d, em_rates_a = [], []
    for em_rate_tot, E_value in zip(em_rates_tot, E_values):
        em_rate_di, em_rate_ai = em_rates_from_E_DA(em_rate_tot, E_value)
        em_rates_d.append(em_rate_di)
        em_rates_a.append(em_rate_ai)
    return em_rates_d, em_rates_a

D and A emission rates for two populations .


def populations_diff_coeff(particles, populations):
    """Diffusion coefficients of the two specified populations.
    """
    D_counts = particles.diffusion_coeff_counts
    if len(D_counts) == 1:
        pop_sizes = [pop.stop - pop.start for pop in populations]
        assert D_counts[0][1] >= sum(pop_sizes)
        D_counts = [(D_counts[0][0], ps) for ps in pop_sizes]

    D_list = []
    D_pop_start = 0  # start index of diffusion-based populations
    for pop, (D, counts) in zip(populations, D_counts):
        D_list.append(D)
        assert pop.start >= D_pop_start
        assert pop.stop <= D_pop_start + counts
        D_pop_start += counts
    return D_list

def populations_diff_coeff ( particles , populations ) : D_counts = particles . diffusion_coeff_counts if len ( D_counts ) == 1 : pop_sizes = [ pop . stop - pop . start for pop in populations ] assert D_counts [ 0 ] [ 1 ] >= sum ( pop_sizes ) D_counts = [ ( D_counts [ 0 ] [ 0 ] , ps ) for ps in pop_sizes ] D_list = [ ] D_pop_start = 0 for pop , ( D , counts ) in zip ( populations , D_counts ) : D_list . append ( D ) assert pop . start >= D_pop_start assert pop . stop <= D_pop_start + counts D_pop_start += counts return D_list

Diffusion coefficients of the two specified populations .


def populations_slices(particles, num_pop_list):
    """2-tuple of slices for selection of two populations.
    """
    slices = []
    i_prev = 0
    for num_pop in num_pop_list:
        slices.append(slice(i_prev, i_prev + num_pop))
        i_prev += num_pop
    return slices

def populations_slices ( particles , num_pop_list ) : slices = [ ] i_prev = 0 for num_pop in num_pop_list : slices . append ( slice ( i_prev , i_prev + num_pop ) ) i_prev += num_pop return slices

2 - tuple of slices for selection of two populations .


def _calc_hash_da(self, rs):
        """Compute hash of D and A timestamps for single-step D+A case.
        """
        self.hash_d = hash_(rs.get_state())[:6]
        self.hash_a = self.hash_d

def _calc_hash_da ( self , rs ) : self . hash_d = hash_ ( rs . get_state ( ) ) [ : 6 ] self . hash_a = self . hash_d

Compute hash of D and A timestamps for single - step D + A case .


def merge_da(self):
        """Merge donor and acceptor timestamps, computes `ts`, `a_ch`, `part`.
        """
        print(' - Merging D and A timestamps', flush=True)
        ts_d, ts_par_d = self.S.get_timestamps_part(self.name_timestamps_d)
        ts_a, ts_par_a = self.S.get_timestamps_part(self.name_timestamps_a)
        ts, a_ch, part = merge_da(ts_d, ts_par_d, ts_a, ts_par_a)
        assert a_ch.sum() == ts_a.shape[0]
        assert (~a_ch).sum() == ts_d.shape[0]
        assert a_ch.size == ts_a.shape[0] + ts_d.shape[0]
        self.ts, self.a_ch, self.part = ts, a_ch, part
        self.clk_p = ts_d.attrs['clk_p']

def
merge_da
(
self
)
:
print
(
' - Merging D and A timestamps'
,
flush
=
True
)
ts_d
,
ts_par_d
=
self
.
S
.
get_timestamps_part
(
self
.
name_timestamps_d
)
ts_a
,
ts_par_a
=
self
.
S
.
get_timestamps_part
(
self
.
name_timestamps_a
)
ts
,
a_ch
,
part
=
merge_da
(
ts_d
,
ts_par_d
,
ts_a
,
ts_par_a
)
assert
a_ch
.
sum
(
)
==
ts_a
.
shape
[
0
]
assert
(
~
a_ch
)
.
sum
(
)
==
ts_d
.
shape
[
0
]
assert
a_ch
.
size
==
ts_a
.
shape
[
0
]
+
ts_d
.
shape
[
0
]
self
.
ts
,
self
.
a_ch
,
self
.
part
=
ts
,
a_ch
,
part
self
.
clk_p
=
ts_d
.
attrs
[
'clk_p'
]

Merge donor and acceptor timestamps computes ts a_ch part .


def save_photon_hdf5(self, identity=None, overwrite=True, path=None):
        """Create a smFRET Photon-HDF5 file with current timestamps."""
        filepath = self.filepath
        if path is not None:
            filepath = Path(path, filepath.name)
        self.merge_da()
        data = self._make_photon_hdf5(identity=identity)
        phc.hdf5.save_photon_hdf5(data, h5_fname=str(filepath),
                                  overwrite=overwrite)

def save_photon_hdf5 ( self , identity = None , overwrite = True , path = None ) : filepath = self . filepath if path is not None : filepath = Path ( path , filepath . name ) self . merge_da ( ) data = self . _make_photon_hdf5 ( identity = identity ) phc . hdf5 . save_photon_hdf5 ( data , h5_fname = str ( filepath ) , overwrite = overwrite )

Create a smFRET Photon - HDF5 file with current timestamps .


def print_attrs(data_file, node_name='/', which='user', compress=False):
    """Print the HDF5 attributes for `node_name`.

    Parameters:
        data_file (pytables HDF5 file object): the data file to print
        node_name (string): name of the path inside the file to be printed.
            Can be either a group or a leaf-node. Default: '/', the root node.
        which (string): Valid values are 'user' for user-defined attributes,
            'sys' for pytables-specific attributes and 'all' to print both
            groups of attributes. Default 'user'.
        compress (bool): if True displays at most a line for each attribute.
            Default False.
    """
    node = data_file.get_node(node_name)
    print ('List of attributes for:\n  %s\n' % node)
    for attr in node._v_attrs._f_list():
        print ('\t%s' % attr)
        attr_content = repr(node._v_attrs[attr])
        if compress:
            attr_content = attr_content.split('\n')[0]
        print ("\t    %s" % attr_content)

def
print_attrs
(
data_file
,
node_name
=
'/'
,
which
=
'user'
,
compress
=
False
)
:
node
=
data_file
.
get_node
(
node_name
)
print
(
'List of attributes for:\n  %s\n'
%
node
)
for
attr
in
node
.
_v_attrs
.
_f_list
(
)
:
print
(
'\t%s'
%
attr
)
attr_content
=
repr
(
node
.
_v_attrs
[
attr
]
)
if
compress
:
attr_content
=
attr_content
.
split
(
'\n'
)
[
0
]
print
(
"\t    %s"
%
attr_content
)

Print the HDF5 attributes for node_name .


def print_children(data_file, group='/'):
    """Print all the sub-groups in `group` and leaf-nodes children of `group`.

    Parameters:
        data_file (pytables HDF5 file object): the data file to print
        group (string): path name of the group to be printed.
            Default: '/', the root node.
    """
    base = data_file.get_node(group)
    print ('Groups in:\n  %s\n' % base)

    for node in base._f_walk_groups():
        if node is not base:
            print ('    %s' % node)

    print ('\nLeaf-nodes in %s:' % group)
    for node in base._v_leaves.itervalues():
        info = node.shape
        if len(info) == 0:
            info = node.read()
        print ('\t%s, %s' % (node.name, info))
        if len(node.title) > 0:
            print ('\t    %s' % node.title)

def
print_children
(
data_file
,
group
=
'/'
)
:
base
=
data_file
.
get_node
(
group
)
print
(
'Groups in:\n  %s\n'
%
base
)
for
node
in
base
.
_f_walk_groups
(
)
:
if
node
is
not
base
:
print
(
'    %s'
%
node
)
print
(
'\nLeaf-nodes in %s:'
%
group
)
for
node
in
base
.
_v_leaves
.
itervalues
(
)
:
info
=
node
.
shape
if
len
(
info
)
==
0
:
info
=
node
.
read
(
)
print
(
'\t%s, %s'
%
(
node
.
name
,
info
)
)
if
len
(
node
.
title
)
>
0
:
print
(
'\t    %s'
%
node
.
title
)

Print all the sub - groups in group and leaf - nodes children of group .


def fit(self, trX, trY, batch_size=64, n_epochs=1, len_filter=LenFilter(), snapshot_freq=1, path=None):
        """Train model on given training examples and return the list of costs after each minibatch is processed.

        Args:
          trX (list) -- Inputs
          trY (list) -- Outputs
          batch_size (int, optional) -- number of examples in a minibatch (default 64)
          n_epochs (int, optional)  -- number of epochs to train for (default 1)
          len_filter (object, optional) -- object to filter training example by length (default LenFilter())
          snapshot_freq (int, optional) -- number of epochs between saving model snapshots (default 1)
          path (str, optional) -- prefix of path where model snapshots are saved.
            If None, no snapshots are saved (default None)

        Returns:
          list -- costs of model after processing each minibatch
        """
        if len_filter is not None:
            trX, trY = len_filter.filter(trX, trY)
        trY = standardize_targets(trY, cost=self.cost)

        n = 0.
        t = time()
        costs = []
        for e in range(n_epochs):
            epoch_costs = []
            for xmb, ymb in self.iterator.iterXY(trX, trY):
                c = self._train(xmb, ymb)
                epoch_costs.append(c)
                n += len(ymb)
                if self.verbose >= 2:
                    n_per_sec = n / (time() - t)
                    n_left = len(trY) - n % len(trY)
                    time_left = n_left/n_per_sec
                    sys.stdout.write("\rEpoch %d Seen %d samples Avg cost %0.4f Time left %d seconds" % (e, n, np.mean(epoch_costs[-250:]), time_left))
                    sys.stdout.flush()
            costs.extend(epoch_costs)

            status = "Epoch %d Seen %d samples Avg cost %0.4f Time elapsed %d seconds" % (e, n, np.mean(epoch_costs[-250:]), time() - t)
            if self.verbose >= 2:
                sys.stdout.write("\r"+status)
                sys.stdout.flush()
                sys.stdout.write("\n")
            elif self.verbose == 1:
                print(status)
            if path and e % snapshot_freq == 0:
                save(self, "{0}.{1}".format(path, e))
        return costs

Train model on given training examples and return the list of costs after each minibatch is processed .