Looking for your First Big wingsuit? Check out TonySuits Foghead3. You will not be disappointed.

App developed by Aleksandr Kunin

Deutsch translate by Tatiana Holm

Spanish translate by Ekaterina Larina

Logo designed by Julia Makoveeva


    def distance a, b       
      rad_per_deg = Math::PI/180  # PI / 180
      rkm = 6371                  # Радиус земли в километрах     
      rm = rkm * 1000            
                  
      dlon_rad = (b[1]-a[1]) * rad_per_deg
      dlat_rad = (b[0]-a[0]) * rad_per_deg
                        
      lat1_rad, lon1_rad = a.map! {|i| i * rad_per_deg } 
      lat2_rad, lon2_rad = b.map! {|i| i * rad_per_deg } 
                                  
      a = Math.sin(dlat_rad/2)**2 + Math.cos(lat1_rad) * Math.cos(lat2_rad) * Math.sin(dlon_rad/2)**2
      c = 2 * Math.asin(Math.sqrt(a)) 
                                          
      rm * c # Расстояние в метрах    
    end

    def process_track_points track_points

      # Пока не придумал что делать с 5 Гц и 10 Гц файлами - оставляю только первую запись по дате создания
      track_points.uniq!{ |x| DateTime.strptime(x[:point_created_at], '%Y-%m-%dT%H:%M:%S') }

      min_h = track_points.min_by{ |x| x[:elevation] }[:elevation]
      # Уменьшим высоту во всех точках на минимальную. (корректировка относительно уровня земли)
      track_points.each do |x|
        x[:elevation] -= min_h
      end

      min_h = track_points.min_by{ |x| x[:elevation] }[:elevation]
      max_h = track_points.max_by{ |x| x[:elevation] }[:elevation]

      # Расчет дистанции и времени полета
      fl_time = 0

      track_points.each_index do |i|
        point = track_points[i]
        point[:distance] = 0 if i == 0
        if i > 0
          prev_point = track_points.at(i-1)

          datetime_1 = DateTime.strptime(point[:point_created_at], '%Y-%m-%dT%H:%M:%S')
          datetime_2 = DateTime.strptime(prev_point[:point_created_at], '%Y-%m-%dT%H:%M:%S')
          fl_time_diff = (datetime_1 - datetime_2) * 1.days
          fl_time += fl_time_diff

          point[:distance] = calc_distance [prev_point[:latitude], prev_point[:longitude]], [point[:latitude], point[:longitude]]
          point[:h_speed] = point[:distance] / fl_time_diff * 3.6
          point[:v_speed] = (prev_point[:elevation] - point[:elevation]) / fl_time_diff * 3.6
        end
        point[:fl_time] = fl_time
      end

      # Медианный фильтр для расстояния и высоты
      track_points.each_index do |i|

        point = track_points[i]

        median_start = [0, i-1].max
        median_end  = [track_points.count-1, i+1].min

        median_points = [track_points[median_start], point, track_points[median_end]]
        point[:distance]  = median_points.map { |x| x[:distance] }.sort[1]
        point[:elevation] = median_points.map { |x| x[:elevation] }.sort[1]
        point[:h_speed]   = median_points.map { |x| x[:h_speed] || 0 }.sort[1]
        point[:v_speed]   = median_points.map { |x| x[:v_speed] || 0 }.sort[1]

      end

      self.ff_start = 0
      self.ff_end = fl_time

      # Развернем массив и найдем точку после достижения максимальной высоты и набору скорости в 25 км/ч
      track_points.reverse!
      start_point = track_points.detect { |x| x[:elevation] >= (max_h - 15) }
      self.ff_start = start_point[:fl_time] if start_point.present?

      track_points.reverse!
      start_point = track_points.detect { |x| (x[:fl_time] > self.ff_start && x[:v_speed] > 25) }
      self.ff_start = start_point[:fl_time] if start_point.present?

      # Найдем первую точку ниже минимума (предполагаю Земли) + 50 метров
      end_point = track_points.detect { |x| x[:elevation] < (min_h + 50) }
      self.ff_end = end_point[:fl_time] if end_point.present?

      track_points

    end
         
      function set_chart_data() {

        var dist_data = [],
          elev_data = [],
          heights_data = [],
          h_speed = [],
          v_speed = [],
          gr = [],

          avg_h_speed = 0,
          avg_v_speed = 0,
          avg_gr = 0,

          min_h_speed = 0,
          max_h_speed = 0,

          min_v_speed = 0,
          max_v_speed = 0,

          min_gr = 0,
          max_gr = 0,

          fl_time = 0,
          dist = 0,
          elev = 0;

        max_val = typeof range_from !== 'undefined' ? range_from : 100000;
        min_val = typeof range_to !== 'undefined' ? range_to : 0;

        var isFirst = true,
            isLast = false;

        for (var index in charts_data) {
          
          var current_point = charts_data[index];
          var point = {};

          isLast = true;

          if (current_point.elevation <= max_val && current_point.elevation >= min_val) {

            point = clone(current_point);

            // Корректировка выбранного диапазона
            if (isFirst) {
            
              isFirst = false;
              if (current_point.elevation != max_val && charts_data.hasOwnProperty(index-1)) {

                point.elevation_diff = max_val - current_point.elevation;
                
                var k = point.elevation_diff / current_point.elevation_diff;

                point.distance = Math.round(current_point.distance * k);
                point.fl_time = Math.round(current_point.fl_time * k);

                point.elevation = current_point.elevation;
                point.v_speed = current_point.v_speed;
                point.h_speed = current_point.h_speed;
              }
            }

            isLast = false;

            dist += point.distance;
            elev += point.elevation_diff;

            elev_data.push([fl_time, elev]);
            dist_data.push([fl_time, dist]);

            heights_data.push([fl_time, point.elevation]);
            h_speed.push([fl_time, point.h_speed]);
            v_speed.push([fl_time, point.v_speed]);

            gr.push([fl_time, point.glrat]);

            fl_time += point.fl_time;

            min_h_speed = min_h_speed == 0 || min_h_speed > point.h_speed ? point.h_speed : min_h_speed;
            max_h_speed = max_h_speed == 0 || max_h_speed < point.h_speed ? point.h_speed : max_h_speed;

            min_v_speed = min_v_speed == 0 || min_v_speed > point.v_speed ? point.v_speed : min_v_speed;
            max_v_speed = max_v_speed == 0 || max_v_speed < point.v_speed ? point.v_speed : max_v_speed;
            
            min_gr = min_gr == 0 || min_gr > point.glrat ? point.glrat : min_gr;
            max_gr = max_gr == 0 || max_gr < point.glrat ? point.glrat : max_gr;
          }

          if (isLast && elev_data.length > 0) {
            if (current_point.elevation <= min_val && charts_data.hasOwnProperty(index - 1)) {

              point = clone(current_point);
              prev_point = charts_data[index - 1];

              point.elevation_diff = prev_point.elevation - min_val;
              var k = point.elevation_diff / current_point.elevation_diff;

              point.fl_time = current_point.fl_time * k;
              point.distance = Math.round(current_point.distance * k);

              dist += point.distance;
              elev += point.elevation_diff;

              elev_data.push([fl_time, elev]);
              dist_data.push([fl_time, dist]);

              heights_data.push([fl_time, point.elevation]);
              h_speed.push([fl_time, point.h_speed]);
              v_speed.push([fl_time, point.v_speed]);

              gr.push([fl_time, point.glrat]);
            }
            break;
          }
        }
 
        var ed_chart = $('#elevation_distance_chart').highcharts();
        ed_chart.series[0].setData(elev_data);
        ed_chart.series[1].setData(dist_data);
        ed_chart.series[2].setData(heights_data);

        var sp_chart = $('#speeds_chart').highcharts();
        sp_chart.series[0].setData(h_speed);
        sp_chart.series[1].setData(v_speed);

        var gr_chart = $('#glideratio_chart').highcharts();
        gr_chart.series[0].setData(gr);

        var ad_chart = $('#all_data_chart').highcharts();
        ad_chart.series[0].setData(h_speed);
        ad_chart.series[1].setData(v_speed);
        ad_chart.series[2].setData(gr);
        ad_chart.series[3].setData(heights_data);
        ad_chart.series[4].setData(dist_data);
        ad_chart.series[5].setData(elev_data);

        $('#dd_distance').text(dist.toString() + ' м');
        $('#dd_elevation').text(elev.toString() + ' м');
        $('#dd_fl_time').text(fl_time.toString() + ' с');
        
        $('#p_min_v_speed').text(min_v_speed.toFixed(0) + '...');
        $('#p_max_v_speed').text('...' + max_v_speed.toFixed(0));
        $('#p_avg_v_speed').text(Math.round(elev / fl_time * 3.6));
        
        $('#p_min_h_speed').text(min_h_speed.toFixed(0) + '...');
        $('#p_max_h_speed').text('...' + max_h_speed.toFixed(0));
        $('#p_avg_h_speed').text(Math.round(dist / fl_time * 3.6).toString());

        $('#p_min_gr').text(min_gr.toFixed(2) + '...');
        $('#p_max_gr').text('...' + max_gr.toFixed(2));
        $('#p_avg_gr').text((dist / elev).toFixed(2));

      };
 
          

GPX icon made by Freepik from www.flaticon.com is licensed by CC BY 3.0
× Something went wrong with that request. Please try again.