Source for file PolarHrm.class.php

Documentation is available at PolarHrm.class.php

  1. <?php
  2. /**
  3.  * Class to parse Polar HRM file and insert / update data to database
  4.  *
  5.  * HRM file contains data related to 1 exercise.
  6.  * This data comes from Polar watch (via 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: PolarHrm.class.php 52 2009-03-17 15:15:30Z jp75018 $
  13.  */
  14.  
  15. /**
  16.  * Class common to all Polar files
  17.  */
  18. require_once ('PolarFile.class.php');
  19. /**
  20.  * Heart rate tools to get user rest HR if not in day info
  21.  */
  22. require_once ('include/hr_tools.php');
  23. /**
  24.  * Get exercise data from database
  25.  */
  26. require_once ('PolarDbQuery.class.php');
  27. /**
  28.  * To generate laps for distance steps / ascend-descend / interval training
  29.  */
  30. require_once ('PolarAutoLap.class.php');
  31. /**
  32.  * Load user related sports
  33.  */
  34. require_once ('PolarSport.class.php');
  35.  
  36. /**
  37.  * In case no PDD day info, default sport is running
  38.  */
  39. define('HRM_DEF_SPORT_ID'1);
  40. /**
  41.  * Number of data insert in a single query (HR data and stats insert)
  42.  */
  43. define('HRM_INSERT_DATA_NB'100);
  44.  
  45. /**
  46.  * Class to parse Polar HRM file and insert / update data to database
  47.  *
  48.  * HRM file contains data related to 1 exercise.
  49.  *
  50.  * The following fields are handled when present in HRM file (some fields are
  51.  * computed before inserting into database):
  52.  *
  53.  * Parameters :
  54.  * - hrm_version : Version of HRM file
  55.  * - date : Date of exercise (format : YYYYMMDD)
  56.  * - start_time : Start time (format : HH:MM:SS)
  57.  * - rank : Rank of exercise in day
  58.  * - monitor_type : Type of monitor (see list of values)
  59.  * - s_mode : List of data type recorded in
  60.  *   ('hr','rr','speed','altitude','cadence')
  61.  * - rec_interval : Recording interval in (1,5,15,30,60,...)
  62.  * - elapsed : Duration in seconds * 10
  63.  * - max_hr : Maximum heart rate (bpm)
  64.  * - rest_hr : Rest heart rate (bpm)
  65.  * - vo2max : VO2 max * 10
  66.  * - weight : Weight in Kg * 10
  67.  *
  68.  * Exercise info (some fields are computed from HR data before inserting) :
  69.  * - rec_distance : Recorded distance in meters * 10
  70.  * - ini_hr : Initial heart rate (bpm)
  71.  * - min_hr : Minimum heart rate (bpm)
  72.  * - avg_hr : Average heart rate (bpm)
  73.  * - max_hr : Maximum heart rate (bpm)
  74.  * - end_hr : Final heart rate (bpm)
  75.  * - ini_speed : Initial speed in km/h * 10
  76.  * - min_speed : Minimum speed in km/h * 10
  77.  * - avg_speed : Average speed in km/h * 10
  78.  * - max_speed : Maximum speed in km/h * 10
  79.  * - end_speed : Final speed in km/h * 10
  80.  * - ini_cadence : Initial cadence in cycle / min
  81.  * - min_cadence : Minimum cadence in cycle / min
  82.  * - avg_cadence : Average cadence in cycle / min
  83.  * - max_cadence : Maximum cadence in cycle / min
  84.  * - end_cadence : Final cadence in cycle / min
  85.  * - avg_stride : Average stride length in centimeters
  86.  * - max_stride : Maximum stride length in centimeters
  87.  * - ini_altitude : Initial altitude in meters
  88.  * - min_altitude : Minimum altitude in meters
  89.  * - avg_altitude : Average altitude in meters
  90.  * - max_altitude : Maximum altitude in meters
  91.  * - end_altitude : Final altitude in meters
  92.  * - ascend : Ascend in meters
  93.  * - beat_sum : Number of heart beats
  94.  * - physio_energy : Physiological energy F(HR) in Kcal
  95.  * - meca_energy : Mecanical energy (movement + ground friction + air friction
  96.  *   + cinetic + potential up - C * potential down) in Kcal
  97.  * - move_energy : Movement energy (internal energy) in calories
  98.  * - fr_grn_energy : Ground friction energy in calories
  99.  * - fr_air_energy : Air friction energy in calories
  100.  * - cin_energy : (external) kinetic energy in calories
  101.  * - up_energy : Potential (ascend only) energy in calories
  102.  * - down_energy : Potential (descend only) energy in calories (in part)
  103.  * - exercise_type : Type of exercise in ('training', 'contest')
  104.  * - mid_elapsed : Elapsed time at mid-distance in seconds * 10
  105.  * - description : Note for the exercise
  106.  *
  107.  * Interval training elements :
  108.  * - repeat_nb : Number of repeatitions (>= 1)
  109.  * - work_distance : Work distance in meters
  110.  * - work_duration : Work duration in seconds
  111.  * - reco_distance : Recovery distance in meters
  112.  * - reco_duration : Recovery duration in seconds
  113.  *
  114.  * Laps (some fields are computed from HR data before inserting) :
  115.  * - elapsed : Duration in seconds * 10 since exercise start
  116.  * - lap_elapsed : Duration in seconds * 10
  117.  * - distance : Recorded distance in meters
  118.  * - ini_hr : Initial heart rate (bpm)
  119.  * - min_hr : Minimum heart rate (bpm)
  120.  * - avg_hr : Average heart rate (bpm)
  121.  * - max_hr : Maximum heart rate (bpm)
  122.  * - end_hr : Final heart rate (bpm)
  123.  * - ini_speed : Initial speed in km/h * 10
  124.  * - min_speed : Minimum speed in km/h * 10
  125.  * - avg_speed : Average speed in km/h * 10
  126.  * - max_speed : Maximum speed in km/h * 10
  127.  * - end_speed : Final speed in km/h * 10
  128.  * - ini_cadence : Initial cadence in cycle / min
  129.  * - min_cadence : Minimum cadence in cycle / min
  130.  * - avg_cadence : Average cadence in cycle / min
  131.  * - max_cadence : Maximum cadence in cycle / min
  132.  * - end_cadence : Final cadence in cycle / min
  133.  * - avg_stride : Average stride length in centimeters
  134.  * - max_stride : Maximum stride length in centimeters
  135.  * - ini_altitude : Initial altitude in meters
  136.  * - min_altitude : Minimum altitude in meters
  137.  * - avg_altitude : Average altitude in meters
  138.  * - max_altitude : Maximum altitude in meters
  139.  * - end_altitude : Final altitude in meters
  140.  * - ascend : Ascend in meters
  141.  * - physio_energy : Physiological energy F(HR) in cal
  142.  * - meca_energy : Mecanical energy in cal
  143.  * - description : Note for lap
  144.  *
  145.  * HR data : For each recording interval (or each heart beat for 'rr'),
  146.  * depending on s_mode:
  147.  * - hr : Heart rate (bpm)
  148.  * - speed : Speed in km/h * 10
  149.  * - cadence: Cadence in cycles / minute
  150.  * - altitude : Altitude in meters
  151.  * - rr : Heart beat duration in milliseconds
  152.  */
  153. class PolarHrm extends PolarFile
  154. {
  155. /**
  156.  * List of recorded data types in ('hr','rr','speed','altitude','cadence')
  157.  * @var array 
  158.  */
  159.   var    $smode;
  160.  
  161. /**
  162.  * Sport standard alias (use to compute mechanical energy)
  163.  * @var string 
  164.  */
  165.   var    $sport_alias;
  166.  
  167. /**
  168.  * Class constructor
  169.  *
  170.  * @param    string  $fileName Polar HRM file name (full path name)
  171.  * @param    integer $userId User ID (as in database)
  172.  * @return    void 
  173.  */
  174.   function PolarHrm ($fileName$userId)
  175.   {
  176.     parent::PolarFile($fileName$userId);
  177.   }
  178.  
  179. /**
  180.  * Insert exercise into database
  181.  *
  182.  * SQL tables :
  183.  * - plw_day_info (insert / update)
  184.  * - plw_exercise (insert / update)
  185.  * - plw_lap (delete / insert)
  186.  * - plw_data (delete / insert), only if data inserted
  187.  * - plw_stat (delete / insert), only if statistics inserted
  188.  *
  189.  * @param    array    $exercise Structure for exercise info, laps, and data:
  190.  *             - 'params' : Parameters (s-mode, interval, ...)
  191.  *             - 'note' : Description of exercise
  192.  *             - 'inttimes' : Data for each lap
  193.  *             - 'intnotes' : Note for each lap (if any)
  194.  *             - 'hrzones' : HR zones data (not used)
  195.  *             - 'trip' : Trip data (not used)
  196.  *             - 'hrdata' : Data for each type recorded
  197.  * @param    boolean    $hr_data True to store 'hrdata' in database, else false
  198.  * @param    boolean    $stat True to store statistics in database, else false
  199.  *             Statistics contain (value, nb) pairs for data types:
  200.  *             'hr', 'speed', 'cadence', 'stride'.
  201.  * @return    void 
  202.  */
  203.   function db_insert($exercise$hr_data true$stat true)
  204.   {
  205.     $para $exercise['params'];
  206.     $note $exercise['note'];
  207.     $intt $exercise['inttimes'];
  208.     $intn $exercise['intnotes'];
  209.     $hrzn $exercise['hrzones'];
  210.     $trip $exercise['trip'];
  211.     $data $exercise['hrdata'];
  212.  
  213.     if (is_array($para))
  214.     return}
  215.     if (count($para))
  216.     return}
  217.     if (is_array($intt))
  218.     $inttarray()}
  219.     if (is_array($intn))
  220.     $intnarray()}
  221.     if (is_array($hrzn))
  222.     $hrznarray()}
  223.     if (is_array($data))
  224.     return}
  225.     $nb_data count($data);
  226.     if ($nb_data)
  227.     return}
  228.  
  229.     if ($stat)
  230.     $stats array()}
  231.  
  232.     $rank = (int)substr(basename($this->fileName)62);
  233.     $smode array();
  234.     reset($para['smode']);
  235.     while (list($modeeach($para['smode']))
  236.     array_push($smode$mode)}
  237.  
  238.   // Distance = sum(laps)
  239.     $distance 0;
  240.     $sum_elapsed 0;
  241.     for ($i 1$i <= count($intt)$i++)
  242.     {
  243.     // Fix "zombie" laps ! (less than 5 second with speed > 30 m/s - 108 km/h)
  244.       $lap_duration $intt[$i]['elapsed'$sum_elapsed;
  245.       $sum_elapsed $intt[$i]['elapsed'];
  246.       $valid_lap true;
  247.       if (($lap_duration 50&& ($lap_duration 0))
  248.       {
  249.           if (($intt[$i]['distance'$lap_duration3)
  250.     {
  251.       $valid_lap false;
  252.     // Compute right distance if end speed
  253.       if ($intt[$i]['end_speed'])
  254.       $distance += round($intt[$i]['end_speed'$lap_duration 360)}
  255.     }
  256.       }
  257.       if ($lap_duration <= 0)
  258.       $valid_lap false}
  259.       if ($valid_lap)
  260.       $distance += $intt[$i]['distance']}
  261.     }
  262.  
  263.   // Get weight from day info (more precision) for energy computing
  264.   // Get closest date if weight = 0
  265.     $exact_weight $para['weight'];
  266.     $request sprintf("SELECT weight FROM plw_day_info WHERE user_id = %d AND weight > 0 ORDER BY ABS(DATEDIFF(day, %s)) LIMIT 1",
  267.       $this->userId$para['date']);
  268.     $result mysql_query($request);
  269.     if (mysql_num_rows($result0)
  270.     {
  271.       $row mysql_fetch_assoc($result);
  272.       $exact_weight =  $row['weight'10;
  273.     }
  274.     mysql_free_result($result);
  275.  
  276.   // Get rest HR, VO2max/MAS from day info (more precision) for energy computing
  277.   // Get closest date if rest HR = 0
  278.   // Get or compute VMA * 10 for speed ratios
  279.   // If no VMA and no VO2max info => Use reserve HR and speed / HR ratio
  280.     $rest_hr 0;
  281.     $max_user_hr 0;
  282.     $vma null;
  283.     $vo2max null;
  284.     $request sprintf("SELECT rest_hr, max_hr, vo2max, vma FROM plw_day_info WHERE user_id = %d AND rest_hr > 0 ORDER BY ABS(DATEDIFF(day, %s)) LIMIT 1",
  285.       $this->userId$para['date']);
  286.     $result mysql_query($request);
  287.     if (mysql_num_rows($result0)
  288.     {
  289.       $row mysql_fetch_assoc($result);
  290.       $rest_hr =  $row['rest_hr'];
  291.       $max_user_hr $row['max_hr'];
  292.       $vma =  $row['vma'];
  293.       $vo2max $row['vo2max'];
  294.     }
  295.     mysql_free_result($result);
  296.   // If no rest HR => get from user record
  297.     get_user_hr_limits($this->userId&$rest_hr&$max_user_hr60);
  298.  
  299.   // If no VMA, use VO2MAX
  300.     if ($vma)
  301.     {
  302.       if ($vo2max)
  303.       $vma round($vo2max VO2MAX_VMA_RATIO)}
  304.       else
  305.       {
  306.       // If no VO2max, try to get vo2max / vma from user record
  307.     $request sprintf("SELECT vo2max, vma FROM plw_user WHERE id = %d",
  308.       $this->userId);
  309.     $result mysql_query($request);
  310.     if ($row mysql_fetch_assoc($result))
  311.     {
  312.       if ($row['vma'])
  313.       $vma $row['vma']}
  314.       else
  315.       {
  316.         if ($row['vo2max'])
  317.         $vma round($row['vo2max'VO2MAX_VMA_RATIO)}
  318.       }
  319.     }
  320.     mysql_free_result($result);
  321.     if ($vma)
  322.     {
  323.     // If yet no VMA, compute from avg speed and HR if possible
  324.       $vma round($exe_avg_speed *
  325.         (($max_user_hr $rest_hr($exe['avg_hr'$rest_hr)));
  326.       if ($vma)
  327.       $vma 170}
  328.     }
  329.       }
  330.     }
  331.  
  332.   /* Try to get sport ID from exercise table (day info PDD file) =>
  333.      Get std sport alias used to compute mechanical energy */
  334.     $sport_id HRM_DEF_SPORT_ID;
  335.     $request sprintf("SELECT sport_id FROM plw_exercise WHERE user_id = %d AND day = %s AND rank = %d",
  336.       $this->userId$para['date']$rank);
  337.     $result mysql_query($request);
  338.     if (mysql_num_rows($result0)
  339.     {
  340.       $row mysql_fetch_assoc($result);
  341.       $sport_id =  $row['sport_id'];
  342.     }
  343.     mysql_free_result($result);
  344.     $sport new PolarSport();
  345.     $sports $sport->get_sports($this->userId);
  346.     unset($sport);
  347.     $this->sport_alias = $sports[$sport_id]['index'];
  348.     unset($sports);
  349.  
  350.   // Get ini, end, avg, min, max HR, beat_sum from HR data ('hr' and 'rr' cases)
  351.     $ini_hr 0;
  352.     $end_hr 0;
  353.     $avg_hr 0;
  354.     $min_hr 0;
  355.     $max_hr 0;
  356.     if ($para['smode']['hr'])
  357.     {
  358.       $sum_hr 0;
  359.       $min_hr 240;
  360.       $sum_hr 0;
  361.       $ini_hr $data[0]['hr'];
  362.       $end_hr $data[$nb_data 1]['hr'];
  363.       for ($i 0$i $nb_data$i++)
  364.       {
  365.     $sum_hr += $data[$i]['hr'];
  366.     if ($data[$i]['hr'$max_hr)
  367.     $max_hr $data[$i]['hr']}
  368.     if ($data[$i]['hr'$min_hr)
  369.     $min_hr $data[$i]['hr']}
  370.     if ($stat && ($data[$i]['hr'0))  // No stat if HR = 0
  371.     $stats['hr'][$data[$i]['hr']] += $para['interval']}
  372.       }
  373.       $avg_hr round($sum_hr $nb_data2);
  374.       $beat_sum round(($para['interval'$sum_hr60);
  375.     }
  376.     if ($para['smode']['rr'])
  377.     {
  378.       $rr_min 100000;
  379.       $rr_max 0;
  380.       $ini_hr round(60000 $data[0]['rr']2);
  381.       $end_hr round(60000 $data[$nb_data 1]['rr']2);
  382.       $avg_hr round($nb_data ($para['length'600)2);
  383.       for ($i 0$i $nb_data$i++)
  384.       {
  385.     if ($data[$i]['rr'$rr_max)
  386.     $rr_max $data[$i]['rr']}
  387.     if ($data[$i]['rr'$rr_min)
  388.     $rr_min $data[$i]['rr']}
  389.     if ($stat)
  390.     $stats['hr'][round(60000/$data[$i]['rr'])+= $data[$i]['rr']/1000}
  391.       }
  392.       $max_hr round(60000 $rr_min2);
  393.       $min_hr round(60000 $rr_max2);
  394.       $beat_sum $nb_data 1;
  395.     // Rounds HR stats and removes if nb = 0
  396.       if (is_array($stats['hr']))
  397.       {
  398.           reset($stats['hr']);
  399.     while (list($valueeach($stats['hr']))
  400.     {
  401.       $stats['hr'][$valueround($stats['hr'][$value]);
  402.       if ($stats['hr'][$value])
  403.       unset($stats['hr'][$value])}
  404.     }
  405.       }
  406.     }
  407.  
  408.  // Get ini, end, avg, min, max speed from HR data
  409.     $ini_speed 0;
  410.     $end_speed 0;
  411.     $avg_speed 0;
  412.     $min_speed 0;
  413.     $max_speed 0;
  414.     $meca_en 0;
  415.     $mov_en 0;
  416.     $grn_en 0;
  417.     $air_en 0;
  418.     $cin_en 0;
  419.     $pot_en_up 0;
  420.     $pot_en_down 0;
  421.     $mid_elapsed 0;
  422.     if ($para['smode']['speed'])
  423.     {
  424.       $sum_speed 0;
  425.       $min_speed 500;
  426.       $ini_speed $data[0]['speed'];
  427.       $end_speed $data[$nb_data 1]['speed'];
  428.       $p_speed 0;
  429.       $cin_en 0;
  430.       $air_en 0;
  431.       $sum_d2 0;
  432.       for ($i 0$i $nb_data$i++)
  433.       {
  434.     $speed $data[$i]['speed'];
  435.     $sum_d2 += $para['interval'$speed 36;
  436.     $sum_speed += $speed;
  437.     if ($speed $max_speed)
  438.     $max_speed $speed}
  439.     if ($speed $min_speed)
  440.     $min_speed $speed}
  441.     if ($speed $p_speed)
  442.     {
  443.       switch ($this->sport_alias)
  444.       {
  445.       case 'running' :
  446.       case 'walking' :
  447.         $cin_en += $speed $speed $p_speed $p_speed;
  448.         break;
  449.       }
  450.     }
  451.     if ($speed 0)
  452.     {
  453.       switch ($this->sport_alias)
  454.       {
  455.       case 'running' :
  456.       case 'walking' :
  457.         $air_en += $para['interval'$speed AIR_CST_ENERGY *
  458.           $speed $speed 46656;    // delta_dist * C * V * V
  459.         break;
  460.       }
  461.     }
  462.     $p_speed $speed;
  463.     if ($stat && ($data[$i]['speed'0))  // No stat if speed = 0
  464.     $stats['speed'][$data[$i]['speed']] += $para['interval']}
  465.       }
  466.       $avg_speed round($sum_speed $nb_data2);
  467.       switch ($this->sport_alias)
  468.       {
  469.       case 'running' :
  470.       case 'walking' :
  471.     $cin_en *= $exact_weight 2592;    // 2592 = 2 * (10 * 3.6)^2
  472.     $cin_en round($cin_en CALORY_2_JOULE);    // In cal
  473.     $mov_en round($exact_weight $distance MOVE_CST_ENERGY);
  474.     $grn_en round($exact_weight $para['length'GROUND_CST_ENERGY/10);
  475.     $air_en round($air_en $exact_weight);
  476.     break;
  477.       }
  478.     // 2nd loop to get mid-distance elapsed time
  479.       if ($sum_d2 0)
  480.       $d_ratio $distance $sum_d2}
  481.       else
  482.       $d_ratio 0}
  483.       $sum_d2 /= 2;
  484.       $sum_d 0;
  485.       $mid_dist $distance 2;
  486.       for ($i 0$i $nb_data$i++)
  487.       {
  488.     $dd $d_ratio $para['interval'$data[$i]['speed'36;
  489.     if (($dd 0&& (($sum_d $dd>= $mid_dist))
  490.     {
  491.       $mid_elapsed =
  492.         round(($i ($mid_dist $sum_d$dd$para['interval'10);
  493.       break;
  494.     }
  495.     $sum_d += $dd;
  496.       }
  497.     }
  498.  
  499.  // Get ini, end, avg, min, max cadence from HR data
  500.  // Also compute avg and max stride length
  501.     $ini_cadence 0;
  502.     $end_cadence 0;
  503.     $avg_cadence 0;
  504.     $min_cadence 0;
  505.     $max_cadence 0;
  506.     $avg_stride 0;
  507.     $max_stride 0;
  508.     $prev_stride 0;
  509.     if ($para['smode']['cadence'])
  510.     {
  511.       $sum_cadence 0;
  512.       $min_cadence 500;
  513.       $sum_cadence 0;
  514.       $ini_cadence $data[0]['cadence'];
  515.       $end_cadence $data[$nb_data 1]['cadence'];
  516.       for ($i 0$i $nb_data$i++)
  517.       {
  518.     $sum_cadence += $data[$i]['cadence'];
  519.     if ($data[$i]['cadence'$max_cadence)
  520.     $max_cadence $data[$i]['cadence']}
  521.     if ($data[$i]['cadence'$min_cadence)
  522.     $min_cadence $data[$i]['cadence']}
  523.     if ($stat && ($data[$i]['cadence'0))  // No stat if cadence = 0
  524.     $stats['cadence'][$data[$i]['cadence']] += $para['interval']}
  525.     if ($para['smode']['speed'])
  526.     {
  527.       if ($data[$i]['cadence'0)
  528.       {
  529.         $stride =
  530.           round(250 $data[$i]['speed'($data[$i]['cadence'])2);
  531.         if ($stride STRIDE_MAX)
  532.         $stride $prev_stride}
  533.         if ($stride $max_stride)
  534.         $max_stride $stride}
  535.         $prev_stride $stride;
  536.       }
  537.       else
  538.       $stride 0}
  539.       if ($stat && (round($stride0))  // No stat if stride = 0
  540.       $stats['stride'][round($stride)+= $para['interval']}
  541.     }
  542.       }
  543.       $avg_cadence round($sum_cadence $nb_data2);
  544.       if ($para['smode']['speed'&& ($avg_cadence 0))
  545.       $avg_stride round(250 $sum_speed ($sum_cadence)2)}
  546.     }
  547.  
  548.   // Get ini, end, avg, min, max altitude + ascend from HR data
  549.     $ini_altitude 0;
  550.     $end_altitude 0;
  551.     $avg_altitude 0;
  552.     $min_altitude 0;
  553.     $max_altitude 0;
  554.     $ascend 0;
  555.     $pot_en_up 0;
  556.     $pot_en_down 0;
  557.     if ($para['smode']['altitude'])
  558.     {
  559.       $sum_altitude 0;
  560.       $min_altitude 10000;
  561.       $sum_altitude 0;
  562.       $ini_altitude $data[0]['altitude'];
  563.       $end_altitude $data[$nb_data 1]['altitude'];
  564.       $alt_local_min $data[0]['altitude'];
  565.       for ($i 1$i $nb_data$i++)
  566.       {
  567.     $sum_altitude += $data[$i]['altitude'];
  568.     if ($data[$i]['altitude'$max_altitude)
  569.     $max_altitude $data[$i]['altitude']}
  570.     if ($data[$i]['altitude'$min_altitude)
  571.     $min_altitude $data[$i]['altitude']}
  572.       // Skip variations due to pause / merge exercises (air pressure change)
  573.           if ($data[$i]['altitude'>
  574.       ($data[$i 1]['altitude'MIN_DELTA_ALTITUDE $para['interval']))
  575.     $alt_local_min $data[$i]['altitude']}
  576.     else
  577.     {
  578.     // Avoid variations due to measure precision (MIN_DELTA_ALTITUDE)
  579.       if ($data[$i]['altitude'>= ($alt_local_min MIN_DELTA_ALTITUDE))
  580.       {
  581.         $ascend += $data[$i]['altitude'$alt_local_min;
  582.         $alt_local_min $data[$i]['altitude'];
  583.       }
  584.       if ($data[$i]['altitude'$alt_local_min)
  585.       $alt_local_min $data[$i]['altitude']}
  586.     }
  587.       }
  588.       $avg_altitude round($sum_altitude $nb_data2);
  589.       switch ($this->sport_alias)
  590.       {
  591.       case 'running' :
  592.       case 'walking' :
  593.     $pot_en_up =round($exact_weight $ascend GRAVI_CST CALORY_2_JOULE);
  594.     break;
  595.       }
  596.       $descend $ascend $ini_altitude $end_altitude;
  597.       if ($descend 0)
  598.       $descend 0}
  599.       switch ($this->sport_alias)
  600.       {
  601.       case 'running' :
  602.       case 'walking' :
  603.     $pot_en_down round(POT_DOWN_USED_CST_ENERGY *
  604.       $exact_weight $descend GRAVI_CST CALORY_2_JOULE);
  605.           break;
  606.       }
  607.     }
  608.     switch ($this->sport_alias)
  609.     {
  610.     case 'running' :
  611.     case 'walking' :
  612.       $meca_en round(($cin_en $pot_en_up $pot_en_down +
  613.     $mov_en $grn_en $air_en1000);
  614.       break;
  615.     }
  616.  
  617.   // Parse note to extract contest distance or interval training expression
  618.     $exercise_type 'training';
  619.     $exact_dist 0;
  620.     $int_train null;
  621.     $options array();
  622.     if ($note)
  623.     {
  624.       $note $this->_parse_note($note,
  625.     &$exercise_type&$exact_dist&$int_train&$options);
  626.     // Check parsed distance is close enough to recorded distance (15%)
  627.       if (($distance 0&& ($exercise_type == 'contest'))
  628.       {
  629.           if (abs($exact_dist $distance($exact_dist $distance0.075)
  630.     $exact_dist 0}
  631.       }
  632.     }
  633.     $real_dist ($exact_dist 0$exact_dist $distance;
  634.  
  635.   // 1. Insert / Update day part
  636.   // Note : weight not updated (better from PDD file)
  637.     $request sprintf("INSERT INTO plw_day_info(user_id, day, cre_date, mod_date, rest_hr, max_hr, weight, vo2max) VALUES(%d, %s, NOW(), NOW(), %s, %s, %s, %s) ON DUPLICATE KEY UPDATE cre_date=cre_date, mod_date=NOW(), rest_hr=%s, max_hr=%s, vo2max=%s",
  638.       $this->userId$para['date'],
  639.       $para['resthr'$para['resthr''NULL',
  640.       $para['maxhr'$para['maxhr''NULL',
  641.       $para['weight'10 $para['maxhr''NULL',
  642.       $para['vo2max'10 $para['vo2max''NULL',
  643.       $para['resthr'$para['resthr''NULL',
  644.       $para['maxhr'$para['maxhr''NULL',
  645.       $para['vo2max'10 $para['vo2max''NULL'
  646.     );
  647.     mysql_query($request);
  648.  
  649.   // Get last inserted exercise ID to check if insert or update
  650.     $max_exercise_id 0;
  651.     $request sprintf("SELECT MAX(id) AS id FROM plw_exercise WHERE user_id = %d",
  652.       $this->userId);
  653.     $result mysql_query($request);
  654.     if (mysql_num_rows($result0)
  655.     {
  656.       $row mysql_fetch_assoc($result);
  657.       $max_exercise_id $row['id'];
  658.     }
  659.     mysql_free_result($result);
  660.  
  661.     $exe_avg_speed $avg_speed;
  662.   // Computes energy from physilogical parameters (HR, rest HR, weight)
  663.     $physio_en 0;
  664.     if ($exact_weight && $rest_hr && $beat_sum && $para['length'&& $vma)
  665.     {
  666.       $physio_old round(6.67 *
  667.     ($beat_sum PHYSIO_CST_REST $rest_hr ($para['length'600)) /
  668.       $exact_weight);
  669.       $physio_en round($exact_weight ($vma/10PHYSIO_CST_POWER *
  670.     ($beat_sum PHYSIO_CST_REST $rest_hr ($para['length'600)));
  671.     }
  672.  
  673.   // 2. Insert / Update exercise part
  674.   /*
  675.     Note : real_distance, description, exercise_type, and options not updated
  676.     (up-to-date in PDD file only)
  677.   */
  678.   // Unset footpod options if no speed measure !
  679.     if ($para['smode']['speed'])
  680.     {
  681.       unset($options['footpod_id']);
  682.       unset($options['footpod_batt']);
  683.     }
  684.     $exe_delta_altitude $max_altitude $min_altitude;
  685.     $request sprintf("INSERT INTO plw_exercise(user_id, day, rank, status, cre_date, mod_date, sport_id, start_time, hrm_version, monitor_type, s_mode, rec_interval, elapsed, rec_distance, real_distance, ini_hr, min_hr, avg_hr, max_hr, end_hr, ini_speed, min_speed, avg_speed, max_speed, end_speed, ini_cadence, min_cadence, avg_cadence, max_cadence, end_cadence, avg_stride, max_stride, ini_altitude, min_altitude, avg_altitude, max_altitude, end_altitude, ascend, beat_sum, physio_energy, meca_energy, move_energy, fr_grn_energy, fr_air_energy, cin_energy, up_energy, down_energy, exercise_type, mid_elapsed, description, shoe, shoe_ini_dist, footpod_id, footpod_batt) VALUES (%d, %s, %d, 'offline', NOW(), NOW(), %d, '%s', %d, %d, ('%s'), %d, %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, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, '%s', %s, %s, %s, %s, %s, %s) ON DUPLICATE KEY UPDATE status=IF(status='offline','offline','online'), cre_date=cre_date, mod_date=NOW(), start_time='%s', hrm_version=%d, monitor_type=%d, s_mode=('%s'), rec_interval=%d, elapsed=%d, rec_distance=%s, ini_hr=%s, min_hr=%s, avg_hr=%s, max_hr=%s, end_hr=%s, ini_speed=%s, min_speed=%s, avg_speed=%s, max_speed=%s, end_speed=%s, ini_cadence=%s, min_cadence=%s, avg_cadence=%s, max_cadence=%s, end_cadence=%s, avg_stride=%s, max_stride=%s, ini_altitude=%s, min_altitude=%s, avg_altitude=%s, max_altitude=%s, end_altitude=%s, ascend=%s, beat_sum=%s, physio_energy=%s, meca_energy=%s, move_energy=%s, fr_grn_energy=%s, fr_air_energy=%s, cin_energy=%s, up_energy=%s, down_energy=%s, mid_elapsed=%s, footpod_id=%s, footpod_batt=%s",
  686.       $this->userId$para['date']$rank$sport_id,
  687.       $para['starttime']$para['version']$para['monitor'],
  688.       implode(','$smode)$para['interval'$para['interval'0,
  689.       $para['length']$distance $distance 'NULL',
  690.       $real_dist $real_dist 'NULL',
  691.       $max_hr $ini_hr 'NULL'$max_hr $min_hr 'NULL',
  692.       $max_hr $avg_hr 'NULL'$max_hr $max_hr 'NULL',
  693.       $max_hr $end_hr 'NULL',
  694.       $max_speed $ini_speed 'NULL',
  695.       $max_speed $min_speed 'NULL',
  696.       $max_speed $avg_speed 'NULL',
  697.       $max_speed $max_speed 'NULL',
  698.       $max_speed $end_speed 'NULL',
  699.       $max_cadence $ini_cadence 'NULL',
  700.       $max_cadence $min_cadence 'NULL',
  701.       $max_cadence $avg_cadence 'NULL',
  702.       $max_cadence $max_cadence 'NULL',
  703.       $max_cadence $end_cadence 'NULL',
  704.       $max_stride $avg_stride 'NULL',
  705.       $max_stride $max_stride 'NULL',
  706.       (($avg_altitude != 0|| ($ascend 0)) $ini_altitude 'NULL',
  707.       (($avg_altitude != 0|| ($ascend 0)) $min_altitude 'NULL',
  708.       (($avg_altitude != 0|| ($ascend 0)) $avg_altitude 'NULL',
  709.       (($avg_altitude != 0|| ($ascend 0)) $max_altitude 'NULL',
  710.       (($avg_altitude != 0|| ($ascend 0)) $end_altitude 'NULL',
  711.       (($avg_altitude != 0|| ($ascend 0)) $ascend 'NULL',
  712.       $beat_sum $beat_sum 'NULL',
  713.       $physio_en $physio_en 'NULL'$meca_en $meca_en 'NULL',
  714.       $meca_en $mov_en 'NULL'$meca_en $grn_en 'NULL',
  715.       $meca_en $air_en 'NULL'$meca_en $cin_en 'NULL',
  716.       $meca_en $pot_en_up 'NULL'$meca_en $pot_en_down 'NULL',
  717.       $exercise_type,
  718.       $mid_elapsed $mid_elapsed 'NULL',
  719.       $note != '' ?  '\'' mysql_escape_string($note'\'' 'NULL',
  720.       $options['shoe'!= '' ?
  721.       '\'' mysql_escape_string($options['shoe']'\'' :'NULL',
  722.       $options['shoe_ini_dist'$options['shoe_ini_dist''NULL',
  723.       isset($options['footpod_id']$options['footpod_id''NULL',
  724.       isset($options['footpod_batt']$options['footpod_batt''NULL',
  725.       $para['starttime']$para['version']$para['monitor'],
  726.       implode(','$smode)$para['interval'$para['interval'0,
  727.       $para['length'],
  728.       $distance $distance 'NULL',
  729.       $max_hr $ini_hr 'NULL'$max_hr $min_hr 'NULL',
  730.       $max_hr $avg_hr 'NULL'$max_hr $max_hr 'NULL',
  731.       $max_hr $end_hr 'NULL',
  732.       $max_speed $ini_speed 'NULL',
  733.       $max_speed $min_speed 'NULL',
  734.       $max_speed $avg_speed 'NULL',
  735.       $max_speed $max_speed 'NULL',
  736.       $max_speed $end_speed 'NULL',
  737.       $max_cadence $ini_cadence 'NULL',
  738.       $max_cadence $min_cadence 'NULL',
  739.       $max_cadence $avg_cadence 'NULL',
  740.       $max_cadence $max_cadence 'NULL',
  741.       $max_cadence $end_cadence 'NULL',
  742.       $max_stride $avg_stride 'NULL',
  743.       $max_stride $max_stride 'NULL',
  744.       (($avg_altitude != 0|| ($ascend 0)) $ini_altitude 'NULL',
  745.       (($avg_altitude != 0|| ($ascend 0)) $min_altitude 'NULL',
  746.       (($avg_altitude != 0|| ($ascend 0)) $avg_altitude 'NULL',
  747.       (($avg_altitude != 0|| ($ascend 0)) $max_altitude 'NULL',
  748.       (($avg_altitude != 0|| ($ascend 0)) $end_altitude 'NULL',
  749.       (($avg_altitude != 0|| ($ascend 0)) $ascend 'NULL',
  750.       $beat_sum $beat_sum 'NULL',
  751.       $physio_en $physio_en 'NULL'$meca_en $meca_en 'NULL',
  752.       $meca_en $mov_en 'NULL'$meca_en $grn_en 'NULL',
  753.       $meca_en $air_en 'NULL'$meca_en $cin_en 'NULL',
  754.       $meca_en $pot_en_up 'NULL'$meca_en $pot_en_down 'NULL',
  755.       $mid_elapsed $mid_elapsed 'NULL',
  756.       $para['smode']['speed''footpod_id' 'NULL',
  757.       $para['smode']['speed''footpod_batt' 'NULL'
  758.     );
  759.     mysql_query($request);
  760.  
  761.   // Get exercise ID to work with laps, interval training, and HR data
  762.     $exercise_id mysql_insert_id();
  763.  
  764.   // 3. Insert interval training elements (only if new exercise)
  765.     if (is_array($int_train&& ($exercise_id $max_exercise_id))
  766.     {
  767.       for ($int 0$int count($int_train)$int++)
  768.       {
  769.     $train $int_train[$int];
  770.           $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)",
  771.       $exercise_id$int$train['repeat_nb'],
  772.       $train['work_distance'$train['work_distance''NULL',
  773.       $train['work_duration'$train['work_duration''NULL',
  774.       $train['reco_distance'$train['reco_distance''NULL',
  775.       $train['reco_duration'$train['reco_duration''NULL'
  776.     );
  777.     mysql_query($request);
  778.       }
  779.     }
  780.  
  781.   // 4. Insert laps, delete first (not for 'rr')
  782.     $request sprintf("DELETE FROM plw_lap WHERE exercise_id=%d",$exercise_id);
  783.     mysql_query($request);
  784.     if ($para['smode']['rr'])
  785.     {
  786.   // 4.a Insert 'polar' laps
  787.     $total_elapsed 0;
  788.     $p_speed 0;
  789.     $p_laps array();
  790.     for ($l 1$l <= count($intt)$l++)
  791.     {
  792.       $ind floor($total_elapsed (10 $para['interval']))// Ind in HR data
  793.       $nb_rec floor($intt[$l]['elapsed'(10 $para['interval'])) $ind;
  794.       if (($ind $nb_rec$nb_data)
  795.       $nb_rec $nb_data $ind}
  796.       $lap_length $intt[$l]['elapsed'$total_elapsed;
  797.       $lap_distance $intt[$l]['distance'];
  798.  
  799.    // Get ini, end, avg, min, max HR from HR data
  800.       $ini_hr 0;
  801.       $end_hr 0;
  802.       $avg_hr 0;
  803.       $min_hr 0;
  804.       $max_hr 0;
  805.       $beat_sum 0;
  806.       $physio_en 0;
  807.       $meca_en 0;
  808.       $mov_en 0;
  809.       $grn_en 0;
  810.       $air_en 0;
  811.       $cin_en 0;
  812.       $pot_en_up 0;
  813.       $pot_en_down 0;
  814.       if ($para['smode']['hr'&& ($nb_rec 0))
  815.       {
  816.     $sum_hr 0;
  817.     $min_hr 240;
  818.     $sum_hr 0;
  819.     $ini_hr $data[$ind]['hr'];
  820.     $end_hr $data[$ind $nb_rec 1]['hr'];
  821.     for ($i $ind$i $ind $nb_rec$i++)
  822.     {
  823.       $sum_hr += $data[$i]['hr'];
  824.       if ($data[$i]['hr'$max_hr)
  825.       $max_hr $data[$i]['hr']}
  826.       if ($data[$i]['hr'$min_hr)
  827.       $min_hr $data[$i]['hr']}
  828.     }
  829.     $avg_hr round($sum_hr $nb_rec2);
  830.       // Computes energy from physilogical parameters (HR, rest HR, weight)
  831.     $beat_sum round(($para['interval'$sum_hr60);
  832.     if ($exact_weight && $rest_hr && $beat_sum && $lap_length && $vma)
  833.     {
  834.       $physio_en round(1000*$exact_weight ($vma/10PHYSIO_CST_POWER *
  835.         ($beat_sum PHYSIO_CST_REST $rest_hr ($lap_length 600)));
  836.     }
  837.       }
  838.  
  839.    // Get ini, end, avg, min, max speed from HR data
  840.       $ini_speed 0;
  841.       $end_speed 0;
  842.       $avg_speed 0;
  843.       $min_speed 0;
  844.       $max_speed 0;
  845.       if ($para['smode']['speed'&& ($nb_rec 0))
  846.       {
  847.     $sum_speed 0;
  848.     $min_speed 500;
  849.     $sum_speed 0;
  850.     $ini_speed $data[$ind]['speed'];
  851.     $end_speed $data[$ind $nb_rec 1]['speed'];
  852.     $cin_en 0;
  853.     $air_en 0;
  854.     for ($i $ind$i $ind $nb_rec$i++)
  855.     {
  856.       $speed $data[$i]['speed'];
  857.       $sum_speed += $speed;
  858.       if ($speed $max_speed)
  859.       $max_speed $speed}
  860.       if ($speed $min_speed)
  861.       $min_speed $speed}
  862.       if ($speed $p_speed)
  863.       {
  864.         switch ($this->sport_alias)
  865.         {
  866.         case 'running' :
  867.         case 'walking' :
  868.           $cin_en += $speed $speed $p_speed $p_speed;
  869.           break;
  870.         }
  871.       }
  872.       if ($speed 0)
  873.       {
  874.         switch ($this->sport_alias)
  875.         {
  876.         case 'running' :
  877.         case 'walking' :
  878.           $air_en += $para['interval'$speed AIR_CST_ENERGY *
  879.         $speed $speed 46656;    // delta_dist * C * V * V
  880.           break;
  881.         }
  882.       }
  883.       $p_speed $speed;
  884.     }
  885.     $avg_speed round(360 $lap_distance $lap_length2);
  886.     switch ($this->sport_alias)
  887.     {
  888.     case 'running' :
  889.     case 'walking' :
  890.       $cin_en *= $exact_weight 2592;    // 2592 = 2 * (10 * 3.6)^2
  891.       $cin_en round($cin_en CALORY_2_JOULE);    // In cal
  892.       $mov_en round($exact_weight $lap_distance MOVE_CST_ENERGY);
  893.       $grn_en round($exact_weight $lap_length GROUND_CST_ENERGY 10);
  894.       $air_en round($air_en $exact_weight);
  895.     // Adjust cinetic and air energy considering exact duration
  896.       $dist_ratio $lap_length (10 $nb_rec $para['interval']);
  897.       break;
  898.     }
  899.       }
  900.  
  901.    // Get ini, end, avg, min, max cadence from HR data
  902.    // Also compute avg and max stride length
  903.       $ini_cadence 0;
  904.       $end_cadence 0;
  905.       $avg_cadence 0;
  906.       $min_cadence 0;
  907.       $max_cadence 0;
  908.       $avg_stride 0;
  909.       $max_stride 0;
  910.       $prev_stride 0;
  911.       if ($para['smode']['cadence'&& ($nb_rec 0))
  912.       {
  913.     $sum_cadence 0;
  914.     $min_cadence 500;
  915.     $sum_cadence 0;
  916.     $ini_cadence $data[$ind]['cadence'];
  917.     $end_cadence $data[$ind $nb_rec 1]['cadence'];
  918.     for ($i $ind$i $ind $nb_rec$i++)
  919.     {
  920.       $sum_cadence += $data[$i]['cadence'];
  921.       if ($data[$i]['cadence'$max_cadence)
  922.       $max_cadence $data[$i]['cadence']}
  923.       if ($data[$i]['cadence'$min_cadence)
  924.       $min_cadence $data[$i]['cadence']}
  925.       if ($para['smode']['speed'])
  926.       {
  927.         if ($data[$i]['cadence'0)
  928.         {
  929.           $stride =
  930.         round(250 $data[$i]['speed'($data[$i]['cadence'])2);
  931.           if ($stride STRIDE_MAX)
  932.           $stride $prev_stride}
  933.           if ($stride $max_stride)
  934.           $max_stride $stride}
  935.           $prev_stride $stride;
  936.         }
  937.       }
  938.     }
  939.     $avg_cadence round($sum_cadence $nb_rec2);
  940.     if ($para['smode']['speed'&& ($avg_cadence 0))
  941.     $avg_stride round(250 $sum_speed ($sum_cadence)2)}
  942.       }
  943.  
  944.     // Get ini, end, avg, min, max altitude + ascend from HR data
  945.       $ini_altitude 0;
  946.       $end_altitude 0;
  947.       $avg_altitude 0;
  948.       $min_altitude 0;
  949.       $max_altitude 0;
  950.       $ascend 0;
  951.       $pot_en_up 0;
  952.       $pot_en_down 0;
  953.       if ($para['smode']['altitude'&& ($nb_rec 0))
  954.       {
  955.     $sum_altitude 0;
  956.     $min_altitude 10000;
  957.     $sum_altitude 0;
  958.     $ini_altitude $data[$ind]['altitude'];
  959.     $end_altitude $data[$ind $nb_rec 1]['altitude'];
  960.     $alt_local_min $data[$ind]['altitude'];
  961.     for ($i $ind$i $ind $nb_rec$i++)
  962.     {
  963.       $sum_altitude += $data[$i]['altitude'];
  964.       if ($data[$i]['altitude'$max_altitude)
  965.       $max_altitude $data[$i]['altitude']}
  966.       if ($data[$i]['altitude'$min_altitude)
  967.       $min_altitude $data[$i]['altitude']}
  968.       if ($data[$i]['altitude'>= ($alt_local_min MIN_DELTA_ALTITUDE))
  969.       {
  970.         $ascend += $data[$i]['altitude'$alt_local_min;
  971.         $alt_local_min $data[$i]['altitude'];
  972.       }
  973.       if ($data[$i]['altitude'$alt_local_min)
  974.       $alt_local_min $data[$i]['altitude']}
  975.     }
  976.     $avg_altitude round($sum_altitude $nb_rec2);
  977.     switch ($this->sport_alias)
  978.     {
  979.     case 'running' :
  980.     case 'walking' :
  981.       $pot_en_up =
  982.         round($exact_weight $ascend GRAVI_CST CALORY_2_JOULE);
  983.       break;
  984.     }
  985.     $descend $ascend $ini_altitude $end_altitude;
  986.     if ($descend 0)
  987.     $descend 0}
  988.     switch ($this->sport_alias)
  989.     {
  990.     case 'running' :
  991.     case 'walking' :
  992.       $pot_en_down round(POT_DOWN_USED_CST_ENERGY *
  993.         $exact_weight $descend GRAVI_CST CALORY_2_JOULE);
  994.       break;
  995.     }
  996.       }
  997.       switch ($this->sport_alias)
  998.       {
  999.       case 'running' :
  1000.       case 'walking' :
  1001.     $meca_en round($cin_en $pot_en_up $pot_en_down +
  1002.       $mov_en $grn_en $air_en);
  1003.     break;
  1004.       }
  1005.  
  1006.       $lap array(
  1007.           'elapsed' => $intt[$l]['elapsed'],
  1008.           'lap_elapsed' => $intt[$l]['elapsed'$total_elapsed,
  1009.     'distance' => $intt[$l]['distance'],
  1010.     'ini_hr' => $ini_hr,
  1011.     'min_hr' => $min_hr,
  1012.     'avg_hr' => $avg_hr,
  1013.     'max_hr' => $max_hr,
  1014.     'end_hr' => $end_hr,
  1015.     'ini_speed' => $ini_speed,
  1016.     'min_speed' => $min_speed,
  1017.     'avg_speed' => $avg_speed,
  1018.     'max_speed' => $max_speed,
  1019.     'end_speed' => $end_speed,
  1020.     'ini_cadence' => $ini_cadence,
  1021.     'min_cadence' => $min_cadence,
  1022.     'avg_cadence' => $avg_cadence,
  1023.     'max_cadence' => $max_cadence,
  1024.     'end_cadence' => $end_cadence,
  1025.     'avg_stride' => $avg_stride,
  1026.     'max_stride' => $max_stride,
  1027.     'ini_altitude' => $ini_altitude,
  1028.     'min_altitude' => $min_altitude,
  1029.     'avg_altitude' => $avg_altitude,
  1030.     'max_altitude' => $max_altitude,
  1031.     'end_altitude' => $end_altitude,
  1032.     'ascend' => $ascend,
  1033.     'physio_energy' => $physio_en,
  1034.     'meca_energy' => $meca_en,
  1035.     'beat_sum' => $beat_sum,
  1036.     'description' => $intn[$l]
  1037.       );
  1038.       if ($nb_rec 0)    // Add Test to avoid empty intervals!
  1039.       {
  1040.     $this->_insert_lap($exercise_id'polar'$l$lap);
  1041.     $p_laps[$l$lap;
  1042.       }
  1043.       $total_elapsed $intt[$l]['elapsed'];
  1044.     }
  1045.  
  1046.     $auto_lap new PolarAutoLap($this->sport_alias);
  1047.  
  1048.   // 4.b Insert 'distance' laps if recorded distance (speed mesure)
  1049.     if ($distance 0)
  1050.     {
  1051.     // Get real distance from database because it comes from PDD file
  1052.       $request sprintf("SELECT real_distance FROM plw_exercise WHERE id = %d",
  1053.     $exercise_id);
  1054.       $result mysql_query($request);
  1055.       $exe_data mysql_fetch_assoc($result);
  1056.       mysql_free_result($result);
  1057.       $real_distance $exe_data['real_distance'];
  1058.     // Compute distance step
  1059.       $d_step 5000;        // 5 km
  1060.       if ($real_distance <= 100000)    // <= 100 km
  1061.       $d_step 2000}    // 2 km
  1062.       if ($real_distance <= 50000)    // <= 50 km
  1063.       $d_step 1000}    // 1 km
  1064.       if ($real_distance 10000)    // < 10 km
  1065.       $d_step 500;  }    // 500 m
  1066.       if ($real_distance 5000)    // < 5 km
  1067.       $d_step 100}    // 100 m
  1068.       $d_laps $auto_lap->generate_auto_laps('distance'$data,
  1069.     $para['interval']$real_distance$rest_hr$exact_weight$d_step,
  1070.     $vma 10);
  1071.       if (count($d_laps1)
  1072.       {
  1073.     for ($l 1$l <= count($d_laps)$l++)
  1074.     $this->_insert_lap($exercise_id'distance'$l$d_laps[$l])}
  1075.       }
  1076.       unset($d_laps);
  1077.     }
  1078.  
  1079.   // 4.c Insert 'interval' laps if recorded distance (speed mesure)
  1080.   // Get interval training elements because may be set in PDD exercise note
  1081.     $dbQuery new PolarDbQuery();
  1082.     $int_train $dbQuery->get_interval_training($exercise_id);
  1083.     unset($dbQuery);
  1084.     if (count($int_train))
  1085.     {
  1086.       $i_laps $auto_lap->generate_interval_training($p_laps,
  1087.     $int_train$vma 10);
  1088.       if (count($i_laps1)
  1089.       {
  1090.     for ($l 1$l <= count($i_laps)$l++)
  1091.     $this->_insert_lap($exercise_id'interval'$l$i_laps[$l])}
  1092.       }
  1093.       unset($i_laps);
  1094.     }
  1095.  
  1096.   // 4.d Insert 'altitude' laps if recorded altitude
  1097.     if ($exe_delta_altitude >= MIN_DELTA_ALTITUDE)
  1098.     {
  1099.       $a_laps $auto_lap->generate_auto_laps('altitude'$data,
  1100.     $para['interval']$real_distance$rest_hr$exact_weight,
  1101.     $exe_delta_altitude$vma 10);
  1102.       if (count($a_laps1)
  1103.       {
  1104.     for ($l 1$l <= count($a_laps)$l++)
  1105.     $this->_insert_lap($exercise_id'altitude'$l$a_laps[$l])}
  1106.       }
  1107.       unset($a_laps);
  1108.     }
  1109.  
  1110.   // 4.e Insert 'time' laps
  1111.   // Compute time step
  1112.     $t_step 36000;    // 1 H
  1113.     if ($para['length'<= 864000)    // <= 24 H
  1114.     $t_step 18000}// 30'
  1115.     if ($para['length'<= 432000)    // <= 12 H
  1116.     $t_step 9000}    // 15'
  1117.     if ($para['length'<= 144000)    // <= 4 H
  1118.     $t_step 3000}    // 5'
  1119.     if ($para['length'<= 30000)    // <= 50'
  1120.     $t_step 600}    // 1'
  1121.     if ($para['length'<= 15000)    // <= 25'
  1122.     $t_step 300}    // 30"
  1123.     if ($para['length'<= 7200)    // <= 12'
  1124.     $t_step 150}    // 15"
  1125.     $t_laps $auto_lap->generate_auto_laps('time'$data,
  1126.       $para['interval']$real_distance$rest_hr$exact_weight$t_step,
  1127.       $vma 10);
  1128.     if (count($t_laps1)
  1129.     {
  1130.       for ($l 1$l <= count($t_laps)$l++)
  1131.       $this->_insert_lap($exercise_id'time'$l$t_laps[$l])}
  1132.     }
  1133.     unset($t_laps);
  1134.  
  1135.     unset($auto_lap);
  1136.     }
  1137.  
  1138.   // 5. Insert data, delete first
  1139.     $request =
  1140.       sprintf("DELETE FROM plw_data WHERE exercise_id=%d"$exercise_id);
  1141.     mysql_query($request);
  1142.     if ($hr_data)
  1143.     {
  1144.       for ($i 0$i $nb_data$i++)
  1145.       {
  1146.       // Insert N by N
  1147.     if (($i HRM_INSERT_DATA_NB))
  1148.     {
  1149.       $request 'INSERT INTO plw_data(exercise_id, rank, hr, rr, speed, cadence, altitude) VALUES ';
  1150.     }
  1151.     else
  1152.     $request .= ','}
  1153.     $request .= sprintf("(%d,%d,%s,%s,%s,%s,%s)",
  1154.       $exercise_id$i 1,
  1155.       $para['smode']['hr'"'" $data[$i]['hr'"'" 'NULL',
  1156.       $para['smode']['rr'"'" $data[$i]['rr'"'" 'NULL',
  1157.       $para['smode']['speed'"'" $data[$i]['speed'"'" 'NULL',
  1158.       $para['smode']['cadence'"'" $data[$i]['cadence'"'" 'NULL',
  1159.       $para['smode']['altitude'"'" $data[$i]['altitude'"'"'NULL'
  1160.     );
  1161.     if (((($i 1HRM_INSERT_DATA_NB)) || (($i 1== $nb_data))
  1162.     mysql_query($request)}
  1163.       }
  1164.     }
  1165.  
  1166.   // 5. Insert stats, delete first
  1167.     if ($stat)
  1168.     {
  1169.       $request =
  1170.     sprintf("DELETE FROM plw_stat WHERE exercise_id=%d"$exercise_id);
  1171.       mysql_query($request);
  1172.       $i 0;
  1173.     // Get number of rows
  1174.       $nb_stat 0;
  1175.       reset($stats);
  1176.       while (list($data_type$valueseach($stats))
  1177.       $nb_stat += count($values)}
  1178.       reset($stats);
  1179.       while (list($data_type$valueseach($stats))
  1180.       {
  1181.     reset($values);
  1182.     while (list($value$nbeach($values))
  1183.     {
  1184.     // Insert N by N
  1185.       if (($i HRM_INSERT_DATA_NB))
  1186.       {
  1187.         $request 'INSERT INTO plw_stat(exercise_id, data_type, value, nb) VALUES ';
  1188.       }
  1189.       else
  1190.       $request .= ','}
  1191.       $request .= sprintf("(%d,'%s',%d,%d)",
  1192.         $exercise_id$data_type$value$nb);
  1193.       if (((($i 1HRM_INSERT_DATA_NB)) || (($i 1== $nb_stat))
  1194.       mysql_query($request)}
  1195.       $i++;
  1196.     }
  1197.       }
  1198.     }
  1199.   }
  1200.  
  1201. /**
  1202.  * Insert one lap row into plw_lap SQL table
  1203.  *
  1204.  * @param    integer    $exercise_id Exercise ID
  1205.  * @param    string    $lap_type Type of lap in ('polar','distance','altitude',
  1206.  *             'interval')
  1207.  * @param    integer    $rank Rank of lap (from 1)
  1208.  * @param    array    $lap Lap fields (same structure as plw_lap table)
  1209.  * @return    void 
  1210.  */
  1211.   function _insert_lap($exercise_id$lap_type$rank$lap)
  1212.   {
  1213.     $request sprintf("INSERT INTO plw_lap(exercise_id, lap_type, rank, subtotal, elapsed, lap_elapsed, distance, ini_hr, min_hr, avg_hr, max_hr, end_hr, ini_speed, min_speed, avg_speed, max_speed, end_speed, ini_cadence, min_cadence, avg_cadence, max_cadence, end_cadence, avg_stride, max_stride, ini_altitude, min_altitude, avg_altitude, max_altitude, end_altitude, ascend, physio_energy, meca_energy, beat_sum, description) VALUES (%d, '%s', %d, '%s', %d, %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, %s, %s, %s, %s)",
  1214.       $exercise_id$lap_type$rank$lap['subtotal'],
  1215.       $lap['elapsed']$lap['lap_elapsed'],
  1216.       $lap['distance'$lap['distance''NULL',
  1217.       $lap['max_hr'$lap['ini_hr''NULL',
  1218.       $lap['max_hr'$lap['min_hr''NULL',
  1219.       $lap['max_hr'$lap['avg_hr''NULL',
  1220.       $lap['max_hr'$lap['max_hr''NULL',
  1221.       $lap['max_hr'$lap['end_hr''NULL',
  1222.       $lap['max_speed'$lap['ini_speed''NULL',
  1223.       $lap['max_speed'$lap['min_speed''NULL',
  1224.       $lap['max_speed'$lap['avg_speed''NULL',
  1225.       $lap['max_speed'$lap['max_speed''NULL',
  1226.       $lap['max_speed'$lap['end_speed''NULL',
  1227.       $lap['max_cadence'$lap['ini_cadence''NULL',
  1228.       $lap['max_cadence'$lap['min_cadence''NULL',
  1229.       $lap['max_cadence'$lap['avg_cadence''NULL',
  1230.       $lap['max_cadence'$lap['max_cadence''NULL',
  1231.       $lap['max_cadence'$lap['end_cadence''NULL',
  1232.       $lap['max_stride'$lap['avg_stride''NULL',
  1233.       $lap['max_stride'$lap['max_stride''NULL',
  1234.       (($lap['avg_altitude'!= 0|| ($lap['ascend'0)) ?
  1235.     $lap['ini_altitude''NULL',
  1236.       (($lap['avg_altitude'!= 0|| ($lap['ascend'0)) ?
  1237.     $lap['min_altitude''NULL',
  1238.       (($lap['avg_altitude'!= 0|| ($lap['ascend'0)) ?
  1239.     $lap['avg_altitude''NULL',
  1240.       (($lap['avg_altitude'!= 0|| ($lap['ascend'0)) ?
  1241.     $lap['max_altitude''NULL',
  1242.       (($lap['avg_altitude'!= 0|| ($lap['ascend'0)) ?
  1243.     $lap['end_altitude''NULL',
  1244.       (($lap['avg_altitude'!= 0|| ($lap['ascend'0)) ?
  1245.     $lap['ascend''NULL',
  1246.       $lap['physio_energy'$lap['physio_energy''NULL',
  1247.       $lap['meca_energy'$lap['meca_energy''NULL',
  1248.       $lap['beat_sum'$lap['beat_sum''NULL',
  1249.       $lap['description'!= '' ?
  1250.     '\'' mysql_escape_string($lap['description'])'\'' 'NULL'
  1251.     );
  1252.     mysql_query($request);
  1253.   }
  1254.  
  1255. /**
  1256.  * Parse Polar HRM file (parameters, note, lap times and notes, HR data)
  1257.  *
  1258.  * Notes :
  1259.  * - The following blocks are parsed : <params>, <note>, <inttimes>, <intnotes>,
  1260.  *   <hrzones>, <trip>, <hrdata>.
  1261.  * - Data from <hrzones> and <trip> blocks is not used.
  1262.  *
  1263.  * @param    boolean    $header_only True to parse 'params', 'note', and
  1264.  *             'inttimes' blocks only, else false. True is used to
  1265.  *             display exercise information when uploadind / importing
  1266.  *             HRM files.
  1267.  * @return    array    Structure for exercise data :
  1268.  *             - 'params' : Exercise parameters
  1269.  *             - 'note' : Exercise description
  1270.  *             - 'inttimes' : Exercise laps (there is at least 1 lap)
  1271.  *             - 'intnotes' : Exercise notes when present
  1272.  *             - 'hrzones' : HR zones
  1273.  *             - 'trip' : Trip information
  1274.  *             - 'hrdata' : Recorded data for each data type
  1275.  */
  1276.   function parse($header_only false)
  1277.   {
  1278.     $buffer @file($this->fileName);
  1279.     if (is_array($buffer))
  1280.     return null}
  1281.  
  1282.     $line_no 0;
  1283.     while ($line_no count($buffer))
  1284.     {
  1285.       $begin_block $line_no 1;
  1286.       $block_name $this->_parse_block_get(&$buffer&$line_no);
  1287.  
  1288.       if ($header_only &&
  1289.     (in_array($block_namearray('params''note''inttimes'))))
  1290.       return $exercise}
  1291.  
  1292.       $lg_block $line_no $begin_block 1;
  1293.       if (trim($buffer[$end_block]))
  1294.       $end_block--}
  1295.       $parse_func '_parse_block_' $block_name '_get';
  1296.       if (method_exists($this$parse_func))
  1297.       {
  1298.     $exercise[$block_name=
  1299.       $this->$parse_func(&$buffer$begin_block$lg_block);
  1300.       }
  1301.     }
  1302.  
  1303.   // Check day match filename !
  1304.     $file_day '20' substr(basename($this->fileName'.hrm')06);
  1305.     $data_day $exercise['params']['date'];
  1306.     if ($file_day != $data_day)
  1307.     $exercise null}
  1308.  
  1309.     return $exercise;
  1310.   }
  1311.  
  1312. /**
  1313.  * Parse <params> block in HRM file
  1314.  *
  1315.  * @param    string    $buffer Buffer with HRM file content
  1316.  * @param    integer    $start Line number of block in buffer
  1317.  * @param    integer    $nb Number of lines in block
  1318.  * @return    array    Array of parameters
  1319.  */
  1320.   function _parse_block_params_get(&$buffer$start$nb)
  1321.   {
  1322.     for ($i $start$i ($start $nb)$i++)
  1323.     {
  1324.       $name_value explode('='$buffer[$i]);
  1325.       $name strtolower($name_value[0]);
  1326.       switch ($name)
  1327.       {
  1328.       case 'version' :
  1329.     $this->version = $name_value[1];
  1330.       case 'monitor' :
  1331.       case 'date' :
  1332.       case 'starttime' :
  1333.       case 'length' :
  1334.       case 'interval' :
  1335.       case 'maxhr' :
  1336.       case 'resthr' :
  1337.       case 'vo2max' :
  1338.       case 'weight' :
  1339.       case 'smode' :
  1340.     $value trim($name_value[1]);
  1341.     switch ($name)
  1342.     {
  1343.     case 'smode' :
  1344.       $smode $value;
  1345.       unset($value);
  1346.       if ($this->version > 105)
  1347.       {
  1348.         if ($smode[0== '1')
  1349.         $value['speed'1}
  1350.         if ($smode[1== '1')
  1351.         $value['cadence'1}
  1352.         if ($smode[2== '1')
  1353.         $value['altitude'1}
  1354.         $value['hr'1;
  1355.       }
  1356.       else
  1357.       {
  1358.         if ($smode[0== '0')
  1359.         $value['cadence'1}
  1360.         if ($smode[0== '1')
  1361.         $value['altitude'1}
  1362.         $value['hr'1;
  1363.         $value['speed'1;
  1364.       }
  1365.       break;
  1366.     case 'starttime' :
  1367.       $value substr($value08);
  1368.       break;
  1369.     case 'length' :
  1370.       $value 36000 * (int)substr($value02+
  1371.         600 * (int)substr($value3210 * (int)substr($value62+
  1372.         (int)substr($value91);
  1373.       break;
  1374.     case 'interval' :
  1375.       if ($value == 238)
  1376.       {
  1377.         $params['smode']['rr'1;
  1378.         unset($params['smode']['hr']);
  1379.         $value null;
  1380.       }
  1381.       break;
  1382.     }
  1383.     $params[$name$value;
  1384.     break;
  1385.       }
  1386.     }
  1387.     $this->smode = $params['smode'];
  1388.     return $params;
  1389.   }
  1390.  
  1391. /**
  1392.  * Parse <note> block in HRM file
  1393.  *
  1394.  * @param    string    $buffer Buffer with HRM file content
  1395.  * @param    integer    $start Line number of block in buffer
  1396.  * @param    integer    $nb Number of lines in block
  1397.  * @return    string    Exercise note
  1398.  */
  1399.   function _parse_block_note_get(&$buffer$start$nb)
  1400.   {
  1401.     $note '';
  1402.     for ($i $start$i ($start $nb)$i++)
  1403.     $note .= $buffer[$i]}
  1404.     return trim($note);
  1405.   }
  1406.  
  1407. /**
  1408.  * Parse <inttimes> block in HRM file
  1409.  *
  1410.  * @param    string    $buffer Buffer with HRM file content
  1411.  * @param    integer    $start Line number of block in buffer
  1412.  * @param    integer    $nb Number of lines in block
  1413.  * @return    array    Array of laps (1 to N)
  1414.  */
  1415.   function _parse_block_inttimes_get(&$buffer$start$nb)
  1416.   {
  1417.     $laps array();
  1418.     for ($i 0$i ($nb 5)$i++)
  1419.     {
  1420.       $row explode("\t"trim($buffer[$start $i]));
  1421.       $laps[$i 1]['elapsed'36000 * (int)substr($row[0]02+
  1422.     600 * (int)substr($row[0]3210 * (int)substr($row[0]62+
  1423.     (int)substr($row[0]91);
  1424.       $laps[$i 1]['end_hr'$row[1];
  1425.       $laps[$i 1]['min_hr'$row[2];
  1426.       $laps[$i 1]['avg_hr'$row[3];
  1427.       $laps[$i 1]['max_hr'$row[4];
  1428.       $row explode("\t"trim($buffer[$start $i 1]));
  1429.       $laps[$i 1]['end_speed'$row[3];
  1430.       $laps[$i 1]['end_altitude'$row[5];
  1431.       $row explode("\t"trim($buffer[$start $i 3]));
  1432.       $laps[$i 1]['distance'$row[1];
  1433.       $row explode("\t"trim($buffer[$start $i 4]));
  1434.       $laps[$i 1]['end_stride'$row[0];    // To check !
  1435.     }
  1436.     return $laps;
  1437.   }
  1438.  
  1439. /**
  1440.  * Parse <intnotes> block in HRM file
  1441.  *
  1442.  * @param    string    $buffer Buffer with HRM file content
  1443.  * @param    integer    $start Line number of block in buffer
  1444.  * @param    integer    $nb Number of lines in block
  1445.  * @return    array    Array of lap  notes (indexed by lap number), empty if
  1446.  *             no lap note
  1447.  */
  1448.   function _parse_block_intnotes_get(&$buffer$start$nb)
  1449.   {
  1450.     $notes array();
  1451.     for ($i $start$i ($start $nb)$i++)
  1452.     {
  1453.       $row explode("\t"trim($buffer[$i]));
  1454.       $notes[$row[0]] $row[1];
  1455.     }
  1456.     return $notes;
  1457.   }
  1458.  
  1459. /**
  1460.  * Parse <hrzones> block in HRM file
  1461.  *
  1462.  * @param    string    $buffer Buffer with HRM file content
  1463.  * @param    integer    $start Line number of block in buffer
  1464.  * @param    integer    $nb Number of lines in block
  1465.  * @return    array    Array of HR zones, from 1
  1466.  */
  1467.   function _parse_block_hrzones_get(&$buffer$start$nb)
  1468.   {
  1469.     $zones array();
  1470.     $j 1;
  1471.     for ($i $start$i ($start $nb)$i++)
  1472.     {
  1473.       $value trim($buffer[$i]);
  1474.       if ($value 0)
  1475.       $zones[$j++$value}
  1476.     }
  1477.     return $zones;
  1478.   }
  1479.  
  1480. /**
  1481.  * Parse <trip> block in HRM file
  1482.  *
  1483.  * @param    string    $buffer Buffer with HRM file content
  1484.  * @param    integer    $start Line number of block in buffer
  1485.  * @param    integer    $nb Number of lines in block
  1486.  * @return    array    Trip structure
  1487.  */
  1488.   function _parse_block_trip_get(&$buffer$start$nb)
  1489.   {
  1490.     $j 0;
  1491.     for ($i $start$i ($start $nb)$i++)
  1492.     $rows[$j++trim($buffer[$i])}
  1493.     $trip['distance'$rows[0];
  1494.     $trip['ascend'$rows[1];
  1495.     $trip['duration'$rows[2];
  1496.     $trip['avg_altitude'$rows[3];
  1497.     $trip['max_altitude'$rows[4];
  1498.     $trip['avg_speed'$rows[5];
  1499.     $trip['max_speed'$rows[6];
  1500.     return $trip;
  1501.   }
  1502.  
  1503. /**
  1504.  * Parse <hrdata> block in HRM file
  1505.  *
  1506.  * @param    string    $buffer Buffer with HRM file content
  1507.  * @param    integer    $start Line number of block in buffer
  1508.  * @param    integer    $nb Number of lines in block
  1509.  * @return    array    Array of recorded data values, from 0
  1510.  */
  1511.   function _parse_block_hrdata_get(&$buffer$start$nb)
  1512.   {
  1513.     $hrdata array();
  1514.     $j 0;
  1515.     for ($i $start$i ($start $nb)$i++)
  1516.     {
  1517.       $k 0;
  1518.       $row explode("\t"trim($buffer[$i]));
  1519.       if ($this->smode['hr'])
  1520.       $hrdata[$j]['hr'$row[$k++]}
  1521.       if ($this->smode['rr'])
  1522.       $hrdata[$j]['rr'$row[$k++]}
  1523.       if ($this->smode['speed'])
  1524.       $hrdata[$j]['speed'$row[$k++]}
  1525.       if ($this->smode['cadence'])
  1526.       $hrdata[$j]['cadence'$row[$k++]}
  1527.       if ($this->smode['altitude'])
  1528.       $hrdata[$j]['altitude'$row[$k++]}
  1529.       $j++;
  1530.     }
  1531.     return $hrdata;
  1532.   }
  1533. }
  1534. ?>

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