Weblog Tomasza Przechlewskiego [Zdjęcie T. Przechlewskiego]


scrum
random image [Photo gallery]
Zestawienie tagów
1-wire | 18b20 | 1wire | 2140 | 3rz | alsamixer | amazon | anniversary | antypis | apache | api | applebaum | arm | armenia | astronomy | asus | atom.xml | awk | aws | bachotek | bakłażan | balcerowicz | balta | banan | bash | batumi | berlin | bibtex | bieszczady | biznes | blogger | blogging | blosxom | bono | borne-sulinowo | breugel | bt747 | budapeszt | bursztyn | canon | cedewu | chello | chiller | chillerpl | chown | chujowetaśmy | ciasto | cmentarz | contour | cron | css | csv | curl | cycling | d54250wykh | dbi | debian | dejavu | dhcp | dht22 | dia | docbook | dom | dp1500 | ds18b20 | dulkiewicz | dyndns | dynia | ebay | economy | ekonomia | elka | elm | emacs | emacs23 | english | ep | erasmus | erasmusplus | ess | eu | excel | exif | exiftool | f11 | fc | fc11 | fc15 | fc29 | fc5 | fc8 | fedora | fedora21 | fenix | ffmpeg | finepix | firefox | flickr | folau | fontforge | fontspec | fonty | food | fop | foto | france | francja | fripp | froggit | fuczki | fuji | fuse | gammu | garmin | gawk | gazwyb | gdańsk | gdynia | gender | geo | geocoding | georgia | gft | git | github | gmail | gmaps | gnokii | gnus | google | googlecl | googleearth | googlemaps | gotowanie | gphoto | gphoto2 | gps | gpsbabel | gpsphoto | gpx | gpx-viewer | greasemonkey | gruzja | grzyby | haldaemon | handbrake | hhi | historia | history | hitler | holocaust | holokaust | hpmini | humour | iblue747 | ical | iiyama | ikea | imap | inkscape | inne | internet | j10i2 | javascript | jhead | k800i | kajak | kamera | karob | kleinertest | kml | kmobiletools | knuth | kociewie kołem | kod | kolibki | komorowski | konwersja | krutynia | kuchnia | kurski | latex | latex2rtf | latex3 | lcd | legend | lenny | lesund | lewactwo | lgbt-folly | liberation | linksys | linux | lisp | lisrel | litwa | lizbona | logika | ltr | lubowla | lwp | lwów | m2wś | malta | mapquest | mapsource | marchew | marvell | math | mathjax | mazury | mbank | mediolan | mencoder | mevo | mh17 | michalak | michlmayr | microsoft | monitor | mp4box | mplayer | ms | msc | mssql | msw | mswindows | mtkbabel | museum | muzyka | mymaps | mysql | nafisa | nanopi | natbib | navin | nekrolog | neo | neopi | netbook | niemcy | niemieckie zbrodnie | nikon | nmea | nowazelandia | nuc | nxml | oauth | oauth2 | obituary | odessa | okular | olympus | ooffice | ooxml | opera | osm | otf | otftotfm | other | overclocking | ozbekiston | panoramio | paryż | pdf | pdfpages | pdftex | pdftk | pedophilia | perl | photo | photography | picasa | picasaweb | pim | pine | pis | pit | plotly | pls | plugin | po | podróże | pogoda | politics | polityka | polsat | portugalia | postęp | powerpoint | połtawa | prelink | problem | propaganda | pstoedit | putin | python | pywws | r | radio | random | raspberry | raspberry pi | raspberrypi | refugees | relaxng | ridley | router | rower | rowery | rpi | rsync | rtf | ruby | rugby | rumunia | russia | rwc | rwc2007 | rwc2011 | rwc2019 | rzym | samba | selenium | sem | sernik | sheevaplug | sienkiewicz | signature | sks | skype | skytraq | smoleńsk | sqlite | srtm | sshfs | ssl | staszek wawrykiewicz | statistics | stats | statystyka | stix | stretch | suwałki | svg | svn | swanetia | swornegacie | szwajcaria | słowacja | tbilisi | terrorism | tex | texgyre | texlive | thunderbird | tomato | totalnaopozycja | tourism | tramp | trang | transylwania | truetype | ttf | turcja | turkey | turystyka | tusk | tv | tv5monde | twitter | typetools | ubuntu | uchodźcy | udev | ue | ukraina | umap | unix | upc | updmap | ups | utf8 | uzbekistan | varia | video | vienna | virb edit | vostro | wammu | wdc | wdfs | weather | weathercloud | webcam | webdav | webscrapping | weewx | wh2080 | wiedeń | wikicommons | wilno | win10 | windows | windows8 | wine | wioślarstwo | word | wordpress | wrt54gl | ws1080 | wtyczka | wunderground | ww2 | www | wybory | wybory2015 | włochy | węgry | xemex | xetex | xft | xhtml | xine | xml | xmllint | xsd | xslt | xvidtune | youtube | yum | zakopane | zakupy | zdf | zdrowie | łeba | świdnica | żywność
Archiwum
O stronie
wykorzystywany jest blosxom plus następujące wtyczki: tagging, flatarchives, rss10, lastbuilddatexhtmlmime. Niektóre musiałem dopasować nieco do swoich potrzeb. Więcej o blosxom jest tutaj
Subskrypcja
RSS 1.0
Żuławy wKoło 2017 -- podsumowanie



