$ git clone http://gcodetool.ion.nu/gcodetool.git
commit 3ed10fd7abfa1093e000e7ed78e116210651d98c
Author: Alicia <...>
Date:   Wed Sep 25 23:44:39 2019 +0200

    Added speed analyzis (print, travel, retract) and retraction counting to the 'info' subcommand.

diff --git a/info.c b/info.c
index cff5b44..d450b3d 100644
--- a/info.c
+++ b/info.c
@@ -56,6 +56,11 @@ int gtool_info(const char* filename)
   struct minmax e={nan(""),nan(""),0,0};
   struct minmax emove={nan(""),nan(""),0,0}; // Printing moves
   struct minmax retraction={nan(""),nan(""),0,1};
+  struct minmax retractspeed={nan(""),nan(""),0,0};
+  struct minmax printspeed={nan(""),nan(""),0,0};
+  struct minmax travelspeed={nan(""),nan(""),0,0};
+  unsigned int retractcount=0;
+  double frate=0;
   const struct gcommand* cmd;
   double zpos=nan("");
   char tty=isatty(fileno(stdout));
@@ -74,7 +79,9 @@ int gtool_info(const char* filename)
       double yval=gcommand_getparam(cmd, 'Y');
       double zval=gcommand_getparam(cmd, 'Z');
       double eval=gcommand_getparam(cmd, 'E');
+      double fval=gcommand_getparam(cmd, 'F');
       char move=(!isnan(xval) || !isnan(yval));
+      if(!isnan(fval)){frate=fval;}
       if(!isnan(zval)){zpos=zval;} // Take things like 'lift head' into account, don't treat as Z until there's a positive E
       // Printing line length
       if(!isnan(eval) && move && eval>0)
@@ -90,6 +97,12 @@ int gtool_info(const char* filename)
           minmax(&layer, zpos);
           zpos=nan("");
         }
+        // Print speed
+        minmax(&printspeed, frate);
+      }
+      else if(move) // Travel speed
+      {
+        minmax(&travelspeed, frate);
       }
       // Basic XYZ
       if(!isnan(xval)){minmax(&x, xval);}
@@ -100,11 +113,12 @@ int gtool_info(const char* filename)
         static double eval_=NAN; // Buffer to ignore the last E motion, which often isn't indicative of general retractions
         if(!isnan(eval_))
         {
-          // TODO: count number of retractions?
           minmax(&retraction, eval_);
+          ++retractcount;
         }
         eval_=eval;
         if(eval<e.last){retraction.last=e.last;} // Count retraction from the last extrusion position
+        minmax(&retractspeed, frate);
       }
       if(!isnan(eval)){minmax(&e, eval);}
     }
@@ -162,7 +176,11 @@ int gtool_info(const char* filename)
   printf("Minimum layer height: %.*fmm\n", pd(layer.min));
   printf("Retraction length: %.*fmm\n", pd(-retraction.min));
   printf("Un-retraction length: %.*fmm\n", pd(retraction.max));
+  printf("Number of retractions: %u\n", retractcount);
   printf("Filament use: %.*fmm\n", pd(e.max-e.min));
+  printf("Retraction speed: %.*fmm/s - %.*fmm/s\n", pd(retractspeed.min/60), pd(retractspeed.max/60));
+  printf("Print speed: %.*fmm/s - %.*fmm/s\n", pd(printspeed.min/60), pd(printspeed.max/60));
+  printf("Travel speed: %.*fmm/s - %.*fmm/s\n", pd(travelspeed.min/60), pd(travelspeed.max/60));
   free(bedtemps);
   free(tooltemps);
   return 0;