コード例 #1
0
def extract_from_fit(file_handler):
    activity = Activity(file_handler)
    activity.parse()

    # Records, in an activity file that represent actual data points in your workout
    records = activity.get_records_by_type('session')

    out = []

    for record in records:
        d = {}
        # get the list of valid fields on this record
        valid_field_names = record.get_valid_field_names()

        # Examine only >>> event='session' and trigger='activity_end' <<<
        if not ('trigger' in valid_field_names) and (record.get_data('trigger') == 'activity_end'):
            continue

        for field_name in list(set(INTERESTING_FIELD_NAMES) & set(valid_field_names)):
            # Get the data and units for the field
            field_data = record.get_data(field_name)
            field_units = record.get_units(field_name)

            if not field_units:
                field_units = None

            d[field_name] = {'data': field_data, 'units': field_units}

        out.append(d)
    return out
コード例 #2
0
def convert(filename):
    document = create_document()
    element = create_sub_element(document.getroot(), "Activities")

    activity = Activity(filename)
    activity.parse()
    add_activity(element, activity)

    return document
コード例 #3
0
ファイル: fittotcx.py プロジェクト: Br3nda/FIT-to-TCX
def convert(filename):

    document = createDocument()
    element = createSubElement(document.getroot(), "Activities")

    activity = Activity(filename)
    activity.parse()
    addActivity(element, activity)

    return document
コード例 #4
0
    def parse_fit_file(file_path):
        activity = Activity(file_path)
        activity.parse()

        # Parse the session data, this contains some aggregate statistics about the workout (e.g. avg/max_speed)
        sessions_df = WorkoutFileParser._fit_get_dataframe_by_type(
            activity, 'session')

        # Parse bike_profile data, contains some information the bike and about the sensors on the bike
        bike_profiles_df = WorkoutFileParser._fit_get_dataframe_by_type(
            activity, 'bike_profile')

        # Parse activity data
        activities_df = WorkoutFileParser._fit_get_dataframe_by_type(
            activity, 'activity')

        # Parse lap data: these are aggregate statistics about segments within the workout
        laps_df = WorkoutFileParser._fit_get_dataframe_by_type(activity, 'lap')

        # Parse event data: contains the timestamp of certain events (such as stopping and starting)
        events_df = WorkoutFileParser._fit_get_dataframe_by_type(
            activity, 'event')

        # Parse user data: contains information about the athlete
        user_df = WorkoutFileParser._fit_get_dataframe_by_type(
            activity, 'user_profile')

        # Parse body metrics such as weight, percent_fat, ..
        body_metrics_df = WorkoutFileParser._fit_get_dataframe_by_type(
            activity, 'weight_scale')

        # Parse file data: which sensor/device produced this data?
        file_data_df = WorkoutFileParser._fit_get_dataframe_by_type(
            activity, 'file_id')

        # Parse the sample data: certain parameters (distance, heart rate, ...) are measured at certain freqs
        sample_df = WorkoutFileParser._fit_get_dataframe_by_type(
            activity, 'record')

        return {
            'sessions': sessions_df,
            'bike_profiles': bike_profiles_df,
            'activities': activities_df,
            'laps': laps_df,
            'events': events_df,
            'user': user_df,
            'body_metrics': body_metrics_df,
            'file_data': file_data_df,
            'samples': sample_df
        }
コード例 #5
0
ファイル: read_files.py プロジェクト: akroshko/emacs-otlb
def main_fit_id(argv):
    theids=[]
    if os.path.isdir(argv[2]):
        fit_files=[os.path.join(argv[1],f) for f in os.listdir(sys.argv[1]) if f.endswith('.fit')]
    else:
        fit_files=argv[2:]
    for filename in fit_files:
        sys.stderr.write("Getting ID for .fit file: "+filename+"\n")
        activity = Activity(filename)
        # TODO: this is the really time consuming one
        activity.parse()
        session = next(activity.get_records_by_type('session'))
        start_time=session.get_data("start_time")
        theid=("%04d" % start_time.year)+("%02d" % start_time.month)+("%02d" % start_time.day)+'T'+("%02d" % start_time.hour)+("%02d" % start_time.minute)+("%02d" % start_time.second)
        thefilenametimestamp=os.path.getmtime(filename)
        theids.append((os.path.normpath(filename),theid))
    for theid in theids:
        print theid[0] + ' ' + theid[1]
コード例 #6
0
ファイル: fit_parser.py プロジェクト: jherland/garmin_edge
class FitParser(object):
    def __init__(self, path, tz_offset=None):
        self.path = path
        self.tz_offset = tz_offset
        self.act = Activity(path)
        self.act.parse()

    def parseFitRecord(self, r, last_sample):
        d = r.as_dict()
        try:
            lat = semicircles_to_radians(d['position_lat'])
        except KeyError:
            lat = last_sample.lat
        try:
            lon = semicircles_to_radians(d['position_long'])
        except KeyError:
            lon = last_sample.lon
        ts = time.mktime(d['timestamp'].timetuple())
        if self.tz_offset is None:
            self.tz_offset = local_timezone_offset(ts)
        ts -= self.tz_offset
        return ActSample(ts, lat, lon,
            d.get('altitude', last_sample.alt),
            d.get('temperature', last_sample.temp),
            d.get('distance', last_sample.d),
            d.get('speed', last_sample.v),
            d.get('heart_rate', last_sample.hr),
            d.get('cadence', last_sample.cad),
            d.get('power', last_sample.pwr)
        )

    def samples(self, s=ActSample.empty()):
        """Yield all trackpoints as ActSample objects."""
        for r in self.act.get_records_by_type('record'):
            s = self.parseFitRecord(r, s)
            yield s

    def all(self):
        """Yield all .fit records as dicts."""
        for d in self.act.get_records_as_dicts():
            yield d
コード例 #7
0
def main_fit_id(argv):
    theids = []
    if os.path.isdir(argv[2]):
        fit_files = [
            os.path.join(argv[1], f) for f in os.listdir(sys.argv[1])
            if f.endswith('.fit')
        ]
    else:
        fit_files = argv[2:]
    for filename in fit_files:
        sys.stderr.write("Getting ID for .fit file: " + filename + "\n")
        activity = Activity(filename)
        # TODO: this is the really time consuming one
        activity.parse()
        session = next(activity.get_records_by_type('session'))
        start_time = session.get_data("start_time")
        theid = ("%04d" % start_time.year) + ("%02d" % start_time.month) + (
            "%02d" % start_time.day) + 't' + ("%02d" % start_time.hour) + (
                "%02d" % start_time.minute) + ("%02d" % start_time.second)
        thefilenametimestamp = os.path.getmtime(filename)
        theids.append((os.path.normpath(filename), theid))
    for theid in theids:
        print theid[0] + ' ' + theid[1]
コード例 #8
0
ファイル: run.py プロジェクト: cunnib/fitparse
#!/usr/bin/env python

# Sample usage of python-fitparse to parse an activity and
# print its data records.

from __future__ import print_function
from fitparse import Activity
import re
from collections import OrderedDict

activity = Activity("tests/data/sample-activity.fit")
activity.parse()

# Records of type 'record' (I know, confusing) are the entries in an
# activity file that represent actual data points in your workout.
records = activity.get_records_by_type('record')
current_record_number = 0
dic =OrderedDict.fromkeys(["timestamp","position_lat","position_long","distance","altitude","speed","heart_rate","temperature"])
counter =0
for value in dic.keys():
    if counter != 0:
	print(';',sep='',end='')
    print(value,sep='',end='')
    counter+=1
print()
for record in records:

    # Print record number
    current_record_number += 1
    #print (" Record #%d " % current_record_number).center(40, '-')
コード例 #9
0
        else: 
            return None