Podsumowanie wyników dla lat 2015--2017

z16 <- read.csv("wyniki_zulawy_2016_D.csv", sep = ';',  header=T, na.string="NA", dec=",");
aggregate (z16$time, list(Numer = z16$dist), summary)
z16$year <- 2016;

z15 <- read.csv("wyniki_zulawy_2015_D.csv", sep = ';',  header=T, na.string="NA", dec=",");
aggregate (z15$time, list(Numer = z15$dist), summary)
z15$year <- 2015;

z17 <- read.csv("wyniki_zulawy_2017_D.csv", sep = ';',  header=T, na.string="NA", dec=".");
aggregate (z17$time, list(Numer = z17$dist), summary)
z17$year <- 2017;

zz15 <- z15[, c("dist", "kmH", "time", "year")];
zz16 <- z16[, c("dist", "kmH", "time", "year")];
zz17  <- z17[, c("dist", "kmH", "time", "year")];

zz <- rbind (zz15, zz16, zz17);

## tylko dystans 140
zz140 <- subset (zz, ( dist == 140 ));
sum140 <- aggregate (zz140$kmH, list(Numer = zz140$year), summary)

boxplot (kmH ~ year, zz140, ylab = "Śr.prędkość [kmh]", col = "yellow", main="140km" )

## tylko dystans 55
zz75 <- subset (zz, ( dist > 60 & dist < 90 ));
sum75 <- aggregate (zz75$kmH, list(Numer = zz75$year), summary)
sum75
boxplot (kmH ~ year, zz75, ylab = "Śr.prędkość [kmh]", col = "yellow", main="80/75km" )

## tylko dystans 55
zz55 <- subset (zz, ( dist < 60 ));
sum55 <- aggregate (zz55$kmH, list(Numer = zz55$year), summary)
sum55
xl <- paste ("średnie 2015=", sum55$x[1,4], "kmh   2016=",
  sum55$x[2,4], "kmh   2017=", sum55$x[3,4], " kmh")

  boxplot (kmH ~ year, zz55, xlab = xl,
  ylab = "Śr.prędkość [kmh]", col = "yellow", main="55km" )

A ja (numer 418) byłem 70 w kategorii 140 km, z czasem 5:42:03 co dało 24,56 kmh przeciętną. Do pierwszego bufetu się spinałem, potem już nie...

url | Thu, 28/09/2017 05:17 | tagi: , ,
Przed Żuławy w Koło 2017

Jutro planuję przejechać 140km biorąc udział w imprezie pn. Żuławy wKoło 2017. Niby Żuławy a profil trasy sugeruje jakieś istotne wzniesienie w okolicach 25--40km:

