This shows you the differences between two versions of the page.
sdk:connection-statistics [2019/06/18 07:52] juraschek |
sdk:connection-statistics [2021/08/04 09:20] |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Connection Statistics ====== | ||
- | {{ :sdk:sweg.png |}} | ||
- | {{ :sdk:statistics.jpg |}} | ||
- | In many mobile use cases customers want to have additional information on network coverage, signal and connections quality, roaming and so on. | ||
- | This data can be generated using a SDK script such as ''logger.are''. It queries ''cli status'' periodically and logs the items you are interested in into a comma separated values file e.g.''/home/sdk/sandbox/logger/current.csv''. When the script restarts or when a configurable timeout is reached, a new file is written. | ||
- | A second script will upload the log files in the working directory from time to time to a FTP server. | ||
- | Once having the data on the server, it can be evaluated, e.g. by drawing it onto a map or analyse it with Excel or NI DIAdem. | ||
- | |||
- | |||
- | ===== Logging Skript ===== | ||
- | |||
- | <code c logger.are> | ||
- | /* DESC: A script that can be used for a longtime logging of a NetModule Router. | ||
- | * The upper part of the script is the logger template. The lower part is the real programm, where you describe the settings and workflow. | ||
- | * Copyright (C) 2014 NetModule AG, Switzerland | ||
- | */ | ||
- | | ||
- | template logger { | ||
- | workfile="current.csv"; | ||
- | serialNumber=nb_status('system').SERIAL_NUMBER; | ||
- | currentFilename=""; | ||
- | currentLogStart=0; // the current logfile was startet at this time | ||
- | intervall=5; // new logentry every # seconds | ||
- | logfileIntervall=3600; // new logfile every # seconds | ||
- | // due to the fact that we cannot see the amount of free space in the sdk | ||
- | // we need to improvise and add up the current filesizes | ||
- | currentLogSize=0; // in Bytes | ||
- | maxLogSize=15000000; // in Bytes | ||
- | freespace=1000000; // in Bytes | ||
- | # give the constructor the absolute path to store the Loggs into | ||
- | void logger(string path) { | ||
- | dh=opendir(path); | ||
- | if(dh == -1) { | ||
- | if ( mkdir(path,0666) == -1 ); { | ||
- | nb_syslog("Could not create dir: %s. Exiting",path); | ||
- | exit(1); | ||
- | } | ||
- | } else { | ||
- | nb_syslog("directory already existent: %s", path); | ||
- | closedir(dh); | ||
- | } | ||
- | this.path = path; | ||
- | this.fields = mkarray(); | ||
- | namepart=strftime("%Y%m%d_%H%M%S_start.csv",localtime(time())); | ||
- | this.currentFilename=sprintf("%s_%s",this.serialNumber, namepart); | ||
- | } | ||
- | |||
- | int jiffy() { | ||
- | sys = sysinfo(); | ||
- | u = struct_get(sys, "uptime"); | ||
- | |||
- | if (is_void(u) || u < 1) { | ||
- | return 0; | ||
- | } else { | ||
- | return u; | ||
- | } | ||
- | } | ||
- | |||
- | int addField(string fieldname) { | ||
- | if (strlen(fieldname) > 0 ) { | ||
- | this.fields = array_merge(this.fields, fieldname); | ||
- | return 0; | ||
- | } else { | ||
- | nb_syslog("Fieldname to short, not adding"); | ||
- | return -1; | ||
- | } | ||
- | } | ||
- | |||
- | int addFields(array localFields) { | ||
- | for (i=0; i < length(localFields); i++) { | ||
- | if (this.addField(localFields[i]) == -1 ) return -1; | ||
- | } | ||
- | return 0; | ||
- | |||
- | } | ||
- | |||
- | int newLogfile() { | ||
- | // check if file if we have a work file an rename it to currentFile | ||
- | fd=fopen(sprintf("%s%s", this.path,this.workfile),"r" ); | ||
- | if(fd) { | ||
- | // we have a current workfile lets rename it | ||
- | fclose(fd); | ||
- | if(!rename(sprintf("%s%s",this.path,this.workfile), sprintf("%s%s",this.path,this.currentFilename))){ | ||
- | nb_syslog("Could not rename %s to %s, %s will be deleted",this.workfile, this.currentFilename,this.workfile); | ||
- | remove(sprintf("%s%s",this.path,this.workfile)); | ||
- | } | ||
- | } | ||
- | //set new current Filename as we stored the old one away | ||
- | namepart=strftime("%Y%m%d_%H%M%S.csv",localtime(time())); | ||
- | this.currentFilename=sprintf("%s_%s",this.serialNumber,namepart); | ||
- | this.currentLogStart=this.jiffy(); | ||
- | //creating Header | ||
- | header=""; | ||
- | for (i=0; i < length(this.fields); i++) { | ||
- | header=sprintf('%s%s;',header,this.fields[i]); | ||
- | } | ||
- | |||
- | if(this.addLineToFile(header) == -1 ) { | ||
- | nb_syslog("writing of header to file: %s%s:", this.path,this.workfile); | ||
- | return -1; | ||
- | } else { | ||
- | return 0; | ||
- | } | ||
- | } | ||
- | |||
- | int addLineToFile(string line) { | ||
- | fd=fopen(sprintf("%s%s", this.path,this.workfile),"a" ); | ||
- | if (fd < 0 ) { | ||
- | nb_syslog("could not open file %s%s", this.path, this.workfile); | ||
- | } | ||
- | | ||
- | if(!fwrite(fd,sprintf("%s\n",line))) { | ||
- | nb_syslog("writing of line to file: %s%s:", this.path,this.workfile); | ||
- | fclose(fd); | ||
- | return -1; | ||
- | } else { | ||
- | fclose(fd); | ||
- | return 0; | ||
- | } | ||
- | } | ||
- | |||
- | int filesize(string file) { | ||
- | fd=fopen(file,"r"); | ||
- | if (fd) { | ||
- | fseek(fd,-1); | ||
- | size=ftell(fd); | ||
- | fclose(fd); | ||
- | return size; | ||
- | } else { | ||
- | return -1; | ||
- | } | ||
- | } | ||
- | |||
- | |||
- | int logNow() { | ||
- | //get current status output | ||
- | sections=mkarray("system","wan","wwan","wlan","gnss","lan","openvpn","ipsec","dio","license", "hotspot"); | ||
- | status=mkstruct(); | ||
- | for (i=0;i<length(sections);i++) { | ||
- | status=struct_merge(status,nb_status(sections[i])); | ||
- | } | ||
- | | ||
- | line=""; | ||
- | for (i=0; i < length(this.fields); i++) { | ||
- | value=struct_get(status,this.fields[i]); | ||
- | if (strlen(value) == 0) { | ||
- | value=sprintf("n/a"); | ||
- | } | ||
- | line=sprintf('%s%s;',line,value); | ||
- | } | ||
- | |||
- | if(this.addLineToFile(line) == -1 ) { | ||
- | nb_syslog("writing of line to file failed: %s%s:", this.path,this.workfile); | ||
- | return -1; | ||
- | } else { | ||
- | return 0; | ||
- | } | ||
- | } | ||
- | |||
- | int logRotate() { | ||
- | // get all files in path | ||
- | nb_syslog("Starting Logrotate"); | ||
- | files=mkarray(); | ||
- | handle = opendir(this.path); | ||
- | if (handle != -1) { | ||
- | while ((entry = readdir(handle))) { | ||
- | if (entry == ".") continue; | ||
- | if (entry == "..") continue; | ||
- | if (entry == this.workfile) continue; | ||
- | files=array_merge(files,entry); | ||
- | } | ||
- | closedir(handle); | ||
- | } else { | ||
- | nb_syslog("cannot open directory %s", this.path); | ||
- | } | ||
- | // sort old to new | ||
- | qsort(files); | ||
- | // files=array_reverse(files); | ||
- | // get current filesizes | ||
- | this.currenLogSize=0; | ||
- | sizes=mkarray(); | ||
- | for(i=0;i<length(files);i++){ | ||
- | size[i]=this.filesize(sprintf("%s%s",this.path,files[i])); | ||
- | this.currentLogSize=this.currentLogSize+size[i]; | ||
- | } | ||
- | nb_syslog("Current Logsize: %i byte",this.currentLogSize); | ||
- | //if we don't have enough space delete enough old ones to have enough space | ||
- | return 0; | ||
- | } | ||
- | } | ||
- | // ###################################################### | ||
- | // ###################################################### | ||
- | // ###################################################### | ||
- | |||
- | l = new logger("/logger/"); | ||
- | // Timestamp of the Logentry | ||
- | l.addField("SYSTEM_TIME"); | ||
- | |||
- | |||
- | // GPS Fields | ||
- | l.addField("GNSS1_LONGITUDE"); | ||
- | l.addField("GNSS1_LATITUDE"); | ||
- | l.addField("GNSS1_SATELLITES_INVIEW"); | ||
- | |||
- | |||
- | // LTE Connection Fields | ||
- | // Can be cloneed with MOBILE2 in case more LTE Connections are used | ||
- | // Arbitrary Strength Unit (ASU) https://en.wikipedia.org/wiki/Mobile_phone_signal#ASU | ||
- | l.addField("MOBILE1_SIGNAL_LEVEL"); | ||
- | // received signal code power https://en.wikipedia.org/wiki/Received_signal_code_power | ||
- | l.addField("MOBILE1_SIGNAL_RSCP"); | ||
- | // Received signal strength indication https://en.wikipedia.org/wiki/Received_signal_strength_indication | ||
- | l.addField("MOBILE1_SIGNAL_RSSI"); | ||
- | // Service Type: LTE/HSPA/3G/2G/EDGE | ||
- | l.addField("MOBILE1_SERVICE_TYPE"); | ||
- | // NETWORK ID of the LTE Network we are connected to | ||
- | l.addField("MOBILE1_LAI"); | ||
- | // Area Code in LTE Network | ||
- | l.addField("MOBILE1_LAC"); | ||
- | // Cell ID we are connected to | ||
- | l.addField("MOBILE1_CID"); | ||
- | // Is the SIM registered in Home or Roaming | ||
- | l.addField("MOBILE1_REGISTRATION_STATE"); | ||
- | |||
- | |||
- | // WAN Data Connection field | ||
- | // Connection that is current default Gateway | ||
- | l.addField("WAN_HOTLINK"); | ||
- | // Per WANLINK/LTE Connection: | ||
- | // Current State: up/down/dialin | ||
- | l.addField("WANLINK1_STATE"); | ||
- | // Current upload and download rates | ||
- | l.addField("WANLINK1_DOWNLOAD_RATE"); | ||
- | l.addField("WANLINK1_UPLOAD_RATE"); | ||
- | // Bytes that are already down- or uploaded. | ||
- | l.addField("WANLINK1_DATA_DOWNLOADED"); | ||
- | l.addField("WANLINK1_DATA_UPLOADED"); | ||
- | |||
- | // WLAN Fields | ||
- | // WLAN Clients Attached to the Accesspoint | ||
- | l.addField("WLAN1_STATION_COUNT"); | ||
- | |||
- | l.newLogfile(); | ||
- | l.intervall=5; // log every 5sec | ||
- | l.logfileIntervall=3600; // create a new file every 5 min | ||
- | |||
- | // ###################################################### | ||
- | // ###################################################### | ||
- | // ###################################################### | ||
- | // ###################################################### | ||
- | |||
- | |||
- | while (true) { | ||
- | l.logNow(); | ||
- | sleep(l.intervall); | ||
- | // lets see if we need a new logfile | ||
- | if (l.currentLogStart+l.logfileIntervall<l.jiffy()) { | ||
- | l.newLogfile(); | ||
- | l.logRotate(); | ||
- | } | ||
- | } | ||
- | exit(0); | ||
- | </code> | ||
- | |||
- | <code csv log-data.csv> | ||
- | SYSTEM_TIME;GNSS1_LONGITUDE;GNSS1_LATITUDE;GNSS1_SATELLITES_INVIEW;MOBILE1_SIGNAL;MOBILE1_SERVICE_TYPE;MOBILE1_LAI;MOBILE1_LAC;MOBILE1_CID;WAN_HOTLINK;WANLINK1_STATE;WANLINK1_DOWNLOAD_RATE;WANLINK1_UPLOAD_RATE;WANLINK1_DATA_DOWNLOADED;WANLINK1_DATA_UPLOADED;WLAN1_STATION_COUNT; | ||
- | 2015-03-17 11:50:12;n/a;n/a;n/a;-87;UMTS;22803;1B5F;216C6;WANLINK1;up;168;219;28560577;29223265;0; | ||
- | 2015-03-17 11:50:19;n/a;n/a;n/a;-87;UMTS;22803;1B5F;216C6;WANLINK1;up;168;219;28560577;29223265;0; | ||
- | 2015-03-17 11:50:25;n/a;n/a;n/a;-87;UMTS;22803;1B5F;216C6;WANLINK1;up;168;219;28560577;29223265;0; | ||
- | 2015-03-17 11:50:31;n/a;n/a;n/a;-87;UMTS;22803;1B5F;216C6;WANLINK1;up;168;219;28560577;29223265;0; | ||
- | 2015-03-17 11:50:36;n/a;n/a;n/a;-87;UMTS;22803;1B5F;216C6;WANLINK1;up;168;219;28560577;29223265;0; | ||
- | 2015-03-17 11:50:43;n/a;n/a;n/a;-87;UMTS;22803;1B5F;216C6;WANLINK1;up;168;219;28560577;29223265;0; | ||
- | 2015-03-17 11:50:49;n/a;n/a;n/a;-87;UMTS;22803;1B5F;216C6;WANLINK1;up;168;219;28560577;29223265;0; | ||
- | 2015-03-17 11:50:55;n/a;n/a;n/a;-87;UMTS;22803;1B5F;216C6;WANLINK1;up;168;219;28560577;29223265;0; | ||
- | 2015-03-17 11:51:01;n/a;n/a;n/a;-87;UMTS;22803;1B5F;216C6;WANLINK1;up;168;219;28560577;29223265;0; | ||
- | 2015-03-17 11:51:11;n/a;n/a;n/a;-87;UMTS;22803;1B5F;216C6;WANLINK1;up;0;0;28562684;29225261;0; | ||
- | 2015-03-17 11:51:17;n/a;n/a;n/a;-87;UMTS;22803;1B5F;216C6;WANLINK1;up;0;0;28562684;29225261;0; | ||
- | 2015-03-17 11:51:23;n/a;n/a;n/a;-87;UMTS;22803;1B5F;2B306;WANLINK1;up;0;0;28562684;29225261;0; | ||
- | 2015-03-17 11:51:29;n/a;n/a;n/a;-87;UMTS;22803;1B5F;2B306;WANLINK1;up;0;0;28562684;29225261;0; | ||
- | 2015-03-17 11:51:35;n/a;n/a;n/a;-87;UMTS;22803;1B5F;2B306;WANLINK1;up;0;0;28562684;29225261;0; | ||
- | 2015-03-17 11:51:41;n/a;n/a;n/a;-87;UMTS;22803;1B5F;2B306;WANLINK1;up;0;0;28562684;29225261;0; | ||
- | 2015-03-17 11:51:47;n/a;n/a;n/a;-87;UMTS;22803;1B5F;2B306;WANLINK1;up;0;0;28562684;29225261;0; | ||
- | 2015-03-17 11:51:53;n/a;n/a;n/a;-87;UMTS;22803;1B5F;2B306;WANLINK1;up;0;0;28562684;29225261;0; | ||
- | 2015-03-17 11:51:59;n/a;n/a;n/a;-87;UMTS;22803;1B5F;2B306;WANLINK1;up;0;0;28562684;29225261;0; | ||
- | 2015-03-17 11:52:05;n/a;n/a;n/a;-87;UMTS;22803;1B5F;2B306;WANLINK1;up;0;0;28562684;29225261;0; | ||
- | </code> | ||
- | |||
- | |||
- | ===== Skript to periodcly upload the csv Files ===== | ||
- | |||
- | <code c uploader.are> | ||
- | /* desc: This script uploaed all files in a directory (exception can be specified) to a ftp server. | ||
- | * copyright (c) 2014 netmodule ag, switzerland | ||
- | */ | ||
- | |||
- | |||
- | template uploader { | ||
- | workfile="current.csv"; | ||
- | path=""; | ||
- | removefilesafterupload=false; | ||
- | server=""; | ||
- | serverpath="/"; | ||
- | user=""; | ||
- | password=""; | ||
- | |||
- | void uploader(string path) { | ||
- | dh=opendir(path); | ||
- | if(dh == -1) { | ||
- | nb_syslog("path %s not existent, exiting",path); | ||
- | exit(1); | ||
- | } else { | ||
- | nb_syslog("uploader path: %s", path); | ||
- | closedir(dh); | ||
- | } | ||
- | this.path = path; | ||
- | } | ||
- | |||
- | int uploadfiles() { | ||
- | nb_syslog("starting uploading"); | ||
- | files=mkarray(); | ||
- | handle = opendir(this.path); | ||
- | if (handle != -1) { | ||
- | while ((entry = readdir(handle))) { | ||
- | if (entry == ".") continue; | ||
- | if (entry == "..") continue; | ||
- | if (entry == this.workfile) continue; | ||
- | files=array_merge(files,entry); | ||
- | } | ||
- | closedir(handle); | ||
- | } else { | ||
- | nb_syslog("cannot open directory %s", this.path); | ||
- | } | ||
- | // sort old to new | ||
- | qsort(files); | ||
- | // get current filesizes | ||
- | dump(files); | ||
- | for(i=0;i<length(files);i++){ | ||
- | // lets upload every file | ||
- | if (nb_transfer_put(this.user, this.password, sprintf("%s/%s/%s", this.server, this.serverpath,files[i]), sprintf("%s%s",this.path,files[i])) == 0) { | ||
- | nb_syslog("stored '%s%s' on remote site", this.path, files[i]); | ||
- | if(this.removefilesafterupload) { | ||
- | if(remove(sprintf("%s%s",this.path,files[i]))) { | ||
- | nb_syslog("file %s%s removed after upload", this.path, files[i]); | ||
- | } else { | ||
- | nb_syslog("file %s%s can't be removed after upload", this.path, files[i]); | ||
- | } | ||
- | } | ||
- | } else { | ||
- | nb_syslog("upload of '%s%s' was not successfull ", this.path, files[i]); | ||
- | } | ||
- | } | ||
- | return 0; | ||
- | } | ||
- | } // of template uploader | ||
- | |||
- | |||
- | ########### The real program ############### | ||
- | |||
- | u = new uploader("/logger/"); | ||
- | u.server="ftp://share.netmodule.com/router/public"; | ||
- | u.serverpath="/logger/"; | ||
- | u.user="*******"; | ||
- | u.password="*******"; | ||
- | u.removefilesafterupload=true; | ||
- | u.uploadfiles(); | ||
- | </code> | ||
- | |||
- | |||
- | |||
- | Example Python script to visualize the Data: | ||
- | |||
- | |||
- | |||
- | <code python scatter.py> | ||
- | from mpl_toolkits.basemap import Basemap | ||
- | import matplotlib.pyplot as plt | ||
- | import matplotlib.pylab as pylab | ||
- | import numpy as np | ||
- | import string | ||
- | import matplotlib.cm as cm | ||
- | import csv | ||
- | from datetime import datetime as dt | ||
- | |||
- | |||
- | #2014-11-13 06:18:09 | ||
- | tf="%Y-%m-%d %H:%M:%S" | ||
- | startTime=dt.strptime("2015-02-05 06:00:00", tf) | ||
- | endTime=dt.strptime("2015-02-21 22:00:00", tf) | ||
- | |||
- | twogx=[] #longitudes | ||
- | twogy=[] #latitudes | ||
- | threegx=[] #longitudes | ||
- | threegy=[] #latitudes | ||
- | fourgx=[] #longitudes | ||
- | fourgy=[] #latitudes | ||
- | nagx=[] #longitudes | ||
- | nagy=[] #latitudes | ||
- | d1x=[] | ||
- | d1y=[] | ||
- | d2x=[] | ||
- | d2y=[] | ||
- | cy=[] | ||
- | cx=[] | ||
- | cities=[] | ||
- | |||
- | |||
- | |||
- | errorlist=[] | ||
- | maxlon=-180.0 | ||
- | minlon=180.0 | ||
- | maxlat=-85.0 | ||
- | minlat=85.0 | ||
- | |||
- | fi=open(r'data/<logfile>.log','r') | ||
- | csvlog=csv.DictReader(fi, delimiter=',') | ||
- | for row in csvlog: | ||
- | try: | ||
- | timestamp=dt.strptime(row["system_time"],tf) | ||
- | |||
- | if timestamp > startTime and timestamp < endTime: | ||
- | try: | ||
- | |||
- | stype=row["mobile1_service_type"] | ||
- | row_x=float(row["gnss1_longitude"]) | ||
- | row_y=float(row["gnss1_latidude"]) | ||
- | provider=row["mobile1_network"] | ||
- | if row_x == 0.0 or row_y == 0.0: | ||
- | raise NameError('NoGPsFix') | ||
- | if stype=="EDGE": | ||
- | twogx.append(row_x) | ||
- | twogy.append(row_y) | ||
- | elif stype=="HSPA+" or stype=="HSDPA" or stype=="UMTS": | ||
- | threegx.append(row_x) | ||
- | threegy.append(row_y) | ||
- | elif stype=="LTE": | ||
- | fourgx.append(row_x) | ||
- | fourgy.append(row_y) | ||
- | else: | ||
- | nagx.append(row_x) | ||
- | nagy.append(row_y) | ||
- | |||
- | if provider=="Vodafone.de": | ||
- | d2x.append(row_x) | ||
- | d2y.append(row_y) | ||
- | elif provider=="Telekom.de": | ||
- | d1x.append(row_x) | ||
- | d1y.append(row_y) | ||
- | |||
- | |||
- | if row_x > maxlon : maxlon=row_x | ||
- | if row_x < minlon : minlon=row_x | ||
- | if row_y > maxlat : maxlat=row_y | ||
- | if row_y < minlat : minlat=row_y | ||
- | except: | ||
- | errorlist.append(row["system_time"]) | ||
- | except: | ||
- | errorlist.append(row["system_time"]) | ||
- | fi.close() | ||
- | |||
- | |||
- | fi=open(r'data/cities.csv','r') | ||
- | citycsv=csv.DictReader(fi, delimiter=',') | ||
- | for row in citycsv: | ||
- | try: | ||
- | name=row["city"] | ||
- | row_x=float(row["lon"]) | ||
- | row_y=float(row["lat"]) | ||
- | cy.append(row_y) | ||
- | cx.append(row_x) | ||
- | cities.append(name) | ||
- | except: | ||
- | print "Error Cities" | ||
- | fi.close | ||
- | |||
- | middlelat=maxlat-minlat | ||
- | middlelon=maxlon-minlon | ||
- | ma=middlelat | ||
- | mo=middlelon | ||
- | print "lon" | ||
- | print maxlon | ||
- | print minlon | ||
- | print "lat" | ||
- | print maxlat | ||
- | print minlat | ||
- | print min(threegx) | ||
- | print max(threegy) | ||
- | print mo | ||
- | print ma | ||
- | print middlelat | ||
- | print middlelon | ||
- | |||
- | |||
- | m= Basemap(llcrnrlon=(minlon-(mo*0.1)),llcrnrlat=(minlat-(ma*0.1)),urcrnrlon=(maxlon+(mo*0.1)),urcrnrlat=(maxlat+(ma*0.1)),lat_ts=20, | ||
- | resolution='h',projection='merc',lon_0=-7.95,lat_0=48.48,epsg=3857) | ||
- | m.drawmapboundary(fill_color='white') # fill to edge | ||
- | #m.drawmapscale(lon=(minlon+(middlelon*0.05)), lat=(minlat+(middlelat*0.05)),length=100,lat0=middlelat,lon0=middlelon,barstyle="fancy" ) | ||
- | print("getting wms image") | ||
- | m.wmsimage("http://irs.gis-lab.info/?layers=osm&",layers=["osm"],xpixels=3200,format="png" ) | ||
- | print("received wma image") | ||
- | twogx1,twogy1=m(twogx,twogy) | ||
- | threegx1,threegy1=m(threegx,threegy) | ||
- | fourgx1,fourgy1=m(fourgx,fourgy) | ||
- | nagx1,nagy1=m(nagx,nagy) | ||
- | d1x1,d1y1=m(d1x,d1y) | ||
- | d2x1,d2y1=m(d2x,d2y) | ||
- | cx1,cy1=m(cx,cy) | ||
- | |||
- | #m.scatter(d1x1,d1y1,s=20,c='m',marker="D", alpha=1, edgecolors='none',label="Telekom.de ") | ||
- | #m.scatter(d2x1,d2y1,s=20,c='r',marker="D", alpha=1, edgecolors='none',label="Vodafone.de") | ||
- | |||
- | m.scatter(nagx1,nagy1,s=20,c='r',marker="D", alpha=0.5, edgecolors='none' ,label="No Signal") | ||
- | m.scatter(twogx1,twogy1,s=20,c='b',marker="D", alpha=1, edgecolors='none',label="2G GSM/EDGE") | ||
- | m.scatter(threegx1,threegy1,s=20,c='y',marker="D", alpha=1, edgecolors='none',label="3G UMTS/HSPA+" ) | ||
- | m.scatter(fourgx1,fourgy1,s=20,c='g',marker="D", alpha=1, edgecolors='none' ,label="4G LTE") | ||
- | # some cities | ||
- | m.scatter(cx1,cy1,s=80,c='w',marker="o", alpha=1, edgecolors='k' ) | ||
- | |||
- | |||
- | # print city name | ||
- | for city, xc, yc in zip(cities, cx1, cy1): | ||
- | #draw the city name in a yellow (shaded) box | ||
- | plt.text(xc+200, yc, city, bbox=dict(facecolor='white', alpha=1)) | ||
- | |||
- | |||
- | plt.legend() | ||
- | plt.title("NetModule test drive from " + startTime.strftime(tf) + " until " + endTime.strftime(tf) ) # might want to change this! | ||
- | plt.savefig(dt.now().strftime("%Y%m%d_%H%M%S.png"),dpi=200) | ||
- | plt.show() | ||
- | </code> | ||