Source for file PolarPdd.class.php

Documentation is available at PolarPdd.class.php

  1. <?php
  2. /**
  3.  * Class to parse Polar PDD file and insert / update data to database
  4.  *
  5.  * PDD file contains data related to 1 day (including some exercise data).
  6.  * This data comes from Polar Software.
  7.  *
  8.  * @author    Jean-Philippe Brunon <jp75018@free.fr>
  9.  * @copyright    2007-2009 Jean-Philippe Brunon
  10.  * @license    http://www.opensource.org/licenses/gpl-license.php GPL
  11.  * @package    php-endurance
  12.  * @version    $Id: PolarPdd.class.php 29 2009-03-06 11:47:52Z jp75018 $
  13.  */
  14.  
  15. /**
  16.  * Class common to all Polar files
  17.  */
  18. require_once ('PolarFile.class.php');
  19.  
  20. /**
  21.  * Class to parse Polar PDD file and insert / update data to database
  22.  *
  23.  * PDD file contains data related to 1 day, including some exercise data.
  24.  *
  25.  * The following fields are handled when present in PDD file :
  26.  *
  27.  * Day info :
  28.  * - date : Date of day (format : YYYYMMDD)
  29.  * - resting_hr : Rest heart rate (bpm)
  30.  * - weight : Weight in Kg * 10
  31.  * - sleep_minute : Sleep duration in minutes
  32.  * - sleep_pattern : Quality of sleep (list of values)
  33.  * - temperature : Temperature (in Celcius * 10)
  34.  * - weather : Weather (list of values)
  35.  * - description : Note for the day
  36.  *
  37.  * Exercise info (for each exercise) :
  38.  * - sport_id : Sport ID
  39.  * - title : Exercise title
  40.  * - start_time : Start time (format : HH:MM:SS)
  41.  * - no_report : No report flag (list of values)
  42.  * - total_time : Duration in seconds * 10
  43.  * - rec_distance : Recorded distance in meters * 10
  44.  * - real_distance : Real distance in meters * 10
  45.  * - feeling : Feeling (list of values)
  46.  * - recovery : Recovery (list of values)
  47.  * - ranking : Ranking from 1 to 5 (0 if no ranking)
  48.  * - running_index : Polar running index
  49.  * - footpod_cal : Foot pod calibration (coeff around 1000)
  50.  * - avg_hr : Average heart rate (bpm)
  51.  * - max_hr : Maximum heart rate (bpm)
  52.  * - avg_speed : Average speed in km/h * 10
  53.  * - max_speed : Maximum speed in km/h * 10
  54.  * - avg_cadence : Average cadence in cycles / minute
  55.  * - max_cadence : Maximum cadence in cycles / minute
  56.  * - avg_altitude : Average altitude in meters
  57.  * - max_altitude : Maximum altitude in meters
  58.  * - ascend : Ascend in meters
  59.  * - energy : Energy consumption (Polar) in Kcal
  60.  * - beat_sum : Number of heart beats
  61.  * - description : Note for exercise
  62.  * - hrm_file : HRM file name (parse only)
  63.  */
  64. class PolarPdd extends PolarFile
  65. {
  66. /**
  67.  * List of sleep patterns values (from 0 to 4)
  68.  * @var array 
  69.  */
  70.   var    $sleepPattern = array (
  71.     'excellent''normal''disturbed''insufficient''insomnia');
  72.  
  73. /**
  74.  * List of weather values (from 1 to 5)
  75.  * @var array 
  76.  */
  77.   var    $weather = array (
  78.     'sunny''fewclouds''cloudy''rainy''snowy');
  79.  
  80. /**
  81.  * List of "no report" values (from 0 to 3)
  82.  * @var array 
  83.  */
  84.   var    $inReports = array (
  85.     'all''count''report''none');
  86.  
  87. /**
  88.  * List of feeling values (from 0 to 5)
  89.  * @var array 
  90.  */
  91.   var    $feeling = array (
  92.     'excellent''good''average''bad''very_bad''hurt');
  93.  
  94. /**
  95.  * List of recovery values (from 0 to 4)
  96.  * @var array 
  97.  */
  98.   var    $recovery = array (
  99.     'fully''recovered''normal''tired''exhausted');
  100.  
  101. /**
  102.  * Class constructor
  103.  *
  104.  * @param    string    $fileName Polar PDD file name (full path name)
  105.  * @param    integer    $userId User ID (as in database)
  106.  * @return    void 
  107.  */
  108.   function PolarPdd ($fileName$userId)
  109.   {
  110.     parent::PolarFile($fileName$userId);
  111.   }
  112.  
  113. /**
  114.  * Insert day information into database
  115.  *
  116.  * SQL tables :
  117.  * - plw_day_info (insert / update)
  118.  * - plw_exercise (insert / update)
  119.  *
  120.  * @param    array    $day Structure for day information + some exercise data:
  121.  *             - 'dayinfo' : Day fields
  122.  *             - 'exerciseinfo' : Array of exercise fields, indexed by
  123.  *             exercise rank
  124.  * @return    void 
  125.  */
  126.   function db_insert($day)
  127.   {
  128.     $info $day['dayinfo'];
  129.     $exes $day['exerciseinfo'];
  130.  
  131.     if (is_array($info))
  132.     return}
  133.     if (count($info))
  134.     return}
  135.  
  136.   // 1. Insert / update day part
  137.     $request sprintf("INSERT INTO plw_day_info(user_id, day, cre_date, mod_date, rest_hr, weight, sleep_minute, sleep_pattern, temperature, weather, description) VALUES(%d, %s, NOW(), NOW(), %s, %s, %s, %s, %d, %s, %s) ON DUPLICATE KEY UPDATE cre_date=cre_date, mod_date=NOW(), rest_hr=%s, weight=%s, sleep_minute=%s, sleep_pattern=%s, temperature=%d, weather=%s, description=%s",
  138.       $this->userId$info['date'],
  139.       $info['resting_hr'$info['resting_hr''NULL',
  140.       $info['weight'$info['weight''NULL',
  141.       $info['sleep_minute'$info['sleep_minute''NULL',
  142.       $info['sleep_pattern'> -?
  143.     '\'' $this->sleepPattern[$info['sleep_pattern']] '\'' 'NULL',
  144.       $info['temperature'],
  145.       $info['weather'> -?
  146.     '\'' $this->weather[$info['weather']] '\'' 'NULL',
  147.       $info['description'!= '' ?
  148.     '\'' mysql_escape_string($info['description']'\'' 'NULL',
  149.       $info['resting_hr'$info['resting_hr''NULL',
  150.       $info['weight'$info['weight''NULL',
  151.       $info['sleep_minute'$info['sleep_minute''NULL',
  152.       $info['sleep_pattern'> -?
  153.     '\'' $this->sleepPattern[$info['sleep_pattern']] '\'' 'NULL',
  154.       $info['temperature'],
  155.       $info['weather'> -?
  156.     '\'' $this->weather[$info['weather']] '\'' 'NULL',
  157.       $info['description'!= '' ?
  158.     '\'' mysql_escape_string($info['description']'\'' 'NULL'
  159.     );
  160.     mysql_query($request);
  161.  
  162.   // 2. Insert / update exercise parts
  163.   // Note : rec_distance, avg/max hr, avg/max speed, avg/max acdence,
  164.   //        avg/max altitude, ascend, beat_sum not updated
  165.   //        (better from HRM file)
  166.     if (is_array($exes))
  167.     return}
  168.     for ($i 1$i <= count($exes)$i++)
  169.     {
  170.     // Parse note to extract contest distance or interval training expression
  171.       $real_dist $exes[$i]['real_distance'];
  172.       $note $exes[$i]['description'];
  173.       $exercise_type 'training';
  174.       $exact_dist 0;
  175.       $int_train null;
  176.       $options array();
  177.       if ($note)
  178.       {
  179.     $note $this->_parse_note($note,
  180.       &$exercise_type&$exact_dist&$int_train&$options);
  181.       // Check parsed distance is close enough to recorded distance (15%)
  182.     if (($real_dist 0&& ($exercise_type == 'contest'))
  183.     {
  184.       if (abs($exact_dist $real_dist($exact_dist $real_dist)0.075)
  185.       $exact_dist 0}
  186.     }
  187.       }
  188.       $real_dist ($exact_dist 0$exact_dist $real_dist;
  189.  
  190.     // Note : elapsed not updated (1/10 second precision in HRM file)
  191.     // If no HRM file => Insert with 'online' status (manual data case)
  192.     //   In this case => Take rank = Polar rank + 100 to avoid collisions
  193.       if ($exes[$i]['hrm_file'])
  194.       {
  195.     $exe_status 'empty';
  196.     $exe_rank = (int)substr($exes[$i]['hrm_file'],6,2);
  197.       }
  198.       else
  199.       {
  200.     $exe_status 'online';
  201.     $exe_rank $i 100;
  202.       }
  203.       $request sprintf("INSERT INTO plw_exercise(user_id, day, rank, status, cre_date, mod_date, sport_id, title, start_time, in_reports, elapsed, rec_distance, real_distance, feeling, recovery, ranking, running_index, footpod_cal, avg_hr, max_hr, avg_speed, max_speed, avg_cadence, max_cadence, avg_altitude, max_altitude, ascend, energy, beat_sum, exercise_type, description, shoe, shoe_ini_dist, footpod_id, footpod_batt) VALUES(%d, %s, %d, '%s', NOW(), NOW(), %d, %s, %s, %s, %d, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, '%s', %s, %s, %s, %s, %s) ON DUPLICATE KEY UPDATE status=IF(status='empty','empty','online'), cre_date=cre_date, mod_date=NOW(), sport_id=%d, title=%s, start_time=%s, in_reports=%s, real_distance=%d, feeling=%s, recovery=%s, ranking=%d, running_index=%d, footpod_cal=%d, energy=%d, exercise_type='%s', description=%s, shoe=%s, shoe_ini_dist=%s, footpod_id=%s, footpod_batt=%s",
  204.           $this->userId$info['date']$exe_rank$exe_status,
  205.     $exes[$i]['sport_id'],
  206.     $exes[$i]['title'!= '' ?
  207.       '\'' mysql_escape_string($exes[$i]['title']'\'' 'NULL',
  208.     $exes[$i]['start_time'],
  209.     $exes[$i]['no_report'> -?
  210.       '\'' $this->inReports[$exes[$i]['no_report']] '\'' 'NULL',
  211.     $exes[$i]['total_time'],
  212.     $exes[$i]['rec_distance'$exes[$i]['rec_distance''NULL',
  213.     $real_dist $real_dist 'NULL',
  214.     $exes[$i]['feeling'> -?
  215.       '\'' $this->feeling[$exes[$i]['feeling']] '\'' 'NULL',
  216.     $exes[$i]['recovery'> -?
  217.       '\'' $this->recovery[$exes[$i]['recovery']] '\'' 'NULL',
  218.     $exes[$i]['ranking'$exes[$i]['ranking''NULL',
  219.     $exes[$i]['running_index'$exes[$i]['running_index''NULL',
  220.     $exes[$i]['footpod_cal'$exes[$i]['footpod_cal''NULL',
  221.     $exes[$i]['avg_hr'$exes[$i]['avg_hr''NULL',
  222.     $exes[$i]['max_hr'$exes[$i]['max_hr''NULL',
  223.     $exes[$i]['avg_speed'$exes[$i]['avg_speed''NULL',
  224.     $exes[$i]['max_speed'$exes[$i]['max_speed''NULL',
  225.     $exes[$i]['avg_cadence'$exes[$i]['avg_cadence''NULL',
  226.     $exes[$i]['max_cadence'$exes[$i]['max_cadence''NULL',
  227.     (($exes[$i]['avg_altitude'!= 0|| ($exes[$i]['ascend'0)) ?
  228.       $exes[$i]['avg_altitude''NULL',
  229.     (($exes[$i]['avg_altitude'!= 0|| ($exes[$i]['ascend'0)) ?
  230.       $exes[$i]['max_altitude''NULL',
  231.     (($exes[$i]['avg_altitude'!= 0|| ($exes[$i]['ascend'0)) ?
  232.       $exes[$i]['ascend''NULL',
  233.     $exes[$i]['energy'$exes[$i]['energy''NULL',
  234.     $exes[$i]['beat_sum'$exes[$i]['beat_sum''NULL',
  235.     $exercise_type,
  236.     $note != '' ?  '\'' mysql_escape_string($note'\'' 'NULL',
  237.     $options['shoe'!= '' ?
  238.       '\'' mysql_escape_string($options['shoe']'\'' :'NULL',
  239.     isset($options['shoe_ini_dist']$options['shoe_ini_dist''NULL',
  240.     isset($options['footpod_id']$options['footpod_id''NULL',
  241.     isset($options['footpod_batt']$options['footpod_batt''NULL',
  242.     $exes[$i]['sport_id'],
  243.     $exes[$i]['title'!= '' ?
  244.       '\'' mysql_escape_string($exes[$i]['title']'\'' 'NULL',
  245.     $exes[$i]['start_time'],
  246.     $exes[$i]['no_report'> -?
  247.       '\'' $this->inReports[$exes[$i]['no_report']] '\'' 'NULL',
  248.     $real_dist $real_dist 'NULL',
  249.     $exes[$i]['feeling'> -?
  250.       '\'' $this->feeling[$exes[$i]['feeling']] '\'' 'NULL',
  251.     $exes[$i]['recovery'> -?
  252.       '\'' $this->recovery[$exes[$i]['recovery']] '\'' 'NULL',
  253.     $exes[$i]['ranking'$exes[$i]['ranking''NULL',
  254.     $exes[$i]['running_index'$exes[$i]['running_index''NULL',
  255.     $exes[$i]['footpod_cal'$exes[$i]['footpod_cal''NULL',
  256.     $exes[$i]['energy'$exes[$i]['energy''NULL',
  257.     $exercise_type,
  258.     $note != '' '\'' mysql_escape_string($note'\'' :'NULL',
  259.     $options['shoe'!= '' ?
  260.       '\'' mysql_escape_string($options['shoe']'\'' :'NULL',
  261.     isset($options['shoe_ini_dist']$options['shoe_ini_dist''NULL',
  262.     isset($options['footpod_id']$options['footpod_id''NULL',
  263.     isset($options['footpod_batt']$options['footpod_batt''NULL'
  264.       );
  265.       mysql_query($request);
  266.     // Get exercise ID to work with interval training
  267.       $exercise_id mysql_insert_id();
  268.  
  269.     // Insert interval training elements, delete first
  270.       $request sprintf("DELETE FROM plw_int_training WHERE exercise_id=%d",
  271.     $exercise_id);
  272.       mysql_query($request);
  273.       if (is_array($int_train))
  274.       {
  275.     for ($int 0$int count($int_train)$int++)
  276.     {
  277.       $train $int_train[$int];
  278.       $request sprintf("INSERT INTO plw_int_training(exercise_id, rank, repeat_nb, work_distance, work_duration, reco_distance, reco_duration) VALUES (%d, %d, %d, %s, %s, %s, %s)",
  279.         $exercise_id$int$train['repeat_nb'],
  280.         $train['work_distance'$train['work_distance''NULL',
  281.         $train['work_duration'$train['work_duration''NULL',
  282.         $train['reco_distance'$train['reco_distance''NULL',
  283.         $train['reco_duration'$train['reco_duration''NULL'
  284.       );
  285.       mysql_query($request);
  286.     }
  287.       }
  288.     }
  289.   }
  290.  
  291. /**
  292.  * Parse Polar PDD file (day information, including some exercise data)
  293.  *
  294.  * Note : Both <dayinfo> and <exerciseinfo> blocks are parsed.
  295.  *
  296.  * @return    array    Structure for day and exercises :
  297.  *             - 'dayinfo' : Day fields
  298.  *             - 'exerciseinfo' : Exercise fields, indexed by rank
  299.  */
  300.   function parse()
  301.   {
  302.     $buffer @file($this->fileName);
  303.     if (is_array($buffer))
  304.     return null}
  305.  
  306.     $line_no 0;
  307.     while ($line_no count($buffer))
  308.     {
  309.       $begin_block $line_no 1;
  310.       $block_name $this->_parse_block_get(&$buffer&$line_no);
  311.       $lg_block $line_no $begin_block 1;
  312.       if (trim($buffer[$end_block]))
  313.       $end_block--}
  314.       if (substr($block_name012== 'exerciseinfo')
  315.       {
  316.     $exercise_nb = (int)substr($block_name12);
  317.     $day['exerciseinfo'][$exercise_nb=
  318.     $this->_parse_block_exerciseinfo_get(&$buffer$begin_block$lg_block);
  319.       }
  320.       else
  321.       {
  322.     $parse_func '_parse_block_' $block_name '_get';
  323.     if (method_exists($this$parse_func))
  324.     {
  325.       $day[$block_name=
  326.         $this->$parse_func(&$buffer$begin_block$lg_block);
  327.     }
  328.       }
  329.     }
  330.  
  331.   // Check day match filename !
  332.     $file_day substr(basename($this->fileName'.hrm')08);
  333.     $data_day $day['dayinfo']['date'];
  334.     if ($file_day != $data_day)
  335.     $day null}
  336.  
  337.     return $day;
  338.   }
  339.  
  340. /**
  341.  * Parse <dayinfo> block in PDD file
  342.  *
  343.  * @param    string    $buffer Buffer with PDD file content
  344.  * @param    integer    $start Line number of block in buffer
  345.  * @param    integer    $nb Number of lines in block
  346.  * @return    array    Fields of day
  347.  */
  348.   function _parse_block_dayinfo_get(&$buffer$start$nb)
  349.   {
  350.     $day null;
  351.     $row explode("\t"trim($buffer[$start]));
  352.     $dayinfo->version $row[0];
  353.     $day['version'$row[0];
  354.     $info_row_nb $row[1];
  355.     $num_row_nb $row[2];
  356.     $num_col_nb $row[3];
  357.     $text_row_nb $row[4];
  358.     if ($num_row_nb 1)
  359.     return $day}
  360.     $row explode("\t"trim($buffer[$start 1]));
  361.     $day['date'$row[0];
  362.     $day['exercise_nb'$row[1];
  363.     $day['resting_hr'$row[2];
  364.     $day['weight'round($row[410);
  365.     $day['sleep_minute'round($row[560);
  366.     $more_data true;
  367.     if ($num_row_nb 2)
  368.     $more_data false}
  369.     if ($more_data)
  370.     {
  371.       $row explode("\t"trim($buffer[$start 2]));
  372.       $day['sleep_pattern'$row[0];
  373.     }
  374.     if ($num_row_nb 4)
  375.     $more_data false}
  376.     if ($more_data)
  377.     {
  378.       $row explode("\t"trim($buffer[$start 4]));
  379.       $day['weather'$row[3];
  380.       $day['temperature'$row[4];
  381.     }
  382.     for ($i 0$i $text_row_nb$i++)
  383.     {
  384.       if ($i 0)
  385.       $day['description'.= '\n'}
  386.       $day['description'.=
  387.     trim($buffer[$start $info_row_nb $num_row_nb $i]);
  388.     }
  389.     return $day;
  390.   }
  391.  
  392. /**
  393.  * Parse <exerciseinfo> block in PDD file
  394.  *
  395.  * @param    string    $buffer Buffer with PDD file content
  396.  * @param    integer    $start Line number of block in buffer
  397.  * @param    integer    $nb Number of lines in block
  398.  * @return    array    Array of exercises, indexed by rank
  399.  */
  400.   function _parse_block_exerciseinfo_get(&$buffer$start$nb)
  401.   {
  402.     $exercise null;
  403.     $row explode("\t"trim($buffer[$start]));
  404.     $exercise['version'$row[0];
  405.     $info_row_nb $row[1];
  406.     $num_row_nb $row[2];
  407.     $num_col_nb $row[3];
  408.     $text_row_nb $row[4];
  409.     if ($num_row_nb 1)
  410.     return $exercise}
  411.     $row explode("\t"trim($buffer[$start $info_row_nb]));
  412.     $exercise['no_report'$row[1];
  413.     $exercise['rec_distance'$row[3];
  414.     $exercise['start_time'sprintf("%02d%02d%02d"floor($row[43600),
  415.       floor($row[4]/6060*floor($row[4]/3600)$row[460);
  416.     $exercise['total_time'10 $row[5];
  417.     if ($num_row_nb 2)
  418.     return $exercise}
  419.     $row explode("\t"trim($buffer[$start $info_row_nb 1]));
  420.     $exercise['sport_id'$row[0];
  421.     $exercise['feeling'$row[2];
  422.     $exercise['recovery'$row[3];
  423.     $exercise['energy'$row[5];
  424.     $more_data true;
  425.     if ($num_row_nb 3)
  426.     $more_data false}
  427.     if ($more_data)
  428.     {
  429.       $row explode("\t"trim($buffer[$start $info_row_nb 2]));
  430.       $exercise['real_distance'$row[0];
  431.       $exercise['ascend'$row[5];
  432.     }
  433.     if ($num_row_nb 8)
  434.     $more_data false}
  435.     if ($more_data)
  436.     {
  437.       $row explode("\t"trim($buffer[$start $info_row_nb 7]));
  438.       $exercise['recording_rate'$row[4];
  439.       $exercise['orig_ascend'$row[5];
  440.     }
  441.     if ($num_row_nb 9)
  442.     $more_data false}
  443.     if ($more_data)
  444.     {
  445.       $row explode("\t"trim($buffer[$start $info_row_nb 8]));
  446.       $exercise['avg_hr'$row[0];
  447.       $exercise['max_hr'$row[1];
  448.       $exercise['avg_speed'$row[2];
  449.       $exercise['max_speed'$row[3];
  450.       $exercise['avg_cadence'$row[4];
  451.       $exercise['max_cadence'$row[5];
  452.     }
  453.     if ($num_row_nb 10)
  454.     $more_data false}
  455.     if ($more_data)
  456.     {
  457.       $row explode("\t"trim($buffer[$start $info_row_nb 9]));
  458.       $exercise['avg_altitude'$row[0];
  459.       $exercise['max_altitude'$row[1];
  460.     }
  461.     if ($num_row_nb 12)
  462.     $more_data false}
  463.     if ($more_data)
  464.     {
  465.       $row explode("\t"trim($buffer[$start $info_row_nb 11]));
  466.       $exercise['avg_calory_rate'$row[0];
  467.       $exercise['beat_sum'$row[2];
  468.       $exercise['orig_energy'$row[5];
  469.     }
  470.     if ($num_row_nb 15)
  471.     $more_data false}
  472.     if ($more_data)
  473.     {
  474.       $row explode("\t"trim($buffer[$start $info_row_nb 14]));
  475.       $exercise['ranking'$row[1];
  476.       $exercise['running_index'$row[3];
  477.     }
  478.     if ($num_row_nb 16)
  479.     $more_data false}
  480.     if ($more_data)
  481.     {
  482.       $row explode("\t"trim($buffer[$start $info_row_nb 15]));
  483.       $exercise['footpod_cal'$row[3];
  484.     }
  485.  
  486.     if ($text_row_nb 1)
  487.     return $exercise}
  488.     $exercise['title'trim($buffer[$start $info_row_nb $num_row_nb]);
  489.     if ($text_row_nb 2)
  490.     return $exercise}
  491.     $exercise['description'=
  492.       trim($buffer[$start $info_row_nb $num_row_nb 1]);
  493.     if ($text_row_nb 3)
  494.     return $exercise}
  495.     $exercise['hrm_file'=
  496.       trim($buffer[$start $info_row_nb $num_row_nb 2]);
  497.  
  498.     return $exercise;
  499.   }
  500. }
  501. ?>

Documentation generated on Sat, 28 Mar 2009 23:16:54 +0000 by phpDocumentor 1.4.1