Uważnie przyjrzenie się liczbom (zwłaszcza na osi OY) pozwala stwierdzić, że jest to złudzenie, wynikające z różnicy w jednostkach miary obu osi (kilometry vs metry). W rzeczywistości góra tam jest symboliczna o czym można się przekonać robiąc wykres nachyleń. Żeby pozbyć się przypadkowych błędów związanych z niedokładnością pomiaru oryginalne 673 punktowe dane zostały zmienione na 111 punktowe (uśrednienie minimum 1 km) lub 62 punktowe (uśrednienie minimum 2 km).

Przy czym uśrednienie minimum $x$ oznacza obliczenie nachylenia dla najkrótszego odcinka kolejnych $n$ punktów z oryginalnego śladu GPX, który będzie dłuższy niż $x$.

Skrypty R/dane są tutaj. Oryginale ślady GPX/TCX skopiowane ze strony ŻwK są tutaj.

url | Sat, 23/09/2017 17:02 | tagi: , , ,
Żuławy w koło 2016

Żuławy w koło to maraton rowerowy (czyli przejazd rowerem na dłuższym dystansie -- nie mylić z wyścigiem) organizowany od paru lat na Żuławach jak nazwa wskazuje. Sprawdziłem jak ta impreza wyglądała pod kątem prędkości w roku 2016. W tym celu ze strony Wyniki żUŁAWY wKOŁO 2016 ściągnąłem stosowny plik PDF z danymi, który następnie skonwertowałem do pliku w formacie XLS (Excel) wykorzystując konwerter on-line tajemniczej firmy convertio.pl. Tajemniczej w tym sensie, że nie znalazłem informacji kto i po co tą usługę świadczy.

Konwersja (do formatu CSV) -- jak to zwykle konwersja -- nie poszła na 100% poprawnie i wymagała jeszcze circa 30 minutowej ręcznej obróbki. Być może zresztą są lepsze konwertery, ale problem był z gatunku banalnych i wolałem stracić 30 minut na poprawianiu wyników konwersji niż 2 godziny na ustalaniu, który z konwerterów on-line konwertuje ten konkretny plik PDF (w miarę) bezbłędnie.

Po konwersji wypadało by sprawdzić (chociaż zgrubnie) czy wszystko jest OK.

## Czy każdy wiersz zawieraja 9 pól (powinien)
$ awk -F ';' 'NF != 9 {print NR, NF}' wyniki_zulawy_2016S.csv

## Ilu było uczestników na dystansie 140km?
$ awk -F ';' '$7 ==140 {print $0}' wyniki_zulawy_2016S.csv | wc -l
133

## Ilu było wszystkich (winno być 567 + 1 nagłówek)
$ cat wyniki_zulawy_2016S.csv | wc -l
568 # ok!

Przykładowy wykres pudełkowy

Do analizy statystycznej wykorzystano wykres pudełkowy (porównanie wyników na różnych dystansach) oraz histogram (rozkład średnich prędkości na dystansie 140km). BTW gdyby ktoś nie wiedział co to jest wykres pudełkowy to wyjaśnienie jest na rysunku obok. Objaśnienie: Me, $Q_1$, $Q_3$ to odpowiednio mediana i kwartyle. Dolna/górna krawędź prostokąta wyznacza zatem rozstęp kwartylny (IQR). Wąsy ($W_L$/$W_U$) są wyznaczane jako 150% wartości rozstępu kwartylnego. Wartości leżące poza ,,wąsami'' (nietypowe) są oznaczane kółkami.

Ww. wykresy wygenerowano następującym skryptem:

#
co <- "Żuławy wKoło 2016"
#
z <- read.csv("wyniki_zulawy_2016_C.csv", sep = ';',
  header=T, na.string="NA", dec=",");

aggregate (z$meanv, list(Numer = z$dist), fivenum)

boxplot (meanv ~ dist, z, xlab = "Dystans [km]",
    ylab = "Śr.prędkość [kmh]", col = "yellow", main=co )

## tylko dystans 140
z140 <- subset (z, ( dist == 140 ));

## statystyki zbiorcze
s140 <- summary(z140$meanv)
names(s140)

summary_label <- paste (sep='', "Średnia = ", s140[["Mean"]], 
  "\nMediana = ", s140[["Median"]],
  "\nQ1 = ", s140[["1st Qu."]],  "\nQ3 = ", s140[["3rd Qu."]],
  "\n\nMax = ", s140[["Max."]] )
# drukuje wartości kolumny meanv
# z140$meanv
# drukuje wartości statystyk zbiorczych
s140

# wykres słupkowy
h <- hist(z140$meanv, breaks=c(14,18,22,26,30,34,38), freq=TRUE, 
   col="orange", 
   main=paste (co, "[140km]"), # tytuł
   xlab="Prędkość [kmh]",ylab="L.kolarzy", labels=T, xaxt='n' )
# xaxt usuwa domyślną oś 
# axis definiuje lepiej oś OX
axis(side=1, at=c(14,18,22,26,30,34,38))
text(38, 37, summary_label, cex = .8, adj=c(1,1) )

Dane i wyniki są tutaj

url | Mon, 11/09/2017 08:10 | tagi: , , ,
O lepsze liczenie kadencji

Informacja o kadencji jest w pliku .tcx rejestrowana (przez GarminaEdge 500) w następujący sposób:

  <Activities>
    <Activity Sport="Biking">
      <Lap StartTime="2017-08-31T14:51:16Z">
	<Cadence>77</Cadence>
	...
	<Track>
  	  <Trackpoint>
             <Time>2017-08-31T14:52:02Z</Time>
             <Cadence>0</Cadence>
          </Trackpoint>
	  <Trackpoint>
	     <Time>2017-08-31T14:52:06Z</Time>
	     <Cadence>46</Cadence>
	  </Trackpoint>
	  <Trackpoint>
            <Time>2017-08-31T14:52:07Z</Time>
	    <Cadence>53</Cadence>
	  </Trackpoint>

Lap-ów może być wiele. Każdy zawiera element Track, w którym zapisana jest informacja o fragmencie trasy. Pomiędzy LapTrack znajduje się nagłówek zawierający różne obliczone/zbiorcze informacje, m.in. średnią kadencję. Co by oznaczało, że w powyższym przykładzie średnia kadencja wynosiła 77 obrotów/min. Na odcinku od 14:52:02Z do 14:52:06Z (4 sekundy) średnia kadencja wyniosła 46 o/min, zaś na odcinku 14:52:06Z--07Z (1s) 53 o/min.

Można też policzyć kadencję samodzielnie, co pozwoli na uzyskanie dodatkowej informacji. Liczenie kadencji jest proste. Mnożąc średnią kadencję odcinka razy czas w sekundach otrzymamy liczbę obrotów korby na tym odcinku (kadencję należy podzielić przez 60 bo jest podana w minutach). Odcinki o zerowej kadencji pomija się (Garmin też tak oblicza BTW). Dzieląc łączną liczbę obrotów przez czas otrzymamy średnią kadencję. Ja dodałem opcję pomijania odcinków o pewnej minimalnej prędkości (np. 8 kmh) -- takie odcinki to zwykle jakieś nietypowe fragmenty, zaniżające tylko średnią.

#!/usr/bin/perl
# Cadence calculator for GarminEdge tcx files (or converted fits)
# tprzechlewski@gmail.com
use Getopt::Long;

my $prev_Time = -1;
my $parsingTrack ='N';
my $regCadence = 'N';
my $min_speed = -1; # minimum speed
my $trackNo=1;

GetOptions( "s=f"  => \$min_speed, );
$min_speedMS = ($min_speed *1000)/3600.0;

if ($min_speed > 0) {
  print "*** Segments with speed below $min_speed kmh skipped ***\n";
}

while (<>) {
  chomp();

  # Order is importany (first check for <Track>:
  if ( /<Track>/ ) {
    if ( $regCadence eq 'N' ) {
      ## Check if Cadence is registered
      print STDERR "*** No cadence registered! ***\n";
      exit 1;
    } else {
      $parsingTrack = 'Y' ; print STDERR "### Parsing track #$trackNo...\n";
      $trackNo++;
    }
  }

  # Parsing header:
  if ($parsingTrack eq 'N' ) {
    if ( /<TotalTimeSeconds>/) {
      $edge_LapTime = xmlEleValue('TotalTimeSeconds', $_) ; }
    elsif (/<Cadence>/) {
      $edge_LapCadence = xmlEleValue('Cadence', $_) ; $regCadence = 'Y' }
    elsif (/<DistanceMeters>/) {
      $edge_LapDist = xmlEleValue('DistanceMeters', $_) ;  }
    ##print STDERR "## Parsing track info: $_\n";
    next;
  }


  ## start parsing Track now:
  if (/Speed>/)  { $speed = xmlEleValue ('Speed', $_);
  } elsif ($_ =~ /<Time>(.*)T(.*)Z<\/Time>/ ) {
    $time = $2;
    ($h, $m, $s )  = split /:/, $time;
    $current_time = $h * 60 * 60  + $m * 60 + $s ;
    ##print STDERR "$current_time\n";
  } elsif (/<Cadence>/ ) {
    $cadence = xmlEleValue ('Cadence', $_);
  } elsif (/<DistanceMeters>/ ) { 
    $distance = xmlEleValue ('DistanceMeters', $_);
  } elsif ($_ =~ /<\/Trackpoint>/ ) {

    if ( $prevTime > 0 ) {## pomija pierwsze
      $lastTime = $current_time ;
      if ($cadence > 0 && $speed > $min_speedMS ) {
	$timeDiff = $current_time - $prevTime; 

	$total_time_cycled += $timeDiff ;
	$total_cycles += $cadence * $timeDiff / 60;

	$prevTime = $current_time ;
      }
      else {
	$timeDiff = $current_time - $prevTime; 

	$total_time_idle += $timeDiff ;

	$prevTime = $current_time ;
      }

    } else {
     $prevTime = $current_time ;
     $firstTime = $current_time ;
  }

 }

if ( /<\/Track>/ ) {
   $total_Time = $total_time_idle + $total_time_cycled;

   printf "Time = Idle: %d s Spinning: %d s Total: %d s\n", $total_time_idle,
      $total_time_cycled, $total_Time;
   printf "Time = Idle: %.2f%% Spinning: %.2f%% Total: %.2f%%\n",
      $total_time_idle/$total_Time *100,
      $total_time_cycled/$total_Time * 100, $total_Time/$total_Time *100;

   print "Rotations (total) = $total_cycles\n";
   print "Mean cadence (computed) = " . $total_cycles/$total_time_cycled * 60 . "\n";

   $totalTimeTime = $lastTime - $firstTime;

   print "Total time (last - first) = $totalTimeTime s\n";

  print "Registered (header) values: lap time: $edge_LapTime "
    . "cadence: $edge_LapCadence lap distance (m): $edge_LapDist\n";
  
  ## reset values ## ## ## 
  $grand_total_time_idle += $total_time_idle; 
  $grand_total_time_cycled  += $total_time_cycled ;
  $grand_total_cycles += $total_cycles ;
  $grand_total_Dist +=  $edge_LapDist;

  $total_time_idle = $total_time_cycled = $total_cycles = 0 ;
  $prev_Time = -1; 
  $parsingTrack ='N';
  $regCadence = 'N';
}

} ##/while

## Grand Totals:
$trackNo--;
$grand_total_Time = $grand_total_time_idle + $grand_total_time_cycled;

print "====== Totals/means for $trackNo tracks =====\n";
printf "Time = Idle: %d s Spinning: %d s Total: %d s\n", $grand_total_time_idle,
      $grand_total_time_cycled, $grand_total_Time;
   printf "Time = Idle: %.2f%% Spinning: %.2f%% Total: %.2f%%\n",
      $grand_total_time_idle/$grand_total_Time *100,
      $grand_total_time_cycled/$grand_total_Time * 100, 
      $grand_total_Time/$grand_total_Time *100;
print "Rotations (total) = $grand_total_cycles\n";
print "Mean cadence (computed) = " . $grand_total_cycles/$grand_total_time_cycled * 60 .  "\n";
print "Total distance (registered): $grand_total_Dist (m)\n";