for fpath in filenames:
    if infos:
        print ('##### %s ' % fpath).ljust(60, '#')
        

    print_hook_func = None
    if infos:
        print_hook_func = print_record
    else:
        print_hook_func = return_gpx_trk_entry

    record_number = 0
    a = Activity(fpath)
    fxpath = "%s.gpx" %(fpath)
    name=basename(fpath)
    fdate=datetime.strptime(name, "%Y-%m-%d-%H-%M-%S.fit")
    start_date_activity = ""
    if fdate is not None:
       start_date_activity = datetime.strftime(fdate,"%Y-%m-%sT%H:%M:%SZ")
    #begin the GPX File !!!
    if not os.path.exists(fxpath):
        open(fxpath, 'w').close()
    with open(fxpath, 'rt+') as f:
        print ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
        print ("<gpx creator=\"jpmena\"")
        print (" xmlns:gpxtrx=\"http://www.garmin.com/xmlschemas/GpxExtensions/v3\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"")
        print (" xmlns:gpxtpx=\"http://www.garmin.com/xmlschemas/TrackPointExtension/v1\"")
        print (" xmlns:gpxx=\"http://www.garmin.com/xmlschemas/WaypointExtension/v1\" xmlns:nmea=\"http://trekbuddy.net/2009/01/gpx/nmea\">")
コード例 #10
0
ファイル: fitparse.py プロジェクト: JanDeVisser/Grumble
def convert(filename):
    activity = Activity(filename)
    activity.parse()
    wrapper = ActivityWrapper(activity)
    wrapper.process()
    return None
コード例 #11
0
            startTime = field.data
        if "total_distance" in field.name:
	    global totalDistance
            totalDistance = field.data
	    totalDistance *= 0.0006213711922373
	if "avg_speed" in field.name:
	    global avgSpeed
            avgSpeed = field.data
	    avgSpeed *= 2.236936292054
	if "total_elapsed_time" in field.name:
	    global totalElapsedTime
            totalElapsedTime = field.data
            totalElapsedTime /= 3600

for f in filenames:
    if quiet:
        print f
    else:
        print ('File =  %s ' % f)

    print_hook_func = None
    if not quiet:
        print_hook_func = print_record

    record_number = 0
    a = Activity(f)
    a.parse(hook_func=print_hook_func)
    if startTime is not None:
        f1.write("%s,%s,%s,%s\n" % (startTime,totalDistance,avgSpeed,totalElapsedTime))
	print ("%s,%s,%s,%s\n" % (startTime,totalDistance,avgSpeed,totalElapsedTime))
コード例 #12
0
def pwhr_transfer_function(FitFilePath, ConfigFile=None, OutStream=sys.stdout):

    # this needs to stay INSIDE the function or bad things happen
    import matplotlib.pyplot as plt

    (FilePath, FitFileName) = os.path.split(FitFilePath)

    if ConfigFile is None:
        ConfigFile = FindConfigFile('', FilePath)
    if (ConfigFile is None) or (not os.path.exists(ConfigFile)):
        raise IOError('Configuration file not specified or found')

    #
    #   Parse the configuration file
    #
    if type(ConfigFile) != type(ConfigParser()):
        if (ConfigFile is None) or (not os.path.exists(ConfigFile)):
            raise IOError('Configuration file not specified or found')
        config = ConfigParser()
        config.read(ConfigFile)
        print >> OutStream, 'reading config file ' + ConfigFile
    else:
        config = ConfigFile
    WeightEntry = config.getfloat('user', 'weight')
    WeightToKg = config.getfloat('user', 'WeightToKg')
    weight = WeightEntry * WeightToKg
    age = config.getfloat('user', 'age')
    EndurancePower = config.getfloat('power', 'EndurancePower')
    ThresholdPower = config.getfloat('power', 'ThresholdPower')
    EnduranceHR = config.getfloat('power', 'EnduranceHR')
    ThresholdHR = config.getfloat('power', 'ThresholdHR')
    HRTimeConstant = config.getfloat('power', 'HRTimeConstant')
    HRDriftRate = config.getfloat('power', 'HRDriftRate')
    print >> OutStream, 'WeightEntry    : ', WeightEntry
    print >> OutStream, 'WeightToKg     : ', WeightToKg
    print >> OutStream, 'weight         : ', weight
    print >> OutStream, 'age            : ', age
    print >> OutStream, 'EndurancePower : ', EndurancePower
    print >> OutStream, 'ThresholdPower : ', ThresholdPower
    print >> OutStream, 'EnduranceHR    : ', EnduranceHR
    print >> OutStream, 'ThresholdHR    : ', ThresholdHR
    print >> OutStream, 'HRTimeConstant : ', HRTimeConstant
    print >> OutStream, 'HRDriftRate    : ', HRDriftRate

    # power zones from "Cyclist's Training Bible", 5th ed., by Joe Friel, p51
    FTP = ThresholdPower
    pZones = {
        1: [0, 0.55 * FTP],
        2: [0.55 * FTP, 0.75 * FTP],
        3: [0.75 * FTP, 0.90 * FTP],
        4: [0.90 * FTP, 1.05 * FTP],
        5: [1.05 * FTP, 1.20 * FTP],
        6: [1.20 * FTP, 1.50 * FTP],
        7: [1.50 * FTP, 2.50 * FTP]
    }

    # heart-rate zones from "Cyclist's Training Bible" 5th ed. by Joe Friel, p50
    FTHR = ThresholdHR
    hZones = {
        1: [0, 0.82 * FTHR],  # 1
        2: [0.82 * FTHR, 0.89 * FTHR],  # 2
        3: [0.89 * FTHR, 0.94 * FTHR],  # 3
        4: [0.94 * FTHR, 1.00 * FTHR],  # 4
        5: [1.00 * FTHR, 1.03 * FTHR],  # 5a
        6: [1.03 * FTHR, 1.06 * FTHR],  # 5b
        7: [1.07 * FTHR, 1.15 * FTHR]
    }  # 5c

    # get zone bounds for plotting
    p_zone_bounds = [
        pZones[1][0], pZones[2][0], pZones[3][0], pZones[4][0], pZones[5][0],
        pZones[6][0], pZones[7][0], pZones[7][1]
    ]

    h_zone_bounds = [
        0.4 * FTHR,  # better plotting
        hZones[2][0],
        hZones[3][0],
        hZones[4][0],
        hZones[5][0],
        hZones[6][0],
        hZones[7][0],
        hZones[7][1]
    ]

    from fitparse import Activity
    from activity_tools import extract_activity_signals

    required_signals = ['power', 'heart_rate']

    # get the signals
    activity = Activity(FitFilePath)
    signals = extract_activity_signals(activity, resample='existing')

    if not all(s in signals.keys() for s in required_signals):
        msg = 'required signals not in file'
        print >> OutStream, msg
        print >> OutStream, 'Signals required:'
        for s in required_signals:
            print >> OutStream, '   ' + s
        print >> OutStream, 'Signals contained:'
        for s in signals.keys():
            print >> OutStream, '   ' + s
        raise IOError(msg)

    # resample to constant-increment (1 Hz) with zeros at missing samples
    time_idx = signals['time'].astype('int')
    power_vi = signals['power']
    heart_rate_vi = signals['heart_rate']
    nScans = time_idx[-1] + 1
    time_ci = np.arange(nScans)
    power = np.zeros(nScans)
    power[time_idx] = power_vi
    heart_rate_ci = np.zeros(nScans)
    heart_rate_ci[time_idx] = heart_rate_vi

    # compute the 30-second, moving-average power signal.
    p30 = BackwardMovingAverage(power)
    '''
    # compute moving time
        moving_time would contain data in the gaps--repeats of the "stopped"
        time until it resumes:
            >>> time_idx
            array([ 0,  1,  2,          5,  6,  7,         10, 11, 12])
            >>> time_ci
            array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12])
            >>> moving_time
            array([ 0,  1,  2,  2,  2,  3,  4,  5,  5,  5,  6,  7,  8])
    '''
    moving_time = np.zeros(nScans).astype('int')
    idx = 0
    for i in time_ci[0:-1]:
        moving_time[i] = idx
        if time_idx[idx + 1] == i + 1:
            idx += 1
    moving_time[nScans - 1] = idx

    # Calculate running normalized power and TSS.
    norm_power = np.zeros(nScans)
    TSS = np.zeros(nScans)
    for i in range(1, nScans):
        ii = np.nonzero(time_idx <= i)[0]
        norm_power[i] = np.average(p30[time_idx[ii]]**4)**(0.25)
        TSS[i] = moving_time[i] / 36 * (norm_power[i] / FTP)**2

    #
    # simulate the heart rate
    #
    SampleRate = 1.0
    tau = HRTimeConstant  # 63.0 seconds
    PwHRTable = np.array([
        [0, 0.50 * FTHR],  # Active resting HR
        [0.55 * FTP, 0.70 * FTHR],  # Recovery
        [0.70 * FTP, 0.82 * FTHR],  # Aerobic threshold
        [1.00 * FTP, FTHR],  # Functional threshold
        [1.20 * FTP, 1.03 * FTHR],  # Aerobic capacity
        [1.50 * FTP, 1.06 * FTHR]
    ])  # Max HR

    def heartrate_dot(HR, t):
        i = min(int(t * SampleRate), nScans - 1)
        HRp = np.interp(power[i], PwHRTable[:, 0], PwHRTable[:, 1])
        HRt = HRp + HRDriftRate * TSS[i]
        return (HRt - HR) / tau

    heart_rate_sim = odeint(heartrate_dot, heart_rate_ci[0], time_ci)
    err     = np.squeeze( heart_rate_sim ) \
            - np.squeeze( heart_rate_ci  )
    RMSError = np.sqrt(np.average(err[time_idx]**2))
    print >> OutStream, 'Average  measured HR  : %7i BPM' \
                        % np.average(heart_rate_ci[time_idx])
    print >> OutStream, 'Average simulated HR  : %7i BPM' \
                        % np.average(heart_rate_sim[time_idx])
    print >> OutStream, 'RMS error             : %7i BPM' % RMSError

    #
    # Estimate better values for FTHR and HRDriftRate
    #
    coef = np.polyfit(TSS[time_idx],
                      -err[time_idx],
                      deg=1,
                      w=heart_rate_ci[time_idx] - 0.50 * FTHR)
    slope = coef[0]
    offset = coef[1]
    NewThresholdHR = offset + ThresholdHR
    NewHRDriftRate = slope + HRDriftRate
    print >> OutStream, 'Estimated ThresholdHR : %7.1f BPM' \
                        % NewThresholdHR
    print >> OutStream, 'Estimated HRDriftRate : %7.4f BPM/TSS' \
                        % NewHRDriftRate

    # -------------- debug ---------------
    print 'coef = ', coef
    print 'TSS = ', TSS[-1]
    hh = moving_time[-1] // 3600
    mm = (moving_time[-1] % 3600) // 60
    ss = (moving_time[-1] % 3600) % 60
    print 'moving time: %4i:%02i:%02i' % (hh, mm, ss)
    print 'normalized power:', norm_power[-1], norm_power.max()
    CrossPlotFig = plt.figure()
    sc = plt.scatter(TSS[time_idx], -err[time_idx], s=5)
    plt.title('Simulation Error Vs TSS')
    plt.xlabel('TSS')
    plt.ylabel('BPM')
    plt.grid(b=True, which='major', axis='both')
    a = plt.axis()
    #plt.axis([ 0, a[1], 0, a[3] ])
    y_fit = slope * TSS[time_idx] + offset
    plt.plot(TSS[time_idx], y_fit, 'k-')
    plt.show()

    #
    #   extract lap results
    #
    from activity_tools import extract_activity_laps
    activity = Activity(FitFilePath)
    laps = extract_activity_laps(activity)
    lap_start_time = laps['start_time']  # datetime object
    lap_timestamp = laps['timestamp']
    nLaps = len(lap_start_time)
    t0 = signals['metadata']['timestamp']
    lap_start_sec = np.zeros(nLaps)  # lap start times in seconds
    for i in range(nLaps):
        tBeg = (lap_start_time[i] - t0).total_seconds()
        tEnd = (lap_timestamp[i] - t0).total_seconds()
        lap_start_sec[i] = tBeg

    #
    # time plot
    #

    import matplotlib.dates as md
    from matplotlib.dates import date2num, DateFormatter
    import datetime as dt
    base = dt.datetime(2014, 1, 1, 0, 0, 0)
    x = [base + dt.timedelta(seconds=t) for t in time_ci.astype('float')]
    x = date2num(x)  # Convert to matplotlib format
    x_laps  = [ base + dt.timedelta(seconds=t)   \
                for t in lap_start_sec.astype('float') ]
    x_laps = date2num(x_laps)
    fig1, (ax0, ax1) = plt.subplots(nrows=2, sharex=True)
    ax0.plot_date(x, heart_rate_ci, 'r-', linewidth=1)
    ax0.plot_date(x, heart_rate_sim, 'm-', linewidth=3)
    ax0.set_yticks(h_zone_bounds, minor=False)
    ax0.grid(True)
    ax0.legend(['measured', 'simulated'], loc='upper left')
    ax0.set_title('heart rate, BPM')
    ax1.plot_date(x, power, 'k-', linewidth=1)
    ax1.plot_date(x, p30, 'b-', linewidth=3)
    ax1.xaxis.set_major_formatter(DateFormatter('%H:%M:%S'))
    ax1.set_yticks(p_zone_bounds, minor=False)
    ax1.grid(True)
    ax1.set_title('power, watts')
    for i in range(nLaps):
        ax0.axvline(x_laps[i], label=str(i + 1))
        ax1.axvline(x_laps[i], label=str(i + 1))
    fig1.autofmt_xdate()
    ax1.legend(['power', 'p30'], loc='upper left')
    fig1.suptitle('Pw:HR Transfer Function', fontsize=20)
    fig1.tight_layout()
    fig1.canvas.set_window_title(FitFilePath)
    fig1.subplots_adjust(hspace=0)  # Remove horizontal space between axes
    plt.show()

    def ClosePlots():
        plt.close('all')

    return ClosePlots
コード例 #13
0
ファイル: dragdrop.py プロジェクト: j33433/FitFiles
print 'WeightToKg    : ', WeightToKg
print 'weight        : ', weight
print 'age           : ', age
print 'EndurancePower: ', EndurancePower
print 'ThresholdPower: ', ThresholdPower
print 'EnduranceHR   : ', EnduranceHR
print 'ThresholdHR   : ', ThresholdHR

from datetime import datetime
from fitparse import Activity
from activity_tools import extract_activity_signals

# typ 'S:\\will\\documents\\bike\\fitfiles\\2017-01-08-15-41-48_zwift.fit'
fitfilepath = sys.argv[1]

activity = Activity(fitfilepath)
signals = extract_activity_signals(activity)

########################################################################
###         Compute Calories                                         ###
########################################################################

from numpy import array, arange, append, zeros, cumsum, average
#from pylab import *

#   Formula widely available. One site:
#       https://www.easycalculation.com/formulas/heart-rate-calorie-burn.html

#weight  = 188.0*0.45359237  # lb->kg
#age     = 50.0
コード例 #14
0
ファイル: pwhr_tf_params.py プロジェクト: j33433/FitFiles
names1  = [ 'FIT File', 'FTHR (BPM)',  'tau (sec)', 'sHRDriftRate' ]
fmt     = '%20s:' + '%15s'*3
print >> OutStream, fmt % tuple(names1)
OutStream.flush()

for FitFile in fit_files:

    TimerStart = time.time()     # measure execution time

    print >> OutStream, '#################################################'
    print >> OutStream, '###%s###' % FitFile.center(43)
    print >> OutStream, '#################################################'

    # get the signals
    FitFilePath = FilePath + FitFile
    activity    = Activity(FitFilePath)
    signals     = extract_activity_signals(activity, resample='existing')

    if not all( s in signals.keys() for s in required_signals ):
        msg = 'required signals not in file'
        print >> OutStream, msg
        print >> OutStream, 'Signals required:'
        for s in required_signals:
            print >> OutStream, '   ' + s
        print >> OutStream, 'Signals contained:'
        for s in signals.keys():
            print >> OutStream, '   ' + s
        raise IOError(msg)

    # resample to constant-increment (1 Hz) with zeros at missing samples
    time_idx                = signals['time'].astype('int')
コード例 #15
0
        localString += ("\n<|\"record number\"-> %d, \"type\"-> \"%s\", \"fields\"->{" % (record_number, rec.type.name))
        shortFields = rec.fields[0:99] # for debugging, limits the number of fields per record
        for field in shortFields:
            localString += "<|\"name\"->\"%s\", \"type\"->\"%s\", \"data\"->\"%s\", \"unit\"->\"%s\"|>," % (field.name, field.type.name, field.data, field.units)
        localString = localString[:-1]
        localString += "}|>, "
    else:
    	localString+=""

# the program as such starts below
for f in filenames:
    #displays a header so you know it has started
    if quiet:
        print f
    else:
        print ('##### %s ' % f).ljust(60, '#')

    record_number = 0
    localString = ""
    a = Activity(f)
    localString= "{"
    a.parse(print_record) # calls the interesting function
    localString = localString[:-2]
    localString += "\n}"
    newpath, tail = os.path.split(f)	# parts of the path and filename for the file we're parsing
    tail = tail[:-3]
    tail+="txt"
    outFile = os.path.join(newpath, tail)
    file = open(outFile,"w")
    file.write(localString)
    file.close()
コード例 #16
0
#!/usr/bin/env python

# Sample usage of python-fitparse to parse an activity and
# print its data records.

from __future__ import print_function
from fitparse import Activity
import re
from collections import OrderedDict

activity = Activity("tests/data/sample-activity.fit")
activity.parse()

# Records of type 'record' (I know, confusing) are the entries in an
# activity file that represent actual data points in your workout.
records = activity.get_records_by_type('record')
current_record_number = 0
dic = OrderedDict.fromkeys([
    "timestamp", "position_lat", "position_long", "distance", "altitude",
    "speed", "heart_rate", "temperature"
])
counter = 0
for value in dic.keys():
    if counter != 0:
        print(';', sep='', end='')
    print(value, sep='', end='')
    counter += 1
print()
for record in records:

    # Print record number
コード例 #17
0
def main_fit(argv):
    filename = sys.argv[1]
    activity = Activity(filename)
    activity.parse()
    session = next(activity.get_records_by_type('session'))
    session_dict = {}
    start_time = local_date_to_utc(session.get_data("start_time"))
    real_start_time = session.get_data("start_time")
    session_dict['elapsed-time'] = session.get_data("total_elapsed_time")
    timer_time = session.get_data("total_timer_time")
    session_dict['timer-time'] = timer_time
    distance = session.get_data("total_distance")
    session_dict['distance'] = distance
    # TODO: possibility do this for very small distances
    if distance == 0.0:
        # TODO: change to None?
        session_dict['pace'] = 0.0
    else:
        session_dict['pace'] = (1000. / (distance / timer_time)) / 60.
    theid = time_to_id(start_time)
    session_dict['id'] = theid
    session_dict['start-time'] = theid
    end_time = local_date_to_utc(session.get_data("timestamp"))
    end_time_id = time_to_id(end_time)
    session_dict['end-time'] = end_time_id
    session_dict['laps'] = {}
    session_dict['laps']['start-time'] = []
    session_dict['laps']['start-timestamp'] = []
    session_dict['laps']['end-time'] = []
    session_dict['laps']['end-timestamp'] = []
    session_dict['laps']['timer-time'] = []
    session_dict['laps']['distance'] = []
    session_dict['laps']['pace'] = []
    session_dict['laps']['maximum-speed'] = []
    session_dict['laps']['average-heart-rate'] = []
    session_dict['laps']['maximum-heart-rate'] = []
    session_dict['laps']['trigger-method'] = []
    session_dict['laps']['trackpoints'] = []
    session_dict['lap start times'] = []
    session_dict['lap end times'] = []
    session_dict['lap start distances'] = []
    session_dict['lap end distances'] = []
    for lap in activity.get_records_by_type('lap'):
        session_dict['laps']['start-time'].append(
            str(local_date_to_utc(lap.get_data("start_time"))))
        start_time = lap.get_data("start_time")
        session_dict['laps']['end-time'].append(
            str(local_date_to_utc(lap.get_data("timestamp"))))
        end_time = lap.get_data("timestamp")
        timer_time = lap.get_data("total_timer_time")
        session_dict['laps']['timer-time'].append(timer_time)
        distance = lap.get_data("total_distance")
        session_dict['laps']['distance'].append(distance)
        if distance == 0.0 or distance == None or timer_time == None:
            # TODO: change to None?
            session_dict['laps']['pace'].append(0.)
        else:
            session_dict['laps']['pace'].append(
                (1000. / (distance / timer_time)) / 60.)
        session_dict['laps']['maximum-speed'].append(
            lap.get_data("max_speed"))  # opt
        session_dict['laps']['average-heart-rate'].append(
            lap.get_data("avg_heart_rate"))  #opt
        session_dict['laps']['maximum-heart-rate'].append(
            lap.get_data("max_heart_rate"))  #opt
        session_dict['laps']['trigger-method'].append(
            LAP_TRIGGER_MAP.get(lap.get_data("lap_trigger"), "Manual"))
        first_track_point = True
        for trackpoint in activity.get_records_by_type('record'):
            tts = trackpoint.get_data("timestamp")
            if tts >= start_time and tts <= end_time:
                if first_track_point == True:
                    session_dict['lap start times'].append(
                        (trackpoint.get_data("timestamp") -
                         real_start_time).seconds)
                    session_dict['lap start distances'].append(
                        trackpoint.get_data("distance"))
                    first_track_point = False
                session_dict['laps']['trackpoints'].append({})
                session_dict['laps']['trackpoints'][-1]['timestamp'] = str(
                    local_date_to_utc(trackpoint.get_data("timestamp")))
                session_dict['laps']['trackpoints'][-1]['elapsed-time'] = (
                    trackpoint.get_data("timestamp") - real_start_time).seconds
                session_dict['laps']['trackpoints'][-1][
                    'latitude-position'] = trackpoint.get_data("position_lat")
                session_dict['laps']['trackpoints'][-1][
                    'longitude-position'] = trackpoint.get_data(
                        "position_long")
                session_dict['laps']['trackpoints'][-1][
                    'distance'] = trackpoint.get_data("distance")
                session_dict['laps']['trackpoints'][-1][
                    'altitude'] = trackpoint.get_data("altitude")
                session_dict['laps']['trackpoints'][-1][
                    'speed'] = trackpoint.get_data("speed")
                session_dict['laps']['trackpoints'][-1][
                    'heart rate'] = trackpoint.get_data("heart_rate")
                # TODO: make sure this works out OK, get last valid position
                if session_dict['laps']['trackpoints'][-1][
                        'latitude-position'] == None:
                    session_dict['laps']['trackpoints'][-1]['latitude'] = None
                else:
                    session_dict['laps']['trackpoints'][-1]['latitude'] = str(
                        semicircle_to_degrees(
                            session_dict['laps']['trackpoints'][-1]
                            ['latitude-position']))
                if session_dict['laps']['trackpoints'][-1][
                        'longitude-position'] == None:
                    session_dict['laps']['trackpoints'][-1]['longitude'] = None
                else:
                    session_dict['laps']['trackpoints'][-1]['longitude'] = str(
                        semicircle_to_degrees(
                            session_dict['laps']['trackpoints'][-1]
                            ['longitude-position']))
        # add first and last
        session_dict['lap end times'].append(
            session_dict['laps']['trackpoints'][-1]['elapsed-time'])
        session_dict['lap end times'].append(
            session_dict['laps']['trackpoints'][-1]['distance'])
    # update things from laps
    session_dict['start-latitude'] = session_dict['laps']['trackpoints'][0][
        'latitude']
    session_dict['start-longitude'] = session_dict['laps']['trackpoints'][0][
        'longitude']
    session_dict['end-latitude'] = session_dict['laps']['trackpoints'][-1][
        'latitude']
    session_dict['end-longitude'] = session_dict['laps']['trackpoints'][-1][
        'longitude']
    return session_dict
コード例 #18
0
ファイル: interval_laps.py プロジェクト: j33433/FitFiles
def interval_laps(FitFilePath, ConfigFile=None, OutStream=sys.stdout):

    (FilePath, FitFileName) = os.path.split(FitFilePath)

    if ConfigFile is None:
        ConfigFile = FindConfigFile('', FilePath)
    if (ConfigFile is None) or (not os.path.exists(ConfigFile)):
        raise IOError('Configuration file not specified or found')

    #
    #   Parse the configuration file
    #
    from ConfigParser import ConfigParser
    config = ConfigParser()
    config.read(ConfigFile)
    print >> OutStream, 'reading config file ' + ConfigFile
    ThresholdPower = config.getfloat('power', 'ThresholdPower')
    ThresholdHR = config.getfloat('power', 'ThresholdHR')
    print >> OutStream, 'ThresholdPower: ', ThresholdPower
    print >> OutStream, 'ThresholdHR   : ', ThresholdHR
    FTP = ThresholdPower
    '''
    # Get Records of type 'lap'
    # types: [ 'record', 'lap', 'event', 'session', 'activity', ... ]
    records = activity.get_records_by_type('lap')
    current_record_number = 0

    elapsed_time    = []
    avg_heart_rate  = []
    avg_power       = []
    avg_cadence     = []
    max_heart_rate  = []
    balance         = []

    FirstIter   = True

    for record in records:

        # Print record number
        current_record_number += 1
        #print >> OutStream, (" Record #%d " % current_record_number).center(40, '-')

        # Get the list of valid fields on this record
        valid_field_names = record.get_valid_field_names()

        for field_name in valid_field_names:
            # Get the data and units for the field
            field_data = record.get_data(field_name)
            field_units = record.get_units(field_name)

            ## Print what we've got!
            #if field_units:
            #    print >> OutStream, " * %s: %s %s" % (field_name, field_data, field_units)
            #else:
            #    print >> OutStream, " * %s: %s" % (field_name, field_data)

            if 'timestamp' in field_name:
                if FirstIter:
                    t0  = field_data    # datetime
                    t   = t0
                    FirstIter   = False
                else:
                    t   = field_data    # datetime

            if 'total_timer_time' in field_name:
                elapsed_time.append( field_data )

            if 'avg_power' in field_name:
                avg_power.append( field_data )

            # avg_heart_rate is in a lap record
            if 'avg_heart_rate' in field_name:
                avg_heart_rate.append(field_data)

            if 'max_heart_rate' in field_name:
                max_heart_rate.append(field_data)

            if 'avg_cadence' in field_name:
                avg_cadence.append(field_data)

            if 'left_right_balance' in field_name:
                balance.append(field_data)

    from numpy import nonzero, array, arange, zeros, average, logical_and

    power   = array(avg_power)
    time    = array(elapsed_time)
    cadence = array(avg_cadence)
    avg_hr  = array(avg_heart_rate)
    max_hr  = array(max_heart_rate)
    if len(balance) == 0:
        balance = zeros(len(power))
    else:
        balance = array(balance)
    '''

    #
    #   extract lap results
    #
    from fitparse import Activity
    from activity_tools import extract_activity_laps
    import numpy as np
    activity = Activity(FitFilePath)
    laps = extract_activity_laps(activity)
    power = laps['power']
    time = laps['total_timer_time']
    cadence = laps['cadence']
    avg_hr = laps['avg_hr']
    max_hr = laps['max_hr']
    balance = laps['balance']

    #   All high-intensity intervals:    >80 %FTP
    ii = np.nonzero(power >= 0.80 * FTP)[0]  # index array
    if len(ii) > 0:
        print >> OutStream, 'processing %d all laps above %d watts...' \
                            % (len(ii), 0.80*FTP)
        names1 = ['', '', 'avg', 'avg', 'avg', 'max', 'avg']
        names2 = ['lap', 'time', 'power', 'cad', 'HR', 'HR', 'bal']
        print >> OutStream, "%8s" * 7 % tuple(names1)
        print >> OutStream, "%8s" * 7 % tuple(names2)
        for i in range(len(ii)):
            mm = time[ii[i]] // 60
            ss = time[ii[i]] % 60
            print >> OutStream, '%8d%5i:%02i%8d%8d%8d%8d%8.1f' \
                    % (ii[i], mm, ss, power[ii[i]],
                        cadence[ii[i]],
                        avg_hr[ii[i]],
                        max_hr[ii[i]],
                        balance[ii[i]] )
        mm = sum(time[ii]) // 60
        ss = sum(time[ii]) % 60
        print >> OutStream, '%8s%5i:%02i%8d%8d%8d%8d%8.1f' \
                % ("AVERAGE", mm, ss,
                    sum(  power[ii]*time[ii]) / sum(time[ii]),
                    sum(cadence[ii]*time[ii]) / sum(time[ii]),
                    sum( avg_hr[ii]*time[ii]) / sum(time[ii]),
                    max(max_hr[ii]),
                    sum(balance[ii]*time[ii]) / sum(time[ii]) )
    else:
        print >> OutStream, 'No high-intensity laps found.' \

    #   Tempo intervals:    75-88  %FTP
    ii = np.nonzero(np.logical_and(power >= 0.75 * FTP,
                                   power < 0.88 * FTP))[0]  # index array
    if len(ii) > 0:
        print >> OutStream, 'processing %d tempo laps between %d and %d watts...' \
                            % (len(ii), 0.75*FTP, 0.88*FTP)
        names1 = ['', '', 'avg', 'avg', 'avg', 'max', 'avg']
        names2 = ['lap', 'time', 'power', 'cad', 'HR', 'HR', 'bal']
        print >> OutStream, "%8s" * 7 % tuple(names1)
        print >> OutStream, "%8s" * 7 % tuple(names2)
        for i in range(len(ii)):
            mm = time[ii[i]] // 60
            ss = time[ii[i]] % 60
            print >> OutStream, '%8d%5i:%02i%8d%8d%8d%8d%8.1f' \
                    % (ii[i], mm, ss, power[ii[i]],
                        cadence[ii[i]],
                        avg_hr[ii[i]],
                        max_hr[ii[i]],
                        balance[ii[i]] )
        mm = sum(time[ii]) // 60
        ss = sum(time[ii]) % 60
        print >> OutStream, '%8s%5i:%02i%8d%8d%8d%8d%8.1f' \
                % ("AVERAGE", mm, ss,
                    sum(  power[ii]*time[ii]) / sum(time[ii]),
                    sum(cadence[ii]*time[ii]) / sum(time[ii]),
                    sum( avg_hr[ii]*time[ii]) / sum(time[ii]),
                    max(max_hr[ii]),
                    sum(balance[ii]*time[ii]) / sum(time[ii]) )
    else:
        print >> OutStream, 'No tempo laps found.' \

    #   Cruise intervals:   88-105 %FTP.
    ii = np.nonzero(np.logical_and(power >= 0.88 * FTP,
                                   power < 1.05 * FTP))[0]  # index array
    if len(ii) > 0:
        print >> OutStream, 'processing %d threshold laps between %d and %d watts...' \
                            % (len(ii), 0.88*FTP, 1.05*FTP)
        names1 = ['', '', 'avg', 'avg', 'avg', 'max', 'avg']
        names2 = ['lap', 'time', 'power', 'cad', 'HR', 'HR', 'bal']
        print >> OutStream, "%8s" * 7 % tuple(names1)
        print >> OutStream, "%8s" * 7 % tuple(names2)
        for i in range(len(ii)):
            mm = time[ii[i]] // 60
            ss = time[ii[i]] % 60
            print >> OutStream, '%8d%5i:%02i%8d%8d%8d%8d%8.1f' \
                    % (ii[i], mm, ss, power[ii[i]],
                        cadence[ii[i]],
                        avg_hr[ii[i]],
                        max_hr[ii[i]],
                        balance[ii[i]] )
        mm = sum(time[ii]) // 60
        ss = sum(time[ii]) % 60
        print >> OutStream, '%8s%5i:%02i%8d%8d%8d%8d%8.1f' \
                % ("AVERAGE", mm, ss,
                    sum(  power[ii]*time[ii]) / sum(time[ii]),
                    sum(cadence[ii]*time[ii]) / sum(time[ii]),
                    sum( avg_hr[ii]*time[ii]) / sum(time[ii]),
                    max(max_hr[ii]),
                    sum(balance[ii]*time[ii]) / sum(time[ii]) )
    else:
        print >> OutStream, 'No threshold laps found.' \

    #   VO2max intervals:  105-200 %FTP.
    ii = np.nonzero(np.logical_and(power >= 1.05 * FTP,
                                   power < 2.00 * FTP))[0]  # index array
    if len(ii) > 0:
        print >> OutStream, 'processing %d VO2max laps between %d and %d watts...' \
                            % (len(ii), 1.05*FTP, 2.00*FTP)
        names1 = ['', '', 'avg', 'avg', 'avg', 'max', 'avg']
        names2 = ['lap', 'time', 'power', 'cad', 'HR', 'HR', 'bal']
        print >> OutStream, "%8s" * 7 % tuple(names1)
        print >> OutStream, "%8s" * 7 % tuple(names2)
        for i in range(len(ii)):
            mm = time[ii[i]] // 60
            ss = time[ii[i]] % 60
            print >> OutStream, '%8d%5i:%02i%8d%8d%8d%8d%8.1f' \
                    % (ii[i], mm, ss, power[ii[i]],
                        cadence[ii[i]],
                        avg_hr[ii[i]],
                        max_hr[ii[i]],
                        balance[ii[i]] )
        mm = sum(time[ii]) // 60
        ss = sum(time[ii]) % 60
        print >> OutStream, '%8s%5i:%02i%8d%8d%8d%8d%8.1f' \
                % ("AVERAGE", mm, ss,
                    sum(  power[ii]*time[ii]) / sum(time[ii]),
                    sum(cadence[ii]*time[ii]) / sum(time[ii]),
                    sum( avg_hr[ii]*time[ii]) / sum(time[ii]),
                    max(max_hr[ii]),
                    sum(balance[ii]*time[ii]) / sum(time[ii]) )
    else:
        print >> OutStream, 'No VO2max laps found.' \
コード例 #19
0
# plot_balance.py
'''
cross-plot balance against power
'''

from pylab import plt, arange, interp, array, zeros, sqrt, average

# get signals
from datetime import datetime
from fitparse import Activity
from activity_tools import extract_activity_signals
FileLoc = r'S:\\will\\documents\\OneDrive\\bike\\activities\\will\\'
#fitfilepath = FileLoc + r'2018-06-01-11-13-49.fit'  # long ride
#fitfilepath = FileLoc + r'2016-07-09-13-52-59.fit'  # error
fitfilepath = FileLoc + r'2017-03-09-12-25-12.fit'  # FTP
activity = Activity(fitfilepath)
signals = extract_activity_signals(activity, resample='existing')
power = signals['power']
balance = signals['left_right_balance']
time = signals['time']
PAvgBalance = sum(power * balance) / sum(power)

# get session info
records = activity.get_records_by_type('session')
for record in records:
    valid_field_names = record.get_valid_field_names()

# plotting
CrossPlotFig = plt.figure()
sc = plt.scatter(power,
                 balance,
コード例 #20
0
ファイル: ui.py プロジェクト: pashi/docker-garmin-fit-ui
def get_records(f):
    filename = '%s/%s' % (fit_files_path, f)
    activity = Activity(filename)
    activity.parse()
    records = activity.get_records_by_type('record')
    return records
コード例 #21
0
ファイル: fitparse.py プロジェクト: JanDeVisser/sweattrails
def convert(filename):
    activity = Activity(filename)
    activity.parse()
    wrapper = ActivityWrapper(activity)
    wrapper.process()
    return None
コード例 #22
0
ファイル: zone_detect.py プロジェクト: j33433/FitFiles
def zone_detect(FitFilePath, ConfigFile=None, OutStream=sys.stdout):

    (FilePath, FitFileName) = os.path.split(FitFilePath)

    if ConfigFile is None:
        ConfigFile = FindConfigFile('', FilePath)
    if (ConfigFile is None) or (not os.path.exists(ConfigFile)):
        raise IOError('Configuration file not specified or found')

    #
    #   Parse the configuration file
    #
    from ConfigParser import ConfigParser
    config = ConfigParser()
    config.read(ConfigFile)
    print >> OutStream, 'reading config file ' + ConfigFile
    ThresholdPower = config.getfloat('power', 'ThresholdPower')
    ThresholdHR = config.getfloat('power', 'ThresholdHR')
    print >> OutStream, 'ThresholdPower: ', ThresholdPower
    print >> OutStream, 'ThresholdHR   : ', ThresholdHR

    from datetime import datetime
    from fitparse import Activity
    from activity_tools import extract_activity_signals

    required_signals = ['power']  # 'heart_rate' optional

    # get the signals
    activity = Activity(FitFilePath)
    signals = extract_activity_signals(activity)

    if not all(s in signals.keys() for s in required_signals):
        msg = 'required signals not in file'
        print >> OutStream, msg
        print >> OutStream, 'Signals required:'
        for s in required_signals:
            print >> OutStream, '   ' + s
        print >> OutStream, 'Signals contained:'
        for s in signals.keys():
            print >> OutStream, '   ' + s
        raise IOError(msg)

    hasHR = True if 'heart_rate' in signals.keys() else False

    # up-sample by 5x so that zone-skipping is not needed
    SampleRate = 5.0
    from numpy import arange, interp
    n = len(signals['power'])
    old_time = arange(n)
    nPts = int(n * SampleRate)  # 32-bit integer
    new_time = arange(nPts) / SampleRate
    power = interp(new_time, old_time, signals['power'])
    if hasHR:
        heart_rate = interp(new_time, old_time, signals['heart_rate'])

    # power zones from "Cyclist's Training Bible", 5th ed., by Joe Friel, p51
    FTP = ThresholdPower
    pZones = {
        1: [0, 0.55 * FTP],
        2: [0.55 * FTP, 0.75 * FTP],
        3: [0.75 * FTP, 0.90 * FTP],
        4: [0.90 * FTP, 1.05 * FTP],
        5: [1.05 * FTP, 1.20 * FTP],
        6: [1.20 * FTP, 1.50 * FTP],
        7: [1.50 * FTP, 2.50 * FTP]
    }

    # heart-rate zones from "Cyclist's Training Bible" 5th ed. by Joe Friel, p50
    FTHR = ThresholdHR
    hZones = {
        1: [0, 0.82 * FTHR],  # 1
        2: [0.82 * FTHR, 0.89 * FTHR],  # 2
        3: [0.89 * FTHR, 0.94 * FTHR],  # 3
        4: [0.94 * FTHR, 1.00 * FTHR],  # 4
        5: [1.00 * FTHR, 1.03 * FTHR],  # 5a
        6: [1.03 * FTHR, 1.06 * FTHR],  # 5b
        7: [1.07 * FTHR, 1.15 * FTHR]
    }  # 5c

    def LocateZone(x, zones):
        Z = 1
        if x >= zones[2][0]: Z = 2
        if x >= zones[3][0]: Z = 3
        if x >= zones[4][0]: Z = 4
        if x >= zones[5][0]: Z = 5
        if x >= zones[6][0]: Z = 6
        if x >= zones[7][0]: Z = 7
        return Z

    # define boxcar averages used to test for upward transition out of
    # indicated zone.
    fpZ1 = ForwardBoxcarAverage(power, window=90, SampleRate=SampleRate)
    fpZ2 = ForwardBoxcarAverage(power, window=60, SampleRate=SampleRate)
    fpZ3 = ForwardBoxcarAverage(power, window=45, SampleRate=SampleRate)
    fpZ4 = ForwardBoxcarAverage(power, window=30, SampleRate=SampleRate)
    fpZ5 = ForwardBoxcarAverage(power, window=15, SampleRate=SampleRate)
    fpZ6 = ForwardBoxcarAverage(power, window=5, SampleRate=SampleRate)
    # fpZ7 not needed

    # assemble these into a dictionary:
    # so that I could test
    #     if LocateZone( FBoxCars[CurrentZone][i], pZones )
    #         > CurrentZone:
    FBoxCars = {
        1: fpZ1,
        2: fpZ2,
        3: fpZ3,
        4: fpZ4,
        5: fpZ5,
        6: fpZ6
    }  # Z7 not needed

    # define boxcar averages used to test for downward transition into
    # indicated zone.
    cpZ1 = CenteredBoxcarAverage(power, window=60, SampleRate=SampleRate)
    cpZ2 = CenteredBoxcarAverage(power, window=45, SampleRate=SampleRate)
    cpZ3 = CenteredBoxcarAverage(power, window=30, SampleRate=SampleRate)
    cpZ4 = CenteredBoxcarAverage(power, window=15, SampleRate=SampleRate)
    cpZ5 = CenteredBoxcarAverage(power, window=7, SampleRate=SampleRate)
    cpZ6 = CenteredBoxcarAverage(power, window=3, SampleRate=SampleRate)
    # fpZ7 not needed

    # assemble these into a dictionary:
    # so that I could test
    #     if LocateZone( CBoxCars[CurrentZone][i], pZones )
    #         > CurrentZone:
    CBoxCars = {
        1: cpZ1,
        2: cpZ2,
        3: cpZ3,
        4: cpZ4,
        5: cpZ5,
        6: cpZ6
    }  # Z7 not needed

    from numpy import array, arange, append, zeros, cumsum, average

    cp2 = zeros(nPts)
    fboxpower = zeros(nPts)
    cboxpower = zeros(nPts)
    zone = zeros(nPts)
    zone_mid = zeros(nPts)
    CurrentZone = 1

    # create a phaseless, lowpass-filtered signal for downward transitions
    # see
    #   https://docs.scipy.org/doc/scipy/reference/signal.html
    from scipy import signal
    poles = 4
    cutoff = 0.1  # Hz
    Wn = cutoff / (SampleRate / 2)
    PadLen = int(SampleRate / cutoff)
    b, a = signal.butter(poles, Wn, btype='lowpass')
    # lpfpower    = signal.filtfilt(b, a, power, padlen=PadLen)

    #   calculate zone midpoints for plotting
    ZoneMidPoint = {}  # empty dictionary
    ZoneMidPoint[1] = (pZones[1][0] + pZones[1][1]) / 2
    ZoneMidPoint[2] = (pZones[2][0] + pZones[2][1]) / 2
    ZoneMidPoint[3] = (pZones[3][0] + pZones[3][1]) / 2
    ZoneMidPoint[4] = (pZones[4][0] + pZones[4][1]) / 2
    ZoneMidPoint[5] = (pZones[5][0] + pZones[5][1]) / 2
    ZoneMidPoint[6] = (pZones[6][0] + pZones[6][1]) / 2
    ZoneMidPoint[7] = (pZones[7][0] + pZones[7][1]) / 2

    for i, p in zip(range(nPts), power):

        #   compute the centered 3-second power
        #raise RuntimeError("need to account for sample rate in cp2")
        sr = int(SampleRate)
        if i == 0:
            cp2[i] = power[i]
        elif i < 2 * sr:
            cp2[i] = average(power[0:i])
        elif i > nPts - 2 * sr:
            cp2[i] = average(power[i - sr:])
        else:
            cp2[i] = average(power[i - sr:i + sr + 1])

        #   upward transition
        cz = CurrentZone  # short name
        if cz < 7:
            tz = 7  # Test Zone
            while tz > cz:
                if  (LocateZone(           cp2[i], pZones ) >= tz) \
                  & (LocateZone( FBoxCars[tz-1][i], pZones ) >= tz):
                    CurrentZone = tz
                    zone[i] = CurrentZone
                    break
                tz -= 1
        #   downward transition. Avoid 2nd test if in Z1.
        #   use centered-boxcar average to avoid getting "trapped"
        #   in low zones.
        if cz > 1:
            tz = cz - 1
            while tz >= 1:
                if  (LocateZone(          cp2[i], pZones ) <= tz) \
                  & (LocateZone( CBoxCars[tz][i], pZones ) <= tz) \
                  & (LocateZone( FBoxCars[tz][i], pZones ) <= tz):
                    CurrentZone = tz
                    zone[i] = CurrentZone
                    break
                tz -= 1

        # the filtered power comes from CurrentZone after any transition
        fboxpower[i] = FBoxCars[min(CurrentZone, 6)][i]
        cboxpower[i] = CBoxCars[max(CurrentZone - 1, 1)][i]

        #   calculate zone midpoints for plotting
        zone_mid[i] = ZoneMidPoint[CurrentZone]

    # get zone bounds for plotting
    p_zone_bounds = [
        pZones[1][0], pZones[2][0], pZones[3][0], pZones[4][0], pZones[5][0],
        pZones[6][0], pZones[7][0], pZones[7][1]
    ]

    h_zone_bounds = [
        0.4 * FTHR,  # better plotting
        hZones[2][0],
        hZones[3][0],
        hZones[4][0],
        hZones[5][0],
        hZones[6][0],
        hZones[7][0],
        hZones[7][1]
    ]

    ############################################################
    #                  plotting                                #
    ############################################################

    #
    #   extract lap times
    #
    from activity_tools import extract_activity_laps
    activity = Activity(FitFilePath)
    laps = extract_activity_laps(activity)
    lap_start_time = laps['start_time']  # datetime object
    lap_timestamp = laps['timestamp']
    nLaps = len(lap_start_time)
    t0 = signals['metadata']['timestamp']
    lap_start_sec = zeros(nLaps)  # lap start times in seconds
    for i in range(nLaps):
        tBeg = (lap_start_time[i] - t0).total_seconds()
        tEnd = (lap_timestamp[i] - t0).total_seconds()
        lap_start_sec[i] = tBeg

    # time plot
    import matplotlib.pyplot as plt
    import matplotlib.dates as md
    from matplotlib.dates import date2num, DateFormatter
    import datetime as dt
    base = dt.datetime(2014, 1, 27, 0, 0, 0)
    x = [base + dt.timedelta(seconds=t) for t in new_time]
    x = date2num(x)  # Convert to matplotlib format
    x_laps  = [ base + dt.timedelta(seconds=t)   \
                for t in lap_start_sec.astype('float') ]
    x_laps = date2num(x_laps)
    if hasHR:
        fig1, (ax0, ax1) = plt.subplots(nrows=2, sharex=True)
        ax0.plot_date(x, heart_rate, 'r-', linewidth=3)
        ax0.set_yticks(h_zone_bounds, minor=False)
        ax0.grid(True)
        ax0.set_title('heart rate, BPM')
        for i in range(nLaps):
            ax0.axvline(x_laps[i], label=str(i + 1))
    else:
        fig1, ax1 = plt.subplots(nrows=1, sharex=True)
    ax1.plot_date(x, power, 'k-', linewidth=1)
    ax1.plot_date(x, fboxpower, 'm-', linewidth=1)
    ax1.plot_date(x, cp2, 'r.', markersize=4)
    ax1.plot_date(x, cboxpower, 'b-', linewidth=1)
    ax1.plot_date(x, zone_mid, 'g-', linewidth=3)
    ax1.xaxis.set_major_formatter(DateFormatter('%H:%M:%S'))
    ax1.set_yticks(p_zone_bounds, minor=False)
    ax1.grid(True)
    ax1.set_title('power, watts')
    ax1.legend(['power', 'FBoxCar', 'cp2', 'CBoxCar', 'zone mid'],
               loc='upper left')
    for i in range(nLaps):
        ax1.axvline(x_laps[i], label=str(i + 1))
    fig1.autofmt_xdate()
    fig1.suptitle('Power Zone Detection', fontsize=20)
    fig1.tight_layout()
    fig1.canvas.set_window_title(FitFilePath)
    plt.show()

    # better histogram plot with control of counts
    from numpy import histogram
    PowerCounts, PowerBins = histogram(power, bins=p_zone_bounds)
    ZoneCounts, ZoneBins = histogram(zone_mid, bins=p_zone_bounds)
    fig2, ax = plt.subplots()
    bar_width = 0.35
    opacity = 0.4
    #error_config = {'ecolor': '0.3'}
    zone_ints = arange(7) + 1
    LogY = True
    rects1 = ax.bar(zone_ints,
                    PowerCounts / SampleRate / 60,
                    bar_width,
                    alpha=opacity,
                    color='b',
                    log=LogY,
                    label='raw power')
    rects2 = ax.bar(zone_ints + bar_width,
                    ZoneCounts / SampleRate / 60,
                    bar_width,
                    alpha=opacity,
                    color='r',
                    log=LogY,
                    label='detected zone')
    ax.set_xlabel('Zone')
    ax.set_ylabel('minutes')
    ax.set_title('Zone Detection Histogram')
    ax.set_xticks(zone_ints + bar_width / 2)
    ax.set_xticklabels(('Rec', 'End', 'Tmp', 'Thr', 'VO2', 'An', 'NM'))
    ax.legend()
    fig2.tight_layout()
    fig2.canvas.set_window_title(FitFilePath)
    plt.show()

    # formatted print of histogram
    print >> OutStream, 'Power Zone Histogram:'
    for i in range(7):
        dur = ZoneCounts[i] / SampleRate
        pct = dur / sum(ZoneCounts / SampleRate) * 100
        hh = dur // 3600
        mm = (dur % 3600) // 60
        ss = (dur % 3600) % 60
        print >> OutStream, '    Zone %i: %2i:%02i:%02i (%2i%%)' \
                            % (i+1, hh, mm, ss, pct)
    dur = sum(ZoneCounts) / SampleRate
    hh = dur // 3600
    mm = (dur % 3600) // 60
    ss = (dur % 3600) % 60
    print >> OutStream, '     total: %2i:%02i:%02i' % (hh, mm, ss)

    def ClosePlots():
        plt.close('all')

    return ClosePlots
コード例 #23
0
ファイル: read_files.py プロジェクト: akroshko/emacs-otlb
def main_fit(argv):
    filename=sys.argv[1]
    activity = Activity(filename)
    activity.parse()
    session = next(activity.get_records_by_type('session'))
    session_dict={}
    start_time=local_date_to_utc(session.get_data("start_time"))
    real_start_time=session.get_data("start_time")
    session_dict['elapsed-time']=session.get_data("total_elapsed_time")
    timer_time=session.get_data("total_timer_time")
    session_dict['timer-time']=timer_time
    distance=session.get_data("total_distance")
    session_dict['distance']=distance
    # TODO: possibility do this for very small distances
    if distance == 0.0:
        # TODO: change to None?
        session_dict['pace']=0.0
    else:
        session_dict['pace']=(1000./(distance/timer_time))/60.
    theid=time_to_id(start_time)
    session_dict['id']=theid
    session_dict['start-time']=theid
    end_time=local_date_to_utc(session.get_data("timestamp"))
    end_time_id=time_to_id(end_time)
    session_dict['end-time']=end_time_id
    session_dict['laps']={}
    session_dict['laps']['start-time']=[]
    session_dict['laps']['start-timestamp']=[]
    session_dict['laps']['end-time']=[]
    session_dict['laps']['end-timestamp']=[]
    session_dict['laps']['timer-time']=[]
    session_dict['laps']['distance']=[]
    session_dict['laps']['pace']=[]
    session_dict['laps']['maximum-speed']=[]
    session_dict['laps']['average-heart-rate']=[]
    session_dict['laps']['maximum-heart-rate']=[]
    session_dict['laps']['trigger-method']=[]
    session_dict['laps']['trackpoints']=[]
    session_dict['lap start times']=[]
    session_dict['lap end times']=[]
    session_dict['lap start distances']=[]
    session_dict['lap end distances']=[]
    for lap in activity.get_records_by_type('lap'):
        session_dict['laps']['start-time'].append(str(local_date_to_utc(lap.get_data("start_time"))))
        start_time=lap.get_data("start_time")
        session_dict['laps']['end-time'].append(str(local_date_to_utc(lap.get_data("timestamp"))))
        end_time=lap.get_data("timestamp")
        timer_time=lap.get_data("total_timer_time")
        session_dict['laps']['timer-time'].append(timer_time)
        distance=lap.get_data("total_distance")
        session_dict['laps']['distance'].append(distance)
        if distance == 0.0 or distance == None or timer_time == None:
            # TODO: change to None?
            session_dict['laps']['pace'].append(0.)
        else:
            session_dict['laps']['pace'].append((1000./(distance/timer_time))/60.)
        session_dict['laps']['maximum-speed'].append(lap.get_data("max_speed")) # opt
        session_dict['laps']['average-heart-rate'].append(lap.get_data("avg_heart_rate")) #opt
        session_dict['laps']['maximum-heart-rate'].append(lap.get_data("max_heart_rate")) #opt
        session_dict['laps']['trigger-method'].append(LAP_TRIGGER_MAP.get(lap.get_data("lap_trigger"), "Manual"))
        first_track_point=True
        for trackpoint in activity.get_records_by_type('record'):
            tts = trackpoint.get_data("timestamp")
            if tts >= start_time and tts <= end_time:
                if first_track_point==True:
                    session_dict['lap start times'].append((trackpoint.get_data("timestamp")-real_start_time).seconds)
                    session_dict['lap start distances'].append(trackpoint.get_data("distance"))
                    first_track_point=False
                session_dict['laps']['trackpoints'].append({})
                session_dict['laps']['trackpoints'][-1]['timestamp']=str(local_date_to_utc(trackpoint.get_data("timestamp")))
                session_dict['laps']['trackpoints'][-1]['elapsed-time']=(trackpoint.get_data("timestamp")-real_start_time).seconds
                session_dict['laps']['trackpoints'][-1]['latitude-position']=trackpoint.get_data("position_lat")
                session_dict['laps']['trackpoints'][-1]['longitude-position']=trackpoint.get_data("position_long")
                session_dict['laps']['trackpoints'][-1]['distance']=trackpoint.get_data("distance")
                session_dict['laps']['trackpoints'][-1]['altitude']=trackpoint.get_data("altitude")
                session_dict['laps']['trackpoints'][-1]['speed']=trackpoint.get_data("speed")
                session_dict['laps']['trackpoints'][-1]['heart rate']=trackpoint.get_data("heart_rate")
                # TODO: make sure this works out OK, get last valid position
                if session_dict['laps']['trackpoints'][-1]['latitude-position'] == None:
                    session_dict['laps']['trackpoints'][-1]['latitude']=None
                else:
                    session_dict['laps']['trackpoints'][-1]['latitude']=str(semicircle_to_degrees(session_dict['laps']['trackpoints'][-1]['latitude-position']))
                if session_dict['laps']['trackpoints'][-1]['longitude-position'] == None:
                    session_dict['laps']['trackpoints'][-1]['longitude']=None
                else:
                    session_dict['laps']['trackpoints'][-1]['longitude']=str(semicircle_to_degrees(session_dict['laps']['trackpoints'][-1]['longitude-position']))
        # add first and last
        session_dict['lap end times'].append(session_dict['laps']['trackpoints'][-1]['elapsed-time'])
        session_dict['lap end times'].append(session_dict['laps']['trackpoints'][-1]['distance'])
    # update things from laps
    session_dict['start-latitude']=session_dict['laps']['trackpoints'][0]['latitude']
    session_dict['start-longitude']=session_dict['laps']['trackpoints'][0]['longitude']
    session_dict['end-latitude']=session_dict['laps']['trackpoints'][-1]['latitude']
    session_dict['end-longitude']=session_dict['laps']['trackpoints'][-1]['longitude']
    return session_dict
コード例 #24
0
ファイル: plot_heartrate.py プロジェクト: j33433/FitFiles
def plot_heartrate(FitFilePath, ConfigFile=None, OutStream=sys.stdout):

    verbose = False

    (FilePath, FitFileName) = os.path.split(FitFilePath)

    if ConfigFile is None:
        ConfigFile = FindConfigFile('', FilePath)
    if (ConfigFile is None) or (not os.path.exists(ConfigFile)):
        raise IOError('Configuration file not specified or found')

    #
    #   Parse the configuration file
    #
    from ConfigParser import ConfigParser
    config = ConfigParser()
    config.read(ConfigFile)
    print >> OutStream, 'reading config file ' + ConfigFile
    WeightEntry = config.getfloat('user', 'weight')
    WeightToKg = config.getfloat('user', 'WeightToKg')
    weight = WeightEntry * WeightToKg
    age = config.getfloat('user', 'age')
    sex = config.get('user', 'sex')
    EndurancePower = config.getfloat('power', 'EndurancePower')
    ThresholdPower = config.getfloat('power', 'ThresholdPower')
    EnduranceHR = config.getfloat('power', 'EnduranceHR')
    ThresholdHR = config.getfloat('power', 'ThresholdHR')
    HRTimeConstant = config.getfloat('power', 'HRTimeConstant')
    HRDriftRate = config.getfloat('power', 'HRDriftRate')
    print >> OutStream, 'WeightEntry   : ', WeightEntry
    print >> OutStream, 'WeightToKg    : ', WeightToKg
    print >> OutStream, 'weight        : ', weight
    print >> OutStream, 'age           : ', age
    print >> OutStream, 'sex           : ', sex
    print >> OutStream, 'EndurancePower: ', EndurancePower
    print >> OutStream, 'ThresholdPower: ', ThresholdPower
    print >> OutStream, 'EnduranceHR   : ', EnduranceHR
    print >> OutStream, 'ThresholdHR   : ', ThresholdHR
    print >> OutStream, 'HRTimeConstant : ', HRTimeConstant
    print >> OutStream, 'HRDriftRate    : ', HRDriftRate

    from datetime import datetime
    from fitparse import Activity
    from activity_tools import extract_activity_signals

    required_signals = ['heart_rate']

    # get the signals
    activity = Activity(FitFilePath)
    signals = extract_activity_signals(activity)

    if not all(s in signals.keys() for s in required_signals):
        msg = 'required signals not in file'
        print >> OutStream, msg
        print >> OutStream, 'Signals required:'
        for s in required_signals:
            print >> OutStream, '   ' + s
        print >> OutStream, 'Signals contained:'
        for s in signals.keys():
            print >> OutStream, '   ' + s
        raise IOError(msg)

    time_signal = signals['time']
    heartrate_signal = signals['heart_rate']

    # plot the heart rate
    import numpy as np

    ########################################################################
    ###         Compute Calories                                         ###
    ########################################################################
    '''
    Formula widely available. One site:
        https://www.easycalculation.com/formulas/heart-rate-calorie-burn.html

        For Male,
        Calorie Burned = (  (   -55.0969
                                + (0.6309 x HR)
                                + (0.1988 x W )
                                + (0.2017 x A )  )
                            / 4.184) x 60 x T

        For Female,
        Calorie Burned = (  (   -20.4022
                                + (0.4472 x HR)
                                + (0.1263 x W )
                                + (0.0740 x A )  )
                            / 4.184) x 60 x T

        Where,
        HR  = Heart Rate
        W   = Weight in kilograms
        A   = Age
        T   = Exercise duration time in hours

    However, the formula is adapted to the user's power capacity:
        :   At EnduranceHR, CalPerMin is set to EndurancePower
            (assuming efficiency of 1/4.184 so that calories burned
            equal kJ expended).
        :   At ThresholdHR, CalPerMin is set to ThresholdPower.
        :   Between EnduranceHR and ThresholdHR, CalPerMin is
            interpolated.
        :   Below EnduranceHR, CalPerMin follows the formula, but
            it is scaled onto [EnduranceHR, EndurancePower].
        :   Above ThresholdHR, CalPerMin follows the formula, but
            it is scaled onto [ThresholdHR, ThresholdPower].

    '''

    hr_sig = signals['heart_rate']
    t_sig = signals['time']
    dt_sig = np.append(np.array([1.0]), t_sig[1:] - t_sig[0:-1])
    nPts = t_sig.size
    calories = np.zeros(nPts)

    EnduranceBurn = EndurancePower * 3600 / 1e3 / 60  # Cal/min
    print >> OutStream, 'EnduranceBurn = %5.2f cal/min' % EnduranceBurn
    ThresholdBurn = ThresholdPower * 3600 / 1e3 / 60  # Cal/min
    print >> OutStream, 'ThresholdBurn = %5.2f cal/min' % ThresholdBurn

    if sex == 'male':

        #   calibration at endurance
        EnduranceCoef   = EnduranceBurn             \
                        / ( -55.0969                \
                            + 0.6309*EnduranceHR    \
                            + 0.1988*weight         \
                            + 0.2017*age)           \
                        * 4.184

        #   calibration at threshold
        ThresholdCoef   = ThresholdBurn             \
                        / ( -55.0969                \
                            + 0.6309*EnduranceHR    \
                            + 0.1988*weight         \
                            + 0.2017*age)           \
                        * 4.184

    else:  # female

        #   calibration at endurance
        EnduranceCoef   = EnduranceBurn             \
                        / ( -20.4022                \
                            + 0.4472*EnduranceHR    \
                            + 0.1263*weight         \
                            + 0.0740*age)           \
                        * 4.184

        #   calibration at threshold
        ThresholdCoef   = ThresholdBurn             \
                        / ( -20.4022                \
                            + 0.4472*EnduranceHR    \
                            + 0.1263*weight         \
                            + 0.0740*age)           \
                        * 4.184

    for i, dt, HR in zip(range(nPts), dt_sig, hr_sig):

        # calories per minute
        if HR >= EnduranceHR and HR <= ThresholdHR:
            CalPerMin   = EnduranceBurn                 \
                        + (HR-EnduranceHR)              \
                        * (ThresholdBurn-EnduranceBurn) \
                        / (ThresholdHR-EnduranceHR)
        else:
            coef = EnduranceCoef if (HR < EnduranceHR) else ThresholdCoef
            if sex == 'male':
                CalPerMin   = ( -55.0969        \
                                + 0.6309*HR     \
                                + 0.1988*weight \
                                + 0.2017*age)   \
                            / 4.184             \
                            * coef
            else:
                CalPerMin   = ( -20.4022        \
                                + 0.4472*HR     \
                                + 0.1263*weight \
                                + 0.0740*age)   \
                            / 4.184             \
                            * coef
        calories[i] = dt * CalPerMin / 60

    running_calories = np.cumsum(calories)

    print >> OutStream, 'total calories = %i' % running_calories[nPts - 1]

    ########################################################################
    ###         Zone Histogram                                           ###
    ########################################################################

    # heart-rate zones from "Cyclist's Training Bible" 5th ed. by Joe Friel, p50
    FTHR = ThresholdHR
    hZones = {
        1: ([0, 0.82 * FTHR], ' 1'),
        2: ([0.82 * FTHR, 0.89 * FTHR], ' 2'),
        3: ([0.89 * FTHR, 0.94 * FTHR], ' 3'),
        4: ([0.94 * FTHR, 1.00 * FTHR], ' 4'),
        5: ([1.00 * FTHR, 1.03 * FTHR], '5a'),
        6: ([1.03 * FTHR, 1.06 * FTHR], '5b'),
        7: ([1.07 * FTHR, 1.15 * FTHR], '5c')
    }
    h_zone_bounds = [
        0.4 * FTHR,  #  1 lo
        hZones[2][0][0],  #  2 lo
        hZones[3][0][0],  #  3 lo
        hZones[4][0][0],  #  4 lo
        hZones[5][0][0],  # 5a lo
        hZones[6][0][0],  # 5b lo
        hZones[7][0][0],  # 5c lo
        hZones[7][0][1]
    ]  # 5c hi
    h_zone_labels = [hZones[k][1] for k in range(1, 8)]

    ZoneCounts, ZoneBins = np.histogram(hr_sig, bins=h_zone_bounds)

    # formatted print of histogram
    SampleRate = 1.0
    print >> OutStream, 'Heart-Rate Zone Histogram:'
    for i in range(7):
        dur = ZoneCounts[i] / SampleRate
        pct = dur / sum(ZoneCounts / SampleRate) * 100
        hh = dur // 3600
        mm = (dur % 3600) // 60
        ss = (dur % 3600) % 60
        print >> OutStream, '    Zone %2s: %2i:%02i:%02i (%2i%%)' \
                            % ( h_zone_labels[i], hh, mm, ss, pct)
    dur = sum(ZoneCounts) / SampleRate
    hh = dur // 3600
    mm = (dur % 3600) // 60
    ss = (dur % 3600) % 60
    print >> OutStream, '      total: %2i:%02i:%02i' % (hh, mm, ss)

    ########################################################################
    ###         Power & TSS Estimation                                   ###
    ########################################################################

    from endurance_summary import BackwardMovingAverage

    # see
    #   https://docs.scipy.org/doc/scipy/reference/signal.html
    from scipy import signal
    poles = 3
    cutoff = 0.10  # Hz
    Wn = cutoff / (SampleRate / 2)
    '''
    # construct and apply a differentiating, lowpass filter
    NumB, DenB  = signal.butter(poles, Wn, btype='lowpass',
                                output='ba', analog=True)
    NumF        = signal.convolve( NumB, [1,0])     # add differentiator
    bDLP, aDLP  = signal.bilinear( NumF,DenB, fs=SampleRate )
    hr_dot      = signal.lfilter(bDLP, aDLP, hr_sig)
    '''

    # apply a phaseless lowpass filter, then differentiate.
    # for some reason, running the Butterworth analog filter
    # through bilinear() gives a better result. Otherwise,
    # set analog=False to get coefficients directly.
    PadLen = int(SampleRate / cutoff)  # one period of cutoff
    NumB, DenB = signal.butter(poles,
                               Wn,
                               btype='lowpass',
                               output='ba',
                               analog=True)
    bLPF, aLPF = signal.bilinear(NumB, DenB, fs=SampleRate)
    hr_lpf = signal.filtfilt(bLPF, aLPF, hr_sig, padlen=PadLen)
    hr_dot = np.gradient(hr_lpf, 1 / SampleRate)

    FTP = ThresholdPower
    FTHR = ThresholdHR
    tau = HRTimeConstant
    PwHRTable = np.array([
        [0, 0.50 * FTHR],  # Active resting HR
        [0.55 * FTP, 0.70 * FTHR],  # Recovery
        [0.70 * FTP, 0.82 * FTHR],  # Aerobic threshold
        [1.00 * FTP, FTHR],  # Functional threshold
        [1.20 * FTP, 1.03 * FTHR],  # Aerobic capacity
        [1.50 * FTP, 1.06 * FTHR]
    ])  # Max HR

    # loop through the time series building running power and TSS.
    # Notice this is necessary because HRd[i] depends on TSS[i-1].
    sPower = np.zeros(nPts)
    TSS = np.zeros(nPts)
    HRd = np.zeros(nPts)  # fatigue drift
    HRp = np.zeros(nPts)  # power target
    p30 = np.zeros(nPts)  # 30-sec boxcar average
    w = int(30 * SampleRate)  # window for boxcar
    NPower = np.zeros(nPts)  # normalized power

    for i in range(1, nPts):
        HRd[i] = HRDriftRate * TSS[i - 1]
        HRp[i] = hr_sig[i] + tau * hr_dot[i] - HRd[i]
        sPower[i] = np.interp(HRp[i], PwHRTable[:, 1], PwHRTable[:, 0])
        if i < w:
            p30[i] = np.average(sPower[:i + 1])  # include i
        else:
            p30[i] = np.average(sPower[i - w:i + 1])  # include i
        NPower[i] = np.average(p30[:i]**4)**(0.25)
        TSS[i] = t_sig[i] / 36 * (NPower[i] / FTP)**2

    print >> OutStream, 'estimated NP   = %6i W' % NPower[-1]
    print >> OutStream, 'estimated work = %6i kJ' % \
                ( np.cumsum(sPower)[-1] / 1e3 / SampleRate )
    print >> OutStream, 'estimated TSS  = %6i TSS' % TSS[-1]
    if 'power' in signals.keys():
        mPower = signals['power']
        print >> OutStream, 'measured work  = %6i kJ' % \
                ( np.cumsum( mPower )[-1] / 1e3 / SampleRate )
        mP30 = BackwardMovingAverage(mPower)
        mNPower = np.average(mP30**4)**(0.25)
        mTSS = t_sig[-1] / 36 * (mNPower / FTP)**2
        print >> OutStream, 'measured NP    = %6i W' % mNPower
        print >> OutStream, 'measured TSS   = %6i TSS' % mTSS

    ###########################################################
    ###             plotting                                ###
    ###########################################################

    # power zones from "Cyclist's Training Bible", 5th ed., by Joe Friel, p51
    pZones = {
        1: [0, 0.55 * FTP],
        2: [0.55 * FTP, 0.75 * FTP],
        3: [0.75 * FTP, 0.90 * FTP],
        4: [0.90 * FTP, 1.05 * FTP],
        5: [1.05 * FTP, 1.20 * FTP],
        6: [1.20 * FTP, 1.50 * FTP],
        7: [1.50 * FTP, 2.50 * FTP]
    }

    # heart-rate zones from "Cyclist's Training Bible" 5th ed. by Joe Friel, p50
    hZones = {
        1: [0, 0.82 * FTHR],  # 1
        2: [0.82 * FTHR, 0.89 * FTHR],  # 2
        3: [0.89 * FTHR, 0.94 * FTHR],  # 3
        4: [0.94 * FTHR, 1.00 * FTHR],  # 4
        5: [1.00 * FTHR, 1.03 * FTHR],  # 5a
        6: [1.03 * FTHR, 1.06 * FTHR],  # 5b
        7: [1.07 * FTHR, 1.15 * FTHR]
    }  # 5c

    # get zone bounds for plotting
    p_zone_bounds = [
        pZones[1][0], pZones[2][0], pZones[3][0], pZones[4][0], pZones[5][0],
        pZones[6][0], pZones[7][0], pZones[7][1]
    ]

    h_zone_bounds = [
        0.4 * FTHR,  # better plotting
        hZones[2][0],
        hZones[3][0],
        hZones[4][0],
        hZones[5][0],
        hZones[6][0],
        hZones[7][0],
        hZones[7][1]
    ]

    # power simulation plot
    import matplotlib.pyplot as plt
    import matplotlib.dates as md
    from matplotlib.dates import date2num, DateFormatter
    import datetime as dt
    base = dt.datetime(2014, 1, 1, 0, 0, 0)
    x = [base + dt.timedelta(seconds=t) for t in t_sig.astype('float')]
    x = date2num(x)  # Convert to matplotlib format
    fig1, (ax0, ax1) = plt.subplots(nrows=2, sharex=True)
    ax0.plot_date(x, hr_sig, 'r-', linewidth=2)
    ax0.plot_date(x, tau * hr_dot, 'b-', linewidth=2)
    ax0.plot_date(x, HRd, 'g-', linewidth=2)
    ax0.plot_date(x, HRp, 'k-', linewidth=2)
    ax0.set_yticks(h_zone_bounds, minor=False)
    ax0.grid(True)
    ax0.legend(['HR', 'tau*HRdot', 'HRd', 'HRp'], loc='upper left')
    ax0.set_title('heart rate, BPM')
    if 'power' in signals.keys():
        mPower = signals['power']
        ax1.plot_date(x, mPower, 'k-', linewidth=1)
        ax1.plot_date(x, sPower, 'b-', linewidth=2)
        ax1.legend(['measured power', 'simulated power'], loc='upper left')
    else:
        ax1.plot_date(x, sPower, 'b-', linewidth=2)
        ax1.legend(['simulated power'], loc='upper left')
    ax1.xaxis.set_major_formatter(DateFormatter('%H:%M:%S'))
    ax1.set_yticks(p_zone_bounds, minor=False)
    ax1.grid(True)
    ax1.set_title('power, watts')
    fig1.autofmt_xdate()
    fig1.suptitle('Pw:HR Transfer Function', fontsize=20)
    fig1.tight_layout()
    fig1.canvas.set_window_title(FitFilePath)
    plt.show()

    # plot heart rate and calories
    import matplotlib.dates as md
    from matplotlib.dates import date2num, DateFormatter
    import datetime as dt
    base = dt.datetime(2014, 1, 27, 0, 0, 0)
    x = [base + dt.timedelta(seconds=t) for t in t_sig]
    x = date2num(x)  # Convert to matplotlib format
    fig1, ax0 = plt.subplots()
    ax0.plot_date(x, hr_sig, 'r-', linewidth=3)
    ax0.set_yticks(h_zone_bounds, minor=False)
    ax0.grid(True)
    ax0.set_title('heart rate, BPM')
    ax0.set_title('Heart Rate Analysis')
    fig1.autofmt_xdate()
    fig1.tight_layout()
    fig1.canvas.set_window_title(FitFilePath)
    plt.show()

    #    plt.plot(t_sig/60, hr_sig, 'r.-')
    #    plt.title('Heart Rate and Calories')
    #    plt.ylabel('BPM')
    #    plt.subplot(2, 1, 2)
    #    plt.plot(t_sig/60, running_calories, 'b.-')
    #    plt.xlabel('time (min)')
    #    plt.ylabel('calories')

    # heart rate histogram plot
    fig2, ax2 = plt.subplots()
    bar_width = 0.80  # 0.35
    opacity = 0.4
    #error_config = {'ecolor': '0.3'}
    zone_ints = np.arange(7) + 1
    LogY = False
    rects1 = ax2.bar(zone_ints + bar_width / 2,
                     ZoneCounts / SampleRate / 60,
                     bar_width,
                     alpha=opacity,
                     color='r',
                     log=LogY,
                     label='heart rate')
    ax2.set_xlabel('Zone')
    ax2.set_ylabel('minutes')
    ax2.set_title('Heart Rate Zone Histogram')
    ax2.set_xticks(zone_ints + bar_width / 2)
    ax2.set_xticklabels(h_zone_labels)
    ax2.legend()
    fig2.tight_layout()
    fig2.canvas.set_window_title(FitFilePath)
    plt.show()

    def ClosePlots():
        plt.close('all')

    return ClosePlots
コード例 #25
0
# search_records.py

# crack the record.type
from datetime import datetime
from fitparse import Activity
FileLoc = r'S:\\will\\documents\\OneDrive\\bike\\activities\\will\\'
#fitfilepath = FileLoc + r'2018-06-01-11-13-49.fit'  # long ride
fitfilepath = FileLoc + r'2016-07-09-13-52-59.fit'  # problems
activity = Activity(fitfilepath)
activity.parse()
records = activity.records
record_types = set()
for record in records:
    record_types.add(record.type.name)
fields_by_type = {}
for RType in record_types:
    print 'type = %s' % RType
    records = activity.get_records_by_type(RType)
    RecNum = 0
    fields_by_type[RType] = set()
    for record in records:
        valid_field_names = record.get_valid_field_names()
        #print '    name = ',
        for name in valid_field_names:
            fields_by_type[RType].add(name)
#            print name + ', ',
#            if 'balance' in name.lower():
#                print 'balance found in %s field within %s record type' \
#                        % (name, RType)
#        if RType == 'record':
#            print '*',
コード例 #26
0
ファイル: fit_parser.py プロジェクト: jherland/garmin_edge
 def __init__(self, path, tz_offset=None):
     self.path = path
     self.tz_offset = tz_offset
     self.act = Activity(path)
     self.act.parse()
コード例 #27
0
ファイル: test.py プロジェクト: bomdicScott/pub_fit_parsing
    for fname in glob.glob(
            'C:\\Users\\sean\\Documents\\python-fitparse-master\\tests\\data\\*.fit'
    ):
        time,distance,heartrate,velocity_smooth ,lat,long,altitude,watts,cadence,temp,grade_smooth = [],[],[],[],[],[],[],[],[],[],[]
        header, header_dict, header_csv = True, {}, []  #for create header.json

        count += 1

        print fname

        print_hook_func = None
        if not quiet:
            print_hook_func = print_record

        record_number = 0
        a = Activity(fname)
        a.parse(hook_func=print_hook_func)

        if time != []:
            min_time = min(time)
            for i in range(len(time)):
                time[i] = time[i] - min_time

            write_zero(csv_dict)

            ID = fname[58:-4]
            if len(distance) != 0:
                distance_max = max(distance) / 1000
            else:
                distance_max = 0
            if len(velocity_smooth) != 0:
コード例 #28
0
        fit_id_list = []
        try:
            direct = open(com_path+"direct_table.csv","rb")
            for fit_id in csv.reader(direct):
                fit_id_list += [fit_id[0]]
            direct.close()
        except:
            pass

        ID = fname[38:-4]#depend on your com_path
        if ID not in fit_id_list:
            print fname+" Start parsing!!"
            time_start = times.time()

            a = Activity(fname)
            a.parse(hook_func=print_record)

            #check None value
            for data in csv_dict:
                if(csv_dict[data] < csv_dict["time"]):
                    list = eval(data)
                    list += [-1]
                    csv_dict[data] += 1
                    missing_count[data]+=1

            if time != []:
                min_time = min(time)
                for i in range(len(time)):
                    time[i] = time[i] - min_time
コード例 #29
0
def get_records(f):
    filename = '%s/%s' % (fit_files_path, f)
    activity = Activity(filename)
    activity.parse()
    records = activity.get_records_by_type('record')
    return records
コード例 #30
0
ファイル: force_analysis.py プロジェクト: j33433/FitFiles
def force_analysis(FitFilePath, ConfigFile=None, OutStream=sys.stdout):

    verbose = False

    (FilePath, FitFileName) = os.path.split(FitFilePath)

    if ConfigFile is None:
        ConfigFile = FindConfigFile('', FilePath)
    if (ConfigFile is None) or (not os.path.exists(ConfigFile)):
        raise IOError('Configuration file not specified or found')

    #
    #   Parse the configuration file
    #
    from ConfigParser import ConfigParser
    config = ConfigParser()
    config.read(ConfigFile)
    print >> OutStream, 'reading config file ' + ConfigFile
    CrankRadius     = config.getfloat( 'power', 'CrankRadius' ) \
                    / 25.4      # mm -> inches
    print >> OutStream, 'CrankRadius: ', CrankRadius, ' inches'

    from datetime import datetime
    from fitparse import Activity
    from activity_tools import extract_activity_signals

    required_signals = ['power', 'cadence']

    # get the signals
    activity = Activity(FitFilePath)
    signals = extract_activity_signals(activity)

    if not all(s in signals.keys() for s in required_signals):
        msg = 'required signals not in file'
        print >> OutStream, msg
        print >> OutStream, 'Signals required:'
        for s in required_signals:
            print >> OutStream, '   ' + s
        print >> OutStream, 'Signals contained:'
        for s in signals.keys():
            print >> OutStream, '   ' + s
        raise IOError(msg)

    SampleRate = 1.0  # Hz
    time_signal = signals['time']
    power = signals['power']
    cadence = signals['cadence']
    nPts = len(power)

    # Calculate leg force and reps
    from math import pi
    torque = np.zeros(nPts)
    ii = cadence.nonzero()[0]
    torque[ii]  = power[ii] / cadence[ii] / (2*pi/60)      \
            * 8.8507                                # N*m -> in*lb
    leg_force = torque / CrankRadius
    '''
    Reference from squat:
        At the end of the Anatomic Adaptation
        phase, I could squat 160lbx4x25. With 75% of my body weight
        (190 lbs), the total leg force was 300 lbs, or 150 lbs per leg.
        This went through a depth of 16 inches. So each leg performed
        work of 100 reps times 150 lbs times 16 inches, or
        240,000 in*lb, which is 27.117 kJ at 150 lb.
    '''
    SquatLegForce = (160 + 0.75 * 190) / 2  # lb
    SquatDepth = 16.0  # inches
    SquatReps = 100.0
    SquatWork       = SquatLegForce * SquatDepth * SquatReps    \
                    * 0.000112985416                # in*lb -> kJ

    MaxForce = max(SquatLegForce, max(leg_force))

    # Histogram bin edges. The first bin is underflow (includes data
    # down to the minimum regardless of the first edge), and the last
    # is overflow (includes data up to maximum regardless of the last edge).
    force_bins = np.arange(0, 82, 2)  # (0, 85, 5)
    cadence_bins = np.arange(26, 126, 2)  # (25, 125, 5)

    # compute the force-work histogram
    # Instead of counts, we have revs and work as a function of force.
    # So we have to perform the histogram manually.
    nFBins = len(force_bins) - 1
    force_width = (force_bins[1:nFBins + 1] - force_bins[0:nFBins]) * 0.95

    revs = np.zeros(nFBins)
    force_work = np.zeros(nFBins)
    for i in range(nFBins):
        FBinLo = force_bins[i] if i > 1 else leg_force.min()
        FBinHi = force_bins[i + 1] if i < nFBins else leg_force.max() * 1.1
        ii = np.nonzero(np.logical_and(leg_force >= FBinLo,
                                       leg_force < FBinHi))[0]
        # Compute the revs and work at these indices:
        revs[i] = sum(cadence[ii]) / 60 / SampleRate
        force_work[i]   = sum(power[ii]) / SampleRate   \
                        / 1000.0                        # J -> kJ

    # compute the cadence-work histogram
    nCBins = len(cadence_bins) - 1
    cadence_width = (cadence_bins[1:nCBins + 1] -
                     cadence_bins[0:nCBins]) * 0.95
    cadence_work = np.zeros(nCBins)
    for i in range(nCBins):
        CBinLo = cadence_bins[i] if i > 1 else leg_force.min()
        CBinHi = cadence_bins[i + 1] if i < nCBins else cadence.max() * 1.1
        ii = np.nonzero(np.logical_and(cadence >= CBinLo, cadence < CBinHi))[0]
        cadence_work[i] = sum(power[ii]) / SampleRate   \
                        / 1000.0                        # J -> kJ

    #
    #   Exposure Histogram
    #
    #    I want the plot to display force on the X axis, but this is
    #    the column (2nd) index, and the Y axis is the row (1st) index.
    #    So I need to transpose work2d at some point; it would probably
    #    be best to do so right up front since np.meshgrid() formats
    #    the coordinates this way.
    work2d = np.zeros([nCBins, nFBins])  # note shape!
    power_grid = np.zeros([nCBins + 1, nFBins + 1])
    force_grid, cadence_grid = np.meshgrid(force_bins, cadence_bins)
    for i in range(nFBins):
        FBinLo = force_bins[i] if i > 1 else leg_force.min()
        FBinHi = force_bins[i + 1] if i < nFBins else leg_force.max() * 1.1
        ii = np.nonzero(np.logical_and(leg_force >= FBinLo,
                                       leg_force < FBinHi))[0]
        for j in range(nCBins):
            CBinLo = cadence_bins[j] if j > 1 else cadence.min()
            CBinHi = cadence_bins[j + 1] if j < nCBins else cadence.max() * 1.1
            jj = np.nonzero(
                np.logical_and(cadence[ii] >= CBinLo, cadence[ii] < CBinHi))[0]
            work2d[j,i]     = sum(power[ii[jj]])    \
                            / SampleRate / 1000.0   # J -> kJ
            TorqueNM = force_bins[i] * CrankRadius / 8.8507
            power_grid[j, i] = TorqueNM * cadence_bins[j] * (2 * pi / 60)
    # last grid column
    i += 1
    TorqueNM = force_bins[i] * CrankRadius / 8.8507
    for j in range(nCBins + 1):
        power_grid[j, i] = TorqueNM * cadence_bins[j] * (2 * pi / 60)
    # last grid row. Leave j = nCBins
    for i in range(nFBins + 1):
        TorqueNM = force_bins[i] * CrankRadius / 8.8507
        power_grid[j, i] = TorqueNM * cadence_bins[j] * (2 * pi / 60)
    power_lines = np.arange(50, power_grid.max(), 50)

    ###########################################################
    ###             plotting                                ###
    ###########################################################

    # this needs to stay INSIDE the function or bad things happen
    import matplotlib.pyplot as plt

    # force & cadence time plot with grids at valid bin edges so that
    # underflow and overflow bins are shown accurately.
    import matplotlib.dates as md
    from matplotlib.dates import date2num, DateFormatter
    import datetime as dt
    base = dt.datetime(2014, 1, 1, 0, 0, 0)
    x = [base + dt.timedelta(seconds=t) for t in time_signal.astype('float')]
    x = date2num(x)  # Convert to matplotlib format
    fig1, (ax0, ax1) = plt.subplots(nrows=2, sharex=True)
    ax0.plot_date(x, leg_force, 'r-+', linewidth=1)
    ax0.grid(True)
    ax0.legend(['leg_force'], loc='upper left')
    ax0.set_title('Leg Force, lb')
    ax0.set_yticks(force_bins[1:-1], minor=False)
    ax1.plot_date(x, cadence, 'g-+', linewidth=1)
    ax1.legend(['cadence'], loc='upper left')
    ax1.xaxis.set_major_formatter(DateFormatter('%H:%M:%S'))
    ax1.grid(True)
    ax1.set_title('cadence, RPM')
    ax1.set_yticks(cadence_bins[1:-1], minor=False)
    fig1.autofmt_xdate()
    fig1.suptitle('Force Analysis', fontsize=20)
    fig1.tight_layout()
    fig1.canvas.set_window_title(FitFilePath)
    plt.show()

    # force_work histogram plot
    fig2, ax2 = plt.subplots()
    opacity = 0.8
    LogY = False
    rects1 = ax2.bar(force_bins[:-1],
                     force_work,
                     force_width,
                     align='edge',
                     alpha=opacity,
                     color='r',
                     log=LogY,
                     label='force')
    ax2.set_xlabel('Pedal Force, lb')
    ax2.set_ylabel('work, kJ')
    ax2.set_title('Pedal Force Work Histogram')
    ax2.legend()
    fig2.tight_layout()
    fig2.canvas.set_window_title(FitFilePath)
    plt.show()

    # cadence_work histogram plot
    fig4, ax4 = plt.subplots()
    opacity = 0.8
    rects4 = ax4.barh(cadence_bins[:-1],
                      cadence_work,
                      cadence_width,
                      align='edge',
                      alpha=opacity,
                      color='g',
                      log=False,
                      label='cadence')
    ax4.set_ylabel('cadence, RPM')
    ax4.set_xlabel('work, kJ')
    ax4.set_title('Cadence-Work Histogram')
    ax4.legend()
    fig4.tight_layout()
    fig4.canvas.set_window_title(FitFilePath)
    plt.show()

    # create a custom segmented colormap. I want zero to be a cool color
    # on which I can see the power contours; I want a quick transition that
    # exposes low work values with a subtle cool color; then I want the
    # map to gradually transition through "hotter" colors to red.
    from matplotlib import colors as mcolors
    colors = dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS)
    segment_data = [
        # X        color
        (0, 'darkviolet'),
        (0.005, 'purple'),
        (0.1, 'darkgreen'),
        (0.3, 'blue'),
        (0.5, 'cyan'),
        (0.7, 'yellow'),
        (1.0, 'red')
    ]
    cdict = {'red': [], 'green': [], 'blue': []}
    for x, cName in segment_data:
        rgba = mcolors.to_rgba(colors[cName])
        cdict['red'].append((x, rgba[0], rgba[0]))
        cdict['green'].append((x, rgba[1], rgba[1]))
        cdict['blue'].append((x, rgba[2], rgba[2]))
    newcmp = mcolors.LinearSegmentedColormap('WillsCmap',
                                             segmentdata=cdict,
                                             N=256)

    # exposure histogram plot
    # clone from
    #   https://matplotlib.org/gallery/images_contours_and_fields/pcolor_demo.html
    fig3, ax3 = plt.subplots()
    c = ax3.pcolor(force_grid,
                   cadence_grid,
                   work2d,
                   cmap=newcmp,
                   vmin=work2d.min(),
                   vmax=work2d.max())
    ax3.axis([
        force_bins.min(),
        force_bins.max(),
        cadence_bins.min(),
        cadence_bins.max()
    ])
    cbar = fig3.colorbar(c, ax=ax3)
    cbar.ax.set_ylabel('work, kJ')
    CS = ax3.contour(force_grid,
                     cadence_grid,
                     power_grid,
                     power_lines,
                     colors='k')
    ax3.clabel(CS, fontsize=9, inline=1)
    ax3.set_xlabel('Pedal Force, lb')
    ax3.set_ylabel('cadence, RPM')
    ax3.set_title('Pedal Exposure Histogram')
    fig3.tight_layout()
    fig3.canvas.set_window_title(FitFilePath)
    plt.show()

    def ClosePlots():
        plt.close('all')

    return ClosePlots
コード例 #31
0
        fit_id_list = []
        try:
            direct = open(com_path + "direct_table.csv", "rb")
            for fit_id in csv.reader(direct):
                fit_id_list += [fit_id[0]]
            direct.close()
        except:
            pass

        ID = fname[38:-4]  #depend on your com_path
        if ID not in fit_id_list:
            print fname + " Start parsing!!"
            time_start = times.time()

            a = Activity(fname)
            a.parse(hook_func=print_record)

            #check None value
            for data in csv_dict:
                if (csv_dict[data] < csv_dict["time"]):
                    list = eval(data)
                    list += [-1]
                    csv_dict[data] += 1
                    missing_count[data] += 1

            if time != []:
                min_time = min(time)
                for i in range(len(time)):
                    time[i] = time[i] - min_time
コード例 #32
0
def saddle_endurance_anls(FitFilePath, ConfigFile=None, OutStream=sys.stdout):

    (FilePath, FitFileName) = os.path.split(FitFilePath)

    # no config file needed

    from datetime import datetime
    from fitparse import Activity
    from activity_tools import extract_activity_signals
    import numpy as np

    required_signals = ['power', 'cadence']

    # get the signals
    activity = Activity(FitFilePath)
    signals = extract_activity_signals(activity)

    if not all(s in signals.keys() for s in required_signals):
        msg = 'required signals not in file'
        print >> OutStream, msg
        print >> OutStream, 'Signals required:'
        for s in required_signals:
            print >> OutStream, '   ' + s
        print >> OutStream, 'Signals contained:'
        for s in signals.keys():
            print >> OutStream, '   ' + s
        raise IOError(msg)

    SampleRate = 1.0
    cadence = signals['cadence']
    elapsed_time = signals['time']
    nPts = len(cadence)

    cad_fwd_min_3_8 = FwdRunningMinimum(cadence, wBeg=3, wEnd=8)
    cad_fwd_min_1_8 = FwdRunningMinimum(cadence, wBeg=1, wEnd=8)

    STANDING = 0
    SEATED = 1
    state = STANDING

    CThr = 40.0
    seated_state = np.zeros(nPts)

    for i in range(nPts):
        if state == STANDING:
            if cadence[i] >= CThr and cad_fwd_min_1_8[i] >= CThr:
                state = SEATED
        else:  # state==SEATED:
            if cadence[i] < CThr and cad_fwd_min_3_8[i] < CThr:
                state = STANDING
        seated_state[i] = state

    #
    #   Determine seated segment durations
    #
    iiUp = np.nonzero(seated_state[1:] - seated_state[0:-1] == 1)[0]
    iiDn = np.nonzero(seated_state[1:] - seated_state[0:-1] == -1)[0]
    if iiUp[-1] < iiDn[-1]:
        seg_durtns = np.zeros(len(iiDn))
        seg_starts = np.zeros(len(iiDn)).astype('int')
        seg_stops = np.zeros(len(iiDn)).astype('int')
    else:
        seg_durtns = np.zeros(len(iiDn) + 1)
        seg_starts = np.zeros(len(iiDn) + 1).astype('int')
        seg_stops = np.zeros(len(iiDn) + 1).astype('int')
    if iiDn[0] < iiUp[0]:  # begins seated
        seg_durtns[0] = iiDn[0]
        seg_starts[0] = 0
        seg_stops[0] = iiDn[0]
        if iiUp[-1] > iiDn[-1]:  #   Ends seated
            seg_durtns[1:-1] = iiDn[1:] - iiUp[0:-1]
            seg_starts[1:-1] = iiUp[0:-1]
            seg_stops[1:-1] = iiDn[1:]
            seg_durtns[-1] = nPts - iiUp[-1]
            seg_starts[-1] = iiUp[-1]
            seg_stops[-1] = nPts
        else:  #   ends standing
            seg_durtns[1:] = iiDn[1:] - iiUp
            seg_starts[1:] = iiUp
            seg_stops[1:] = iiDn[1:]
    elif iiUp[0] < iiDn[0]:  # begins standing
        if iiUp[-1] > iiDn[-1]:  #   ends seated
            seg_durtns[0:-1] = iiDn - iiUp[0:-1]
            seg_starts[0:-1] = iiUp[0:-1]
            seg_stops[0:-1] = iiDn
            seg_durtns[-1] = nPts - iiUp[-1]
            seg_starts[-1] = iiUp[-1]
            seg_stops[-1] = nPts
        else:  #   ends standing
            seg_durtns = iiDn - iiUp
            seg_starts = iiUp
            seg_stops = iiDn
    else:
        raise RuntimeError("shouldn't be able to reach this code.")

    #
    #   Formatted print of results for all segments
    #

    # overall results
    dur = nPts / SampleRate
    hh = dur // 3600
    mm = (dur % 3600) // 60
    ss = (dur % 3600) % 60
    print >> OutStream, 'total time     : %2i:%02i:%02i' % (hh, mm, ss)
    iSeat = np.nonzero(seated_state == 1)[0]
    pct = len(iSeat) / float(nPts) * 100.0
    dur = len(iSeat) / SampleRate
    hh = dur // 3600
    mm = (dur % 3600) // 60
    ss = (dur % 3600) % 60
    print >> OutStream, 'seated time    : %2i:%02i:%02i (%2i%%))' % (hh, mm,
                                                                     ss, pct)
    iStnd = np.nonzero(seated_state == 0)[0]
    pct = len(iStnd) / float(nPts) * 100.0
    dur = len(iStnd) / SampleRate
    hh = dur // 3600
    mm = (dur % 3600) // 60
    ss = (dur % 3600) % 60
    print >> OutStream, 'standing time  : %2i:%02i:%02i (%2i%%))' % (hh, mm,
                                                                     ss, pct)

    # segment results
    nSeg = len(seg_durtns)
    print >> OutStream, 'standing segments:'
    names = ['segment', 'start', 'stop', 'duration']
    fmt = "%12s" + "%10s" * 3
    print >> OutStream, fmt % tuple(names)
    for i in range(nSeg):
        Beg = elapsed_time[seg_starts[i]]
        hhBeg = Beg // 3600
        mmBeg = (Beg % 3600) // 60
        ssBeg = (Beg % 3600) % 60
        End = elapsed_time[seg_stops[i]]
        hhEnd = End // 3600
        mmEnd = (End % 3600) // 60
        ssEnd = (End % 3600) % 60
        dur = seg_durtns[i] / SampleRate
        hhDur = dur // 3600
        mmDur = (dur % 3600) // 60
        ssDur = (dur % 3600) % 60
        DurPlus = '' if hhDur == 0 else '+%ih' % (hhDur)
        fmt = '%12d' + '%4i:%02i:%02i' + '%4i:%02i:%02i' + '%7i:%02i' + '%s'
        print >> OutStream, fmt \
                % (i, hhBeg, mmBeg, ssBeg,
                      hhEnd, mmEnd, ssEnd,
                      mmDur, ssDur, DurPlus)

    #
    # best hour saddle endurance
    #
    '''
    Find the longest segments that together total one hour,
    and compute the average duration to serve as a metric for
    the ride.
    '''

    # time limit: 15 minutes less than end for short rides
    TimeLimit = min(3600.0, (nPts / SampleRate - 15 * 60))

    # list of indices for longest durations
    def GetSegment(i):
        return seg_durtns[i]

    indx = range(nSeg)
    indx.sort(reverse=True, key=GetSegment)

    # compute average and print
    print >> OutStream, 'best-hour segments:'
    names = ['segment', 'start', 'stop', 'duration']
    fmt = "%12s" + "%10s" * 3
    print >> OutStream, fmt % tuple(names)
    TotalTime = 0.0
    i = 0
    while TotalTime < TimeLimit and i < nSeg:
        Beg = elapsed_time[seg_starts[indx[i]]]
        hhBeg = Beg // 3600
        mmBeg = (Beg % 3600) // 60
        ssBeg = (Beg % 3600) % 60
        End = elapsed_time[seg_stops[indx[i]]]
        hhEnd = End // 3600
        mmEnd = (End % 3600) // 60
        ssEnd = (End % 3600) % 60
        dur = seg_durtns[indx[i]] / SampleRate
        hhDur = dur // 3600
        mmDur = (dur % 3600) // 60
        ssDur = (dur % 3600) % 60
        DurPlus = '' if hhDur == 0 else '+%ih' % (hhDur)
        fmt = '%12d' + '%4i:%02i:%02i' + '%4i:%02i:%02i' + '%7i:%02i' + '%s'
        print >> OutStream, fmt \
                % (indx[i], hhBeg, mmBeg, ssBeg,
                            hhEnd, mmEnd, ssEnd,
                            mmDur, ssDur, DurPlus)
        TotalTime += dur
        i += 1
    BestAve = TotalTime / float(i)
    hh = BestAve // 3600
    mm = (BestAve % 3600) // 60
    ss = (BestAve % 3600) % 60
    DurPlus = '' if hh == 0 else '+%ih' % (hh)
    print >> OutStream, '        BEST ONE-HOUR AVERAGE:  %7i:%02i%s' \
            % (mm, ss, DurPlus)

    ############################################################
    #                  plotting                                #
    ############################################################

    #
    #   extract lap times
    #
    from activity_tools import extract_activity_laps
    activity = Activity(FitFilePath)
    laps = extract_activity_laps(activity)
    lap_start_time = laps['start_time']  # datetime object
    lap_timestamp = laps['timestamp']
    nLaps = len(lap_start_time)
    t0 = signals['metadata']['timestamp']
    lap_start_sec = np.zeros(nLaps)  # lap start times in seconds
    for i in range(nLaps):
        tBeg = (lap_start_time[i] - t0).total_seconds()
        tEnd = (lap_timestamp[i] - t0).total_seconds()
        lap_start_sec[i] = tBeg

    # time plot
    import matplotlib.pyplot as plt
    import matplotlib.dates as md
    from matplotlib.dates import date2num, DateFormatter
    import datetime as dt
    base = dt.datetime(2014, 1, 27, 0, 0, 0)
    x = [base + dt.timedelta(seconds=t) for t in elapsed_time]
    x = date2num(x)  # Convert to matplotlib format
    x_laps  = [ base + dt.timedelta(seconds=t)   \
                for t in lap_start_sec.astype('float') ]
    x_laps = date2num(x_laps)
    fig, (ax0, ax1, ax2) = plt.subplots(nrows=3, sharex=True)
    ax0.plot_date(x, signals['power'], 'b-', linewidth=1)
    ax0.grid(True)
    ax0.set_ylabel('power, W')
    ax0.set_title('Saddle Endurance')
    ax1.plot_date(x, signals['cadence'], 'g-', linewidth=1)
    ax1.plot_date(x, cad_fwd_min_3_8, 'm-', linewidth=1)
    ax1.plot_date(x, cad_fwd_min_1_8, 'brown', linestyle='-', linewidth=1)
    ax1.grid(True)
    ax1.set_ylabel('cadence, RPM')
    ax1.legend(['cadence', 'cad_fwd_min_3_8', 'cad_fwd_min_1_8'],
               loc='upper left')
    ax2.plot_date(x, seated_state, 'r-', linewidth=3)
    ax2.xaxis.set_major_formatter(DateFormatter('%H:%M:%S'))
    ax2.grid(True)
    ax2.set_ylabel('seated')
    ax2.set_yticks([0, 1])
    ax2.set_yticklabels(('standing', 'seated'))
    for i in range(nLaps):
        ax0.axvline(x_laps[i], label=str(i + 1))
        ax1.axvline(x_laps[i], label=str(i + 1))
        ax2.axvline(x_laps[i], label=str(i + 1))
    fig.canvas.set_window_title(FitFilePath)
    fig.tight_layout()
    fig.subplots_adjust(hspace=0)  # Remove horizontal space between axes
    plt.show()

    def ClosePlots():
        plt.close('all')

    return ClosePlots
コード例 #33
0
EdgeFilePath = FilePath + r'P1 - 2019-04-22-11-40-45.fit'
ZwiftFilePath = FilePath + r'Infocrank (Zwift) 2019-04-22-10-30-09.fit'

# Delay range in seconds.
# for finding the delay in Edge relative to Zwift:
# a positive delay means Edge looks like a delayed version of Zwift.
MinDelay = 40 * 60
MaxDelay = 50 * 60

#EdgeFilePath    = r'2017-01-02-16-12-43_edge.fit'
#ZwiftFilePath   = r'2017-01-02-16-07-51_zwift.fit'

#EdgeFilePath    = r'2017-01-07-10-20-12_edge_quarq.fit'
#ZwiftFilePath   = r'2017-01-07-10-15-57_zwift_fluid2.fit'

EdgeActivity = Activity(EdgeFilePath)
ZwiftActivity = Activity(ZwiftFilePath)

EdgeSignals = extract_activity_signals(EdgeActivity)
ZwiftSignals = extract_activity_signals(ZwiftActivity)

# save signals for faster analysis
SignalMap = {'EdgeSignals': EdgeSignals, 'ZwiftSignals': ZwiftSignals}
import pickle
SignalsFile = open('signals.pkl', 'wb')
pickle.dump(SignalMap, SignalsFile)
SignalsFile.close()

from pylab import arange, interp, array, zeros, sqrt, average
#from numpy import array, zeros, arange, interp, sqrt, average
# plot heart rate and calories
コード例 #34
0
ファイル: readme_sample.py プロジェクト: j33433/FitFiles
#!/usr/bin/env python

# Sample usage of python-fitparse to parse an activity and
# print its data records.

from datetime import datetime

from fitparse import Activity

#fitfilepath = r'S:\will\documents\bike\python-fitparse-master\tests\data\\' \
#            + r'sample-activity.fit'
fitfilepath = r'2017-01-02-16-12-43_edge.fit'

activity = Activity(fitfilepath)
activity.parse()

# Records of type 'record' (I know, confusing) are the entries in an
# activity file that represent actual data points in your workout.
# types: [ 'record', 'lap', 'event', 'session', 'activity', ... ]
records = activity.get_records_by_type('lap')
current_record_number = 0

time_signal = []
heart_rate  = []
FirstIter   = True

for record in records:

    # Print record number
    current_record_number += 1
    print (" Record #%d " % current_record_number).center(40, '-')
コード例 #35
0
def endurance_summary(FitFilePath, ConfigFile=None, OutStream=sys.stdout):

    (FilePath, FitFileName) = os.path.split(FitFilePath)

    if ConfigFile is None:
        ConfigFile = FindConfigFile('', FilePath)
    if (ConfigFile is None) or (not os.path.exists(ConfigFile)):
        raise IOError('Configuration file not specified or found')

    #
    #   Parse the configuration file
    #
    from ConfigParser import ConfigParser
    config      = ConfigParser()
    config.read(ConfigFile)
    print >> OutStream, 'reading config file ' + ConfigFile
    ThresholdPower  = config.getfloat( 'power', 'ThresholdPower' )
    ThresholdHR     = config.getfloat( 'power', 'ThresholdHR'    )
    print >> OutStream, 'ThresholdPower: ', ThresholdPower
    print >> OutStream, 'ThresholdHR   : ', ThresholdHR

    # power zones from "Cyclist's Training Bible", 5th ed., by Joe Friel, p51
    FTP = ThresholdPower
    pZones  = { 1   : [    0    ,   0.55*FTP ],
                2   : [ 0.55*FTP,   0.75*FTP ],
                3   : [ 0.75*FTP,   0.90*FTP ],
                4   : [ 0.90*FTP,   1.05*FTP ],
                5   : [ 1.05*FTP,   1.20*FTP ],
                6   : [ 1.20*FTP,   1.50*FTP ],
                7   : [ 1.50*FTP,   2.50*FTP ]}

    # heart-rate zones from "Cyclist's Training Bible" 5th ed. by Joe Friel, p50
    FTHR = ThresholdHR
    hZones  = { 1   : [     0    ,   0.82*FTHR ],  # 1
                2   : [ 0.82*FTHR,   0.89*FTHR ],  # 2
                3   : [ 0.89*FTHR,   0.94*FTHR ],  # 3
                4   : [ 0.94*FTHR,   1.00*FTHR ],  # 4
                5   : [ 1.00*FTHR,   1.03*FTHR ],  # 5a
                6   : [ 1.03*FTHR,   1.06*FTHR ],  # 5b
                7   : [ 1.07*FTHR,   1.15*FTHR ]}  # 5c

    # get zone bounds for plotting
    p_zone_bounds   = [ pZones[1][0],
                        pZones[2][0],
                        pZones[3][0],
                        pZones[4][0],
                        pZones[5][0],
                        pZones[6][0],
                        pZones[7][0],
                        pZones[7][1] ]

    h_zone_bounds   = [     0.4*FTHR,   # better plotting
                        hZones[2][0],
                        hZones[3][0],
                        hZones[4][0],
                        hZones[5][0],
                        hZones[6][0],
                        hZones[7][0],
                        hZones[7][1] ]


    from datetime import datetime
    from fitparse import Activity
    from activity_tools import extract_activity_signals

    required_signals    = [ 'power',
                            'heart_rate' ]

    # get the signals
    activity = Activity(FitFilePath)
    signals     = extract_activity_signals(activity, resample='existing')

    if not all( s in signals.keys() for s in required_signals ):
        msg = 'required signals not in file'
        print >> OutStream, msg
        print >> OutStream, 'Signals required:'
        for s in required_signals:
            print >> OutStream, '   ' + s
        print >> OutStream, 'Signals contained:'
        for s in signals.keys():
            print >> OutStream, '   ' + s
        raise IOError(msg)

    '''
    ####################
    # Get Records of type 'lap'
    # types: [ 'record', 'lap', 'event', 'session', 'activity', ... ]
    records = activity.get_records_by_type('lap')
    current_record_number = 0

    elapsed_time    = []
    timer_time      = []
    avg_heart_rate  = []
    avg_power       = []
    avg_cadence     = []
    max_heart_rate  = []
    balance         = []
    lap_timestamp   = []
    lap_start_time  = []

    FirstIter   = True

    for record in records:

        # Print record number
        current_record_number += 1
        #print (" Record #%d " % current_record_number).center(40, '-')

        # Get the list of valid fields on this record
        valid_field_names = record.get_valid_field_names()

        for field_name in valid_field_names:
            # Get the data and units for the field
            field_data = record.get_data(field_name)
            field_units = record.get_units(field_name)

            ## Print what we've got!
            #if field_units:
            #    print >> OutStream, " * %s: %s %s" % (field_name, field_data, field_units)
            #else:
            #    print >> OutStream, " * %s: %s" % (field_name, field_data)

            if 'timestamp' in field_name:
                lap_timestamp.append( field_data )

            if 'start_time' in field_name:
                lap_start_time.append( field_data )

            if 'total_elapsed_time' in field_name:
                elapsed_time.append( field_data )

            if 'total_timer_time' in field_name:
                timer_time.append( field_data )

            if 'avg_power' in field_name:
                avg_power.append( field_data )

            # avg_heart_rate is in a lap record
            if 'avg_heart_rate' in field_name:
                avg_heart_rate.append(field_data)

            if 'max_heart_rate' in field_name:
                max_heart_rate.append(field_data)

            if 'avg_cadence' in field_name:
                avg_cadence.append(field_data)

            if 'left_right_balance' in field_name:
                balance.append(field_data)

        #print
    ####################
    '''

    #
    #   extract lap results
    #
    from fitparse import Activity
    from activity_tools import extract_activity_laps
    import numpy as np
    activity    = Activity(FitFilePath)
    laps        = extract_activity_laps(activity)
    avg_power       = laps['power']
    time            = laps['time']
    cadence         = laps['cadence']
    avg_heart_rate  = laps['avg_hr']
    max_heart_rate  = laps['max_hr']
    balance         = laps['balance']
    lap_start_time  = laps['start_time']
    lap_timestamp   = laps['timestamp' ]
    timer_time      = laps['total_timer_time']
    elapsed_time    = laps['total_elapsed_time']


    IntervalThreshold = 0.0     # get all laps (0.72*FTP)
    from numpy import nonzero, array, arange, zeros, average, logical_and

    # resample power to constant-increment (1 Hz) with zeros at missing samples
    time_idx                = signals['time'].astype('int')
    power_vi                = signals['power']
    heart_rate_vi           = signals['heart_rate']
    nScans                  = time_idx[-1]+1
    time_ci                 = arange(nScans)
    power                   = zeros(nScans)
    power[time_idx]         = power_vi
    heart_rate_ci           = zeros(nScans)
    heart_rate_ci[time_idx] = heart_rate_vi

    t0 = signals['metadata']['timestamp']
    print >> OutStream, 'signal timestamp: ', t0.time()

    # plot lap results as continuous time signals
    lap_avg_hr_c        = zeros(nScans)
    lap_avg_power_c     = zeros(nScans)
    lap_norm_power_c    = zeros(nScans)

    # compute the 30-second, moving-average power signal.
    p30 = BackwardMovingAverage( power )

    #
    # compute lap metrics
    #
    print >> OutStream, 'lap results:'
    nLaps   = len(elapsed_time)
    vi_time_vector  = signals['time']

    lap_avg_power   = zeros(nLaps)
    lap_norm_power  = zeros(nLaps)
    lap_avg_hr      = zeros(nLaps)
    lap_if          = zeros(nLaps)      # intensity factor
    lap_start_sec   = zeros(nLaps)      # lap start times in seconds

    #time    = array(elapsed_time)
    #cadence = array(avg_cadence)
    #avg_hr  = array(avg_heart_rate)
    #max_hr  = array(max_heart_rate)
    #balance = array(balance)
    names1  = [    '', '  lap', '  avg', ' norm', 'avg',  'max',    '' ]
    names2  = [ 'lap', ' time', 'power', 'power', ' HR',  ' HR', ' IF' ]
    fmt     = "%8s"+"%10s"+"%8s"*5
    print >> OutStream, fmt % tuple(names1)
    print >> OutStream, fmt % tuple(names2)

    for i in range(nLaps):
        # count samples in this lap
        tBeg = (lap_start_time[i] - t0).total_seconds()
        tEnd = (lap_timestamp[i]  - t0).total_seconds()
        ii = nonzero( logical_and( time_idx >= tBeg,  \
                                   time_idx <  tEnd)  )[0]
        nPts = ii.size
        lap_start_sec[i]    = tBeg
        lap_avg_hr[i]       = average(heart_rate_vi[ii])
        lap_avg_power[i]    = average(power[time_idx[ii]])
        lap_norm_power[i]   = average( p30[time_idx[ii]]**4 )**(0.25)
        lap_if[i]           = lap_norm_power[i] / FTP

        # duration from lap metrics
        dur = (lap_timestamp[i] - lap_start_time[i]).total_seconds()
        mm = timer_time[i] // 60
        ss = timer_time[i]  % 60
        fmt = '%8d'+'%7i:%02i'+'%8i'*4 + '%8.2f'
        print >> OutStream, fmt \
                % ( i,
                    mm, ss,
                    avg_power[i],
                    lap_norm_power[i],
                    avg_heart_rate[i],
                    max_heart_rate[i],
                    lap_if[i]           )

        # plot lap results as continuous time signals
        lap_avg_hr_c    [time_idx[ii]]  = lap_avg_hr[i]
        lap_avg_power_c [time_idx[ii]]  = lap_avg_power[i]
        lap_norm_power_c[time_idx[ii]]  = lap_norm_power[i]

    #
    # ride-level results
    #
    print >> OutStream, 'ride-level results:'
    names1  = [    '', 'moving', '  avg', ' norm', 'avg',    '',  'Pw:' ]
    names2  = [ 'seg', '  time', 'power', 'power', ' HR', ' IF',  ' HR' ]
    fmt     = "%8s"+"%10s"+"%8s"*5
    print >> OutStream, fmt % tuple(names1)
    print >> OutStream, fmt % tuple(names2)

    # whole ride
    tBeg = (lap_start_time[0] - t0).total_seconds()
    tEnd = (lap_timestamp[-1] - t0).total_seconds()
    ii = nonzero( logical_and( time_idx >= tBeg,  \
                               time_idx <  tEnd)  )[0]
    nPts = ii.size
    dur = nPts  # sample rate 1 Hz
    hh  = dur // 3600
    mm  = (dur % 3600) // 60
    ss  = (dur % 3600) % 60
    all_avg_hr      = average(heart_rate_vi[ii])
    all_avg_power   = average(power_vi[ii])
    all_norm_power  = average( p30[time_idx[ii]]**4 )**(0.25)
    all_max_hr      = max(heart_rate_vi[ii])
    all_if          = all_norm_power / FTP
    # aerobic decoupling
    iiH1            = ii[0:nPts/2]
    h1_norm_power   = average( p30[time_idx[iiH1]]**4 )**(0.25)
    h1_avg_hr       = average(heart_rate_vi[iiH1])
    h1ef            = h1_norm_power / h1_avg_hr
    iiH2            = ii[nPts/2:]
    h2_norm_power   = average( p30[time_idx[iiH2]]**4 )**(0.25)
    h2_avg_hr       = average(heart_rate_vi[iiH2])
    h2ef            = h2_norm_power / h2_avg_hr
    all_pw_hr       = (h1ef-h2ef)/(h1ef)*100.0
    fmt = '%8s'+'%4i:%02i:%02i'+'%8i'*3 + '%8.2f' + '%8.1f'
    print >> OutStream, fmt \
            % ( 'all',
                hh, mm, ss,
                all_avg_power,
                all_norm_power,
                all_avg_hr,
                all_if,
                all_pw_hr          )

    # without end laps
    tBeg = (lap_start_time[1] - t0).total_seconds()
    tEnd = (lap_timestamp[-2] - t0).total_seconds()
    ii = nonzero( logical_and( time_idx >= tBeg,  \
                               time_idx <  tEnd)  )[0]
    nPts = ii.size
    dur = nPts  # sample rate 1 Hz
    hh  = dur // 3600
    mm  = (dur % 3600) // 60
    ss  = (dur % 3600) % 60
    mid_avg_hr      = average(heart_rate_vi[ii])
    mid_avg_power   = average(power_vi[ii])
    mid_norm_power  = average( p30[time_idx[ii]]**4 )**(0.25)
    mid_max_hr      = max(heart_rate_vi[ii])
    mid_if          = mid_norm_power / FTP
    # aerobic decoupling
    iiH1            = ii[0:nPts/2]
    h1_norm_power   = average( p30[time_idx[iiH1]]**4 )**(0.25)
    h1_avg_hr       = average(heart_rate_vi[iiH1])
    h1ef            = h1_norm_power / h1_avg_hr
    iiH2            = ii[nPts/2:]
    h2_norm_power   = average( p30[time_idx[iiH2]]**4 )**(0.25)
    h2_avg_hr       = average(heart_rate_vi[iiH2])
    h2ef            = h2_norm_power / h2_avg_hr
    mid_pw_hr       = (h1ef-h2ef)/(h1ef)*100.0
    fmt = '%5i-%02i'+'%4i:%02i:%02i'+'%8i'*3 + '%8.2f' + '%8.1f'
    print >> OutStream, fmt \
            % ( 1, nLaps-2,
                hh, mm, ss,
                mid_avg_power,
                mid_norm_power,
                mid_avg_hr,
                mid_if,
                mid_pw_hr          )

    print
    print

    #
    # time plot
    #

    import matplotlib.pyplot as plt
    import matplotlib.dates as md
    from matplotlib.dates import date2num, DateFormatter
    import datetime as dt
    base = dt.datetime(2014, 1, 1, 0, 0, 0)
    x = [base + dt.timedelta(seconds=t) for t in time_ci.astype('float')]
    x = date2num(x) # Convert to matplotlib format
    fig1, (ax0, ax1) = plt.subplots(nrows=2, sharex=True)
    ax0.plot_date( x, heart_rate_ci, 'r-', linewidth=1 );
    ax0.plot_date( x, lap_avg_hr_c,  'r-', linewidth=3 );
    ax0.set_yticks( h_zone_bounds, minor=False)
    x_laps  = [ base + dt.timedelta(seconds=t)   \
                for t in lap_start_sec.astype('float') ]
    x_laps  = date2num(x_laps)
    ax0.grid(True)
    ax0.set_ylabel('heart rate, BPM')
    ax1.plot_date( x, power,            'k-', linewidth=1 );
    ax1.plot_date( x, p30,              'm-', linewidth=1);
    ax1.plot_date( x, lap_avg_power_c,  'b-', linewidth=3);
    ax1.plot_date( x, lap_norm_power_c, 'g-', linewidth=3);
    ax1.xaxis.set_major_formatter(DateFormatter('%H:%M:%S'))
    ax1.set_yticks( p_zone_bounds, minor=False)
    ax1.grid(True)
    ax1.set_ylabel('power, watts')
    ax1.legend(['power', 'p30', 'lap_avg_power', 'lap_norm_power'],
                loc='upper left');
    for i in range(nLaps):
        ax0.axvline( x_laps[i], label=str(i+1) )
        ax1.axvline( x_laps[i], label=str(i+1) )
    fig1.autofmt_xdate()
    fig1.suptitle('Endurance Power Results', fontsize=20)
    fig1.tight_layout()
    fig1.subplots_adjust(hspace=0)   # Remove horizontal space between axes
    fig1.canvas.set_window_title(FitFilePath)
    plt.show()

    def ClosePlots():
        plt.close('all')

    return ClosePlots
コード例 #36
0
def channel_inspect_anls(FitFilePath, ConfigFile=None, OutStream=sys.stdout,
                         ParentWin=None):

    (FilePath, FitFileName) = os.path.split(FitFilePath)

    if ConfigFile is None:
        ConfigFile = FindConfigFile('', FilePath)
    if (ConfigFile is None) or (not os.path.exists(ConfigFile)):
        raise IOError('Configuration file not specified or found')
    #
    #   Parse the configuration file
    #
    from ConfigParser import ConfigParser
    config      = ConfigParser()
    config.read(ConfigFile)
    print >> OutStream, 'reading config file ' + ConfigFile
    if config.has_section('units'):
        # convert list of tuples to dictionary
        UserUnits = dict( config.items('units') )
    else:
        UserUnits = None

    # get the signals
    from datetime import datetime
    from fitparse import Activity
    from activity_tools import extract_activity_signals, UnitHandler
    activity    = Activity(FitFilePath)
    signals     = extract_activity_signals(activity, resample='existing')

    # convert units
    if UserUnits is not None:
        unithandler = UnitHandler(UserUnits)
        signals = unithandler.ConvertSignalUnits(signals)

    ChannelList = signals.keys()
    ChannelList.remove('time')
    ChannelList.remove('metadata')

    print >> OutStream, 'Signals contained:'
    for s in ChannelList:
        print >> OutStream, '   ' + s

    if ParentWin is None:
        app = wx.App()

    dlg = wx.MultiChoiceDialog( ParentWin,
                               "Pick channels\nto plot",
                               "channel inspector: " + FitFileName,
                               ChannelList)

    if (dlg.ShowModal() == wx.ID_OK):
        selections = dlg.GetSelections()
        ChannelNames = [ ChannelList[x] for x in selections ]
        print >> OutStream, "Plotting: %s" % (ChannelNames)

    dlg.Destroy()

    #
    #   extract lap results
    #
    from activity_tools import extract_activity_laps
    activity    = Activity(FitFilePath)
    laps        = extract_activity_laps(activity)
    lap_start_time  = laps['start_time']    # datetime object
    lap_timestamp   = laps['timestamp' ]
    nLaps           = len(lap_start_time)
    t0 = signals['metadata']['timestamp']
    lap_start_sec   = np.zeros(nLaps)          # lap start times in seconds
    for i in range(nLaps):
        tBeg = (lap_start_time[i] - t0).total_seconds()
        tEnd = (lap_timestamp[i]  - t0).total_seconds()
        lap_start_sec[i]    = tBeg

    #
    # time plot
    #
    nPlots  = len(ChannelNames)

    PlotColors  = { 'power'         : 'm'       ,
                    'heart_rate'    : 'r'       ,
                    'cadence'       : 'g'       ,
                    'speed'         : 'b'       ,
                    'temperature'   : 'brown'   }


    import matplotlib.pyplot as plt
    import matplotlib.dates as md
    from matplotlib.dates import date2num, DateFormatter
    import datetime as dt
    base = dt.datetime(2014, 1, 1, 0, 0, 0)
    x = [base + dt.timedelta(seconds=t) for t in signals['time'].astype('float')]
    x = date2num(x) # Convert to matplotlib format
    #fig1, (ax0, ax1) = plt.subplots(nrows=2, sharex=True)
    x_laps  = [ base + dt.timedelta(seconds=t)   \
                for t in lap_start_sec.astype('float') ]
    x_laps  = date2num(x_laps)
    axislist    = []
    fig = plt.figure()
    for i, channel in zip( range(nPlots), ChannelNames ):
        if PlotColors.has_key(channel):
            pcolor  = PlotColors[channel]
        else:
            pcolor  = 'k-'
        if i > 0:
            ax  = plt.subplot( nPlots, 1, i+1, sharex=axislist[0] )
        else:
            ax  = plt.subplot( nPlots, 1, i+1 )
        ax.plot_date( x, signals[channel], pcolor, linewidth=1, linestyle='-' );
        for j in range(nLaps):
            ax.axvline( x_laps[j], label=str(j+1) )
        ax.grid(True)
        if signals['metadata']['units'].has_key(channel):
            YLabel  = channel + '\n' \
                    + signals['metadata']['units'][channel]
        else:
            YLabel  = channel + '\n' + 'none'
        ax.set_ylabel(YLabel)
        ax.xaxis.set_major_formatter(DateFormatter('%H:%M:%S'))
        ax.grid(True)
        axislist.append(ax)
    fig.autofmt_xdate()
    fig.suptitle(FitFileName, fontsize=20)
    fig.tight_layout()
    fig.canvas.set_window_title(FitFilePath)
    fig.subplots_adjust(hspace=0)   # Remove horizontal space between axes
    plt.show()


    def ClosePlots():
        plt.close('all')

    return ClosePlots