## ### ### ### ### ###

sub xmlEleValue {
  my $en = shift; # element name
  my $el = shift; # line

  $el =~ /<$en>(.*)<\/$en>/;

  return "$1";

}

Przykładowy wydruk dla pliku fit/tcx zawierającego trzy segmenty:

$ cadencecalc.pl 2017-08-31.tcx
### Parsing track #1...
Time = Idle: 552 s Spinning: 2082 s Total: 2634 s
Time = Idle: 20.96% Spinning: 79.04% Total: 100.00%
Rotations (total) = 2684.95
Mean cadence (computed) = 77.3760806916427
Total time (last - first) = 2634 s
Registered (header) values: lap time: 2438.06 cadence: 77 lap distance (m): 16339.2
### Parsing track #2...
Time = Idle: 80 s Spinning: 652 s Total: 732 s
Time = Idle: 10.93% Spinning: 89.07% Total: 100.00%
Rotations (total) = 860.716666666666
Mean cadence (computed) = 79.2070552147239
Total time (last - first) = 3366 s
Registered (header) values: lap time: 731.05 cadence: 79 lap distance (m): 4994.82
### Parsing track #3...
Time = Idle: 29 s Spinning: 105 s Total: 134 s
Time = Idle: 21.64% Spinning: 78.36% Total: 100.00%
Rotations (total) = 120.716666666667
Mean cadence (computed) = 68.9809523809524
Total time (last - first) = 3500 s
Registered (header) values: lap time: 134.378 cadence: 69 lap distance (m): 761.78
====== Totals/means for 3 tracks =====
Time = Idle: 661 s Spinning: 2839 s Total: 3500 s
Time = Idle: 18.89% Spinning: 81.11% Total: 100.00%
Rotations (total) = 3666.38333333333
Mean cadence (computed) = 77.4860866502289
Total distance (registered): 22095.8 (m)

Podając jako minimalną prędkość 9 km/h otrzymamy:

$ cadencecalc.pl -s 9 2017-08-31.tcx
*** Segments with speed below 9 kmh skipped ***
### Parsing track #1...
Time = Idle: 654 s Spinning: 1980 s Total: 2634 s
Time = Idle: 24.83% Spinning: 75.17% Total: 100.00%
Rotations (total) = 2582.11666666667
Mean cadence (computed) = 78.2459595959596
Total time (last - first) = 2634 s
Registered (header) values: lap time: 2438.06 cadence: 77 lap distance (m): 16339.2
### Parsing track #2...
Time = Idle: 80 s Spinning: 652 s Total: 732 s
Time = Idle: 10.93% Spinning: 89.07% Total: 100.00%
Rotations (total) = 860.716666666666
Mean cadence (computed) = 79.2070552147239
Total time (last - first) = 3366 s
Registered (header) values: lap time: 731.05 cadence: 79 lap distance (m): 4994.82
### Parsing track #3...
Time = Idle: 29 s Spinning: 105 s Total: 134 s
Time = Idle: 21.64% Spinning: 78.36% Total: 100.00%
Rotations (total) = 120.716666666667
Mean cadence (computed) = 68.9809523809524
Total time (last - first) = 3500 s
Registered (header) values: lap time: 134.378 cadence: 69 lap distance (m): 761.78
====== Totals/means for 3 tracks =====
Time = Idle: 763 s Spinning: 2737 s Total: 3500 s
Time = Idle: 21.80% Spinning: 78.20% Total: 100.00%
Rotations (total) = 3563.55
Mean cadence (computed) = 78.1194738765071
Total distance (registered): 22095.8 (m)

Idle oznacza czas w którym nie kręcimy i/lub jedziemy poniżej prędkości minimalnej (podawany jest czas w sekundach i udział w całości). Rotations to liczba obrotów. Obliczone wartości wyglądają na prawidłowe na co wskazywałoby, że są bliskie wartościom liczonym przez Garmina.

url | Wed, 06/09/2017 11:09 | tagi: