diff --git a/instantPASDy-GUI.py b/instantPASDy-GUI.py
index db069b1702d09c326f5a4d414e314d47d070a9e7..3488c3f16559fd20a95a644cc2a92aa952ede367 100644
--- a/instantPASDy-GUI.py
+++ b/instantPASDy-GUI.py
@@ -588,6 +588,8 @@ while True:
         #config = configparser.ConfigParser()
         window.Element('configfile').Update(window.Element('_configfile').Get())
         configfile = window.Element('configfile').Get()
+        
+        #config = PASDy.Config(configfile, verbose=1)
         if configfile == '': configfile = 'config/config.cfg'
         if os.path.isfile(configfile):
             #temp = config.read(configfile)
diff --git a/instantPASDy.py b/instantPASDy.py
index d961f1ca4eec9d0311894fca8a7becd8c116cd94..2937d8524a8b9cb7be1d8ad46da879c46c65acb3 100644
--- a/instantPASDy.py
+++ b/instantPASDy.py
@@ -1,6 +1,11 @@
 # Suppress nasty future warnings about pandas datetime conersion
 import warnings
 warnings.simplefilter(action='ignore', category=FutureWarning)
+
+from PASDy.Files import *
+from PASDy.Spatiotemp import *
+from PASDy.Misc import *
+
 #
 from datetime import datetime, timedelta
 import pytz
@@ -25,81 +30,36 @@ import simplekml
 from glob import glob
 
 #JJ packages used for calculating nearest neighbors
-from heapq import nsmallest, nlargest
-from scipy import stats
-from math import sin, cos, sqrt, atan2, radians
-import itertools
-from sklearn.neighbors import KDTree
+#from heapq import nsmallest, nlargest
+#from scipy import stats
+#from math import sin, cos, sqrt, atan2, radians
+#import itertools
+#from sklearn.neighbors import KDTree
 
 # Overwrite print() with flush, i.e., no buffer
-_orig_print = print
-def print(*args, **kwargs):
-    _orig_print(*args, flush=True, **kwargs)
+print = print_flush
+#_orig_print = print
+#def print(*args, **kwargs):
+#    _orig_print(*args, flush=True, **kwargs)
     
 #JJ recalculate strange coordinates to proper decimals 
-def deg100min2dec(coord):
-    coord = coord/100
-    deg = np.floor(coord)
-    minute = (coord - deg)/0.6
-    dec = deg+minute
-    return(dec)
+#def deg100min2dec(coord):
+#    coord = coord/100
+#    deg = np.floor(coord)
+#    minute = (coord - deg)/0.6
+#    dec = deg+minute
+#    return(dec)
     
     
 #JJ use x nearest neighbors for averaging
 ## find k closest lat/lon to every lat/lon coordinates
-def distance(pointA, pointB):
-    return sqrt((pointB[0] - pointA[0]) ** 2 + (pointB[1] - pointA[1]) ** 2)
+#def distance(pointA, pointB):
+#    return sqrt((pointB[0] - pointA[0]) ** 2 + (pointB[1] - pointA[1]) ** 2)
 
 import zipfile
 import os
 
 
-def distanceinkm(xstand,ystand,xfor,yfor): # approximate radius of earth in km
-   
-    R = 6373.0
-    lat1 = radians(xstand)
-    lon1 = radians(ystand)
-    lat2 = radians(xfor)
-    lon2 = radians(yfor)
-    
-    dlon = lon2 - lon1
-    dlat = lat2 - lat1
-    a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2
-    c = 2 * atan2(sqrt(a), sqrt(1 - a))
-    distance = R * c
-    return(distance)
-
-def Neighbours(lat, lon, numpoints, data):
-    numpoints = numpoints+1 # points itself included - therefore add one to get desired output
-    points = np.vstack((lat, lon)).T # create numpy array from coordinates
-    
-    kdt = KDTree(points, leaf_size=30, metric='euclidean') # build tree with euclidean distances
-    neighborpoints = kdt.query(points, k=numpoints, return_distance=False) # find the closest k points  
-        
-    # calculate maximal distance within the k clostest coordinates
-    neighbordist = []
-    for i in range(len(points)):
-        furthest = np.array(nlargest(1, points[neighborpoints[i]], lambda p : distance(p, (points[i])))) # find the coordinates for the k closest points
-        neighbordist.append(distanceinkm(points[i][0],points[i][1], furthest[0][0],furthest[0][1])*1000)
-        
-    return neighbordist, neighborpoints
-
-    #neighborpoints = [] # create empty list for all results
-    
-    #for i in range(len(points)): ## iterate for all points
-    #    nearest = np.array(nsmallest(numpoints, points, lambda p : distance(p, (points[i])))) # find the coordinates for the k closest points
-    #    print(nearest)
-    #    index = [] # create empty list for the index of the positions
-               
-    #    for m in range(len(nearest)): 
-    #        idx, rest = np.where(points==nearest[m]) # find the rows of these closest k coordinates
-    #        idx = stats.mode(idx) # calculate the mode - necessary because sometimes several outputs from above
-    #        index.append(idx.mode.tolist()) # create list from the k rows found
-                
-    #    neighborpoints.append(index) # create list of k closest locations for all locations
-    #for i in range(len(neighborpoints)):
-    #    neighborpoints[i] = list(itertools.chain(*neighborpoints[i])) # reduce for one nested list
-    
    
       
 
@@ -313,26 +273,26 @@ class NM:
         file_save_msg(file, '')
 
         
-def file_save_msg(file, name='File', end=''):
-    size = os.stat(file).st_size
-    if   size < 1024:    sizestr = "%.0f Bytes" % (size)
-    elif size < 1024**2: sizestr = "%.0f KB"    % (size/1024)
-    else:                sizestr = "%.1f MB"    % (size/1024/1024)
-    print("%s saved to %s (%s)" % (name, file, sizestr), end=end)
+#def file_save_msg(file, name='File', end=''):
+#    size = os.stat(file).st_size
+#    if   size < 1024:    sizestr = "%.0f Bytes" % (size)
+#    elif size < 1024**2: sizestr = "%.0f KB"    % (size/1024)
+#    else:                sizestr = "%.1f MB"    % (size/1024/1024)
+#    print("%s saved to %s (%s)" % (name, file, sizestr), end=end)
     
 
-def ifdef(a, b, convert=float):
-    if not a:
-        return(b)
-    else:
-        return(convert(a))
+#def ifdef(a, b, convert=float):
+#    if not a:
+#        return(b)
+#    else:
+#        return(convert(a))
 
-def xsplit(s, range=0, type=str):
-    a = np.array(s.replace(',',' ').split(), dtype=type)
+#def xsplit(s, range=0, type=str):
+#    a = np.array(s.replace(',',' ').split(), dtype=type)
     # fill up with NaNs
-    for i in np.arange(range-len(a)):
-        a = np.append(a, np.nan)
-    return(a)
+#    for i in np.arange(range-len(a)):
+#        a = np.append(a, np.nan)
+#    return(a)
 
 re_crnsdata = re.compile(r'^\d+\,')
 re_dataselect = re.compile(r'//DataSelect\s*=\s*(\w+)')
@@ -407,10 +367,10 @@ def read(filename, archive=None, tz='UTC'):
     return(datastr+"\n")
 
 
-def chomp(x):
-    if x.endswith("\r\n"): return x[:-2]
-    if x.endswith("\n") or x.endswith("\r"): return x[:-1]
-    return(x)
+#def chomp(x):
+#    if x.endswith("\r\n"): return x[:-2]
+#    if x.endswith("\n") or x.endswith("\r"): return x[:-1]
+#    return(x)
     
 def read_header(filename, archive=None):
 
@@ -563,12 +523,12 @@ class Fig:
             Fig.fig.savefig(bbox_inches='tight')
             plt.close()
             
-def yesno2bool(s):
-    s = s.lower()
-    if s == 'yes' or s == 'y':
-        return(True)
-    else:
-        return(False)
+#def yesno2bool(s):
+#    s = s.lower()
+#    if s == 'yes' or s == 'y':
+#        return(True)
+#    else:
+#        return(False)
 
 config = None
 
diff --git a/lib/CORN.py b/lib/CORN.py
deleted file mode 100644
index bec04f3674ba5c44f3a5a52495dbe2ad6d4d6938..0000000000000000000000000000000000000000
--- a/lib/CORN.py
+++ /dev/null
@@ -1 +0,0 @@
-import CORN
\ No newline at end of file
diff --git a/lib/PASDy/CORN.py b/lib/PASDy/CORN.py
new file mode 100644
index 0000000000000000000000000000000000000000..4baa30e1f1792ef98f3e190ec021c0f8febb458f
--- /dev/null
+++ b/lib/PASDy/CORN.py
@@ -0,0 +1,19 @@
+# CORNish PASDy
+
+
+## Configuration
+##
+### Inherit from PASDy.Config(...)
+###
+import PASDy.Config
+
+class Config(PASDy.Config.Config):
+
+    def __init__(self, *args, **kwargs):
+
+        #  Set keys if not defined
+        if not 'unfold_numunits' in kwargs: 
+            kwargs['unfold_numunits'] = ['aggregate']
+
+        # Call parent's init with the extended keys
+        super().__init__(*args, **kwargs)
\ No newline at end of file
diff --git a/lib/PASDy/Config.py b/lib/PASDy/Config.py
new file mode 100644
index 0000000000000000000000000000000000000000..31e4305892dd23e378b70c598baaedea2fffe6c3
--- /dev/null
+++ b/lib/PASDy/Config.py
@@ -0,0 +1,157 @@
+# PASDy.Config
+
+import os
+import re
+from configobj import ConfigObj # better alternative to configparser: preserves order and comments
+from copy import deepcopy
+
+import PASDy.Files as Files
+import PASDy.Misc as Misc
+from PASDy.Misc import printv
+
+#
+#
+class Config:
+   
+    # config = PASDy.Config(file)
+    # creates a Config object from file.cfg
+
+    def __init__(self, file, default_file='config.cfg', encoding='cp850', verbose=2,
+                 reprocess=True, clean_paths=True, convert_bool=True, unfold_range=True, join_lists=True,
+                 unfold_numunits=[]):
+        self.file = file
+        self.default_file = default_file
+        self.encoding = encoding
+        self._verbose = verbose
+        self.reprocess = reprocess
+        self.clean_paths = clean_paths
+        self.convert_bool = convert_bool
+        self.unfold_range = unfold_range
+        self.join_lists = join_lists
+        self.unfold_numunits = unfold_numunits
+        
+        self.c = None
+        self.read()
+    
+    # Verbose print
+    def _print(self, level, s=''):
+        printv(level, self._verbose, s)        
+    
+    # Read
+    def read(self):
+        if self.file == '' or self.file is None or not self.file:
+            # os.path.abspath('') is better than __file__ most of the time, see https://stackoverflow.com/questions/39125532/file-does-not-exisit-in-jupyter-notebook
+            self.file = self.default_file
+            self._print(2, 'No file given, choosing default %s' % default_file)
+        
+        self.c = config_read(self.file, self.encoding, self._verbose)
+
+        if not self.c is None:
+            self.sections = self.c.sections
+            if self.reprocess:
+                self.do_process()
+
+    # Process
+    def do_process(self):
+        self.cp = deepcopy(self.c)
+        
+        for section in self.sections:
+            for k in self.c[section]:
+                v = self.c[section][k]
+                
+                if 'path' in k:
+                    if self.clean_paths:
+                        self.cp[section][k] = Files.trailingslash(v)
+                        
+                elif isinstance(v, list):
+                    if self.join_lists:
+                        self.cp[section][k] = ', '.join(v)
+                
+                elif v.lower() == 'yes' or v.lower() == 'no':
+                    if self.convert_bool:                
+                        self.cp[section][k] = Misc.yesno2bool(v)
+                        
+                elif k.endswith('_range'):
+                    if self.unfold_range:
+                        if not isinstance(v, list):
+                            v = ['','']
+                        self.cp[section][k[:k.index('_range')]+'_min'] = v[0]
+                        self.cp[section][k[:k.index('_range')]+'_max'] = v[1]
+                    
+                elif self.unfold_numunits:
+                    if k in self.unfold_numunits:
+                        if v:
+                            v_bundle = re.search('^(\d+)([a-z]+)', v, re.IGNORECASE)
+                            self.cp[section][k] = [v_bundle.group(1), v_bundle.group(2)]
+                        else:
+                            self.cp[section][k] = ['','']
+
+                        self.cp[section][k+'_num'] = v[0]
+                        self.cp[section][k+'_unit'] = v[1]
+    
+    def undo_process(self):
+        
+        for section in self.sections:
+            for k in self.cp[section]:
+                v = self.cp[section][k]
+                
+                if isinstance(v, bool):
+                    if self.convert_bool:
+                        self.c[section][k] = Misc.bool2yesno(v)
+                    
+                elif ',' in v:
+                    if self.join_lists:
+                        self.c[section][k] = [x.strip() for x in v.split(',')]
+                
+                elif k.endswith('_range'):
+                    if self.unfold_range:
+                        kcut = k[:k.index('_range')]
+                        self.c[section][k] = [ self.cp[section][kcut+'_min'], self.cp[section][kcut+'_max'] ]
+                        if self.cp[section][kcut+'_min'] == '':
+                            self.c[section][k] = ''
+                        
+                elif self.unfold_numunits:
+                    if k in self.unfold_numunits:
+                        if self.cp[section][k+'_num'] == '':
+                            self.c[section][k] = ''
+                        else:
+                            self.c[section][k] = self.cp[section][k+'_num'] + self.cp[section][k+'_unit']
+    
+    # Save
+    def save(self, file=None):
+        self.undo_process()
+        # no argument given
+        if file is None: file = self.file
+        # Save
+        config_save(self.c, file, self._verbose)
+        
+
+# Functions
+#
+## Read ConfigObj from file.cfg
+##
+def config_read(file, encoding='cp850', verbose=1):
+    
+    if os.path.isfile(file):
+        printv(1, verbose, 'Reading sensor config: %s' % file)
+        return(ConfigObj(file, encoding=encoding))
+    else:
+        print(1, verbose, 'File does not exist: %s' % file)
+        return(None)
+
+
+## Save ConfigObj into file
+##
+def config_save(config, file, verbose=1):
+    
+    if file == '' or file is None or not file:
+        # invalid file
+        printv(0, verbose, 'Cannot save config file: %s' % file)
+        return(False)
+    else:
+        config.filename = file
+        config.write_empty_values = True
+        config.write()
+        # TODO: check if write() was successful
+        Files.file_save_msg(file, name='Config')
+        return(True)
\ No newline at end of file
diff --git a/lib/PASDy/Files.py b/lib/PASDy/Files.py
new file mode 100644
index 0000000000000000000000000000000000000000..3d4ed9131ef006a4331a0546ab6c3ed65a5c3827
--- /dev/null
+++ b/lib/PASDy/Files.py
@@ -0,0 +1,30 @@
+# PASDy.Files
+
+# Formatting
+#
+### Path trailing / 
+def trailingslash(p):
+    import os
+    # add / to folder
+    if os.path.isdir(p):
+        return(os.path.join(p, ''))
+    # remove / from file
+    elif os.path.isfile(p):
+        return(p.rstrip('\\').rstrip('/'))
+    else:
+        return(p)
+
+
+## Output
+##
+### Print newly created file name and size
+###
+def file_save_msg(file, name='File', end=''):
+    import os
+    size = os.stat(file).st_size
+    if   size < 1024:    sizestr = "%.0f Bytes" % (size)
+    elif size < 1024**2: sizestr = "%.0f KB"    % (size/1024)
+    else:                sizestr = "%.1f MB"    % (size/1024/1024)
+    print("%s saved to %s (%s)" % (name, file, sizestr), end=end)
+    
+
diff --git a/lib/PASDY.py b/lib/PASDy/Misc.py
similarity index 62%
rename from lib/PASDY.py
rename to lib/PASDy/Misc.py
index 8bae7441f516989b496dab33e3f0217332149a52..01c22c9d124eccbb4092af11b3284697350276ca 100644
--- a/lib/PASDY.py
+++ b/lib/PASDy/Misc.py
@@ -1,4 +1,4 @@
-import PASDY
+#import PASDY
 
 # General functions
 
@@ -8,6 +8,12 @@ _orig_print = print
 def print_flush(*args, **kwargs):
     _orig_print(*args, flush=True, **kwargs)
 
+## Verbose print
+##
+def printv(v=1, v_ref=1, s=''):
+    if v <= v_ref:
+        print(s)
+
 ## System calls
 ##
 ### Launch a file using its corresponding default application
@@ -39,6 +45,7 @@ def ifdef(a, b, convert=float):
 ### Append some NaNs
 ###
 def xsplit(s, range=0, type=str):
+    import numpy as np
     a = np.array(s.replace(',',' ').split(), dtype=type)
     # fill up with NaNs
     for i in np.arange(range-len(a)):
@@ -74,36 +81,3 @@ def bool2yesno(boo):
     else:
         return('no')
         
-## Output
-##
-### Print newly created file name and size
-###
-def file_save_msg(file, name='File', end=''):
-    size = os.stat(file).st_size
-    if   size < 1024:    sizestr = "%.0f Bytes" % (size)
-    elif size < 1024**2: sizestr = "%.0f KB"    % (size/1024)
-    else:                sizestr = "%.1f MB"    % (size/1024/1024)
-    print("%s saved to %s (%s)" % (name, file, sizestr), end=end)
-    
-
-# Spatial functions
-
-## Distance
-##
-### use x nearest neighbors for averaging
-### find k closest lat/lon to every lat/lon coordinates
-### 
-def distance(pointA, pointB):
-    return sqrt((pointB[0] - pointA[0]) ** 2 + (pointB[1] - pointA[1]) ** 2)
-
-
-## Coordinate conversion
-##
-### recalculate strange coordinates to proper decimals 
-### 
-def deg100min2dec(coord):
-    coord = coord/100
-    deg = np.floor(coord)
-    minute = (coord - deg)/0.6
-    dec = deg+minute
-    return(dec)
diff --git a/lib/PASDy/NM.py b/lib/PASDy/NM.py
new file mode 100644
index 0000000000000000000000000000000000000000..5e8a8f57323a65b9eca29517fa19d18af1f63f9a
--- /dev/null
+++ b/lib/PASDy/NM.py
@@ -0,0 +1,3 @@
+
+def whoami():
+    print("I am NM!")
\ No newline at end of file
diff --git a/lib/PASDy/Spatiotemp.py b/lib/PASDy/Spatiotemp.py
new file mode 100644
index 0000000000000000000000000000000000000000..8c5d41dfe109439411e7b03b2b8af9c6207a796c
--- /dev/null
+++ b/lib/PASDy/Spatiotemp.py
@@ -0,0 +1,92 @@
+# PASDy.Spatiotemp -- Spatiotemporal data analysis
+
+import numpy as np
+
+
+# Spatial functions
+
+## Distance
+##
+### use x nearest neighbors for averaging
+### find k closest lat/lon to every lat/lon coordinates
+### 
+def distance(pointA, pointB):
+    return sqrt((pointB[0] - pointA[0]) ** 2 + (pointB[1] - pointA[1]) ** 2)
+
+
+## Coordinate conversion
+##
+### recalculate strange coordinates to proper decimals 
+### 
+def deg100min2dec(coord):
+    import numpy
+    coord = coord/100
+    deg = numpy.floor(coord)
+    minute = (coord - deg)/0.6
+    dec = deg+minute
+    return(dec)
+
+
+# Nearest Neighbor
+#
+## JJ packages used for calculating nearest neighbors
+## 
+from heapq import nsmallest, nlargest
+from scipy import stats
+from math import sin, cos, sqrt, atan2, radians
+import itertools
+from sklearn.neighbors import KDTree
+
+
+### From lat, lon get distance in km
+###
+def distanceinkm(xstand, ystand, xfor, yfor): # approximate radius of earth in km
+   
+    R = 6373.0
+    lat1 = radians(xstand)
+    lon1 = radians(ystand)
+    lat2 = radians(xfor)
+    lon2 = radians(yfor)
+    
+    dlon = lon2 - lon1
+    dlat = lat2 - lat1
+    a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2
+    c = 2 * atan2(sqrt(a), sqrt(1 - a))
+    distance = R * c
+    
+    return(distance)
+
+### 
+###
+def Neighbours(lat, lon, numpoints, data):
+
+    numpoints = numpoints+1 # points itself included - therefore add one to get desired output
+    points = np.vstack((lat, lon)).T # create numpy array from coordinates
+    
+    kdt = KDTree(points, leaf_size=30, metric='euclidean') # build tree with euclidean distances
+    neighborpoints = kdt.query(points, k=numpoints, return_distance=False) # find the closest k points  
+        
+    # calculate maximal distance within the k clostest coordinates
+    neighbordist = []
+    for i in range(len(points)):
+        furthest = np.array(nlargest(1, points[neighborpoints[i]], lambda p : distance(p, (points[i])))) # find the coordinates for the k closest points
+        neighbordist.append(distanceinkm(points[i][0],points[i][1], furthest[0][0],furthest[0][1])*1000)
+        
+    return neighbordist, neighborpoints
+
+    #neighborpoints = [] # create empty list for all results
+    
+    #for i in range(len(points)): ## iterate for all points
+    #    nearest = np.array(nsmallest(numpoints, points, lambda p : distance(p, (points[i])))) # find the coordinates for the k closest points
+    #    print(nearest)
+    #    index = [] # create empty list for the index of the positions
+               
+    #    for m in range(len(nearest)): 
+    #        idx, rest = np.where(points==nearest[m]) # find the rows of these closest k coordinates
+    #        idx = stats.mode(idx) # calculate the mode - necessary because sometimes several outputs from above
+    #        index.append(idx.mode.tolist()) # create list from the k rows found
+                
+    #    neighborpoints.append(index) # create list of k closest locations for all locations
+    #for i in range(len(neighborpoints)):
+    #    neighborpoints[i] = list(itertools.chain(*neighborpoints[i])) # reduce for one nested list
+    
diff --git a/playground.py b/playground.py
new file mode 100644
index 0000000000000000000000000000000000000000..471db42197eafba9ed36bfb1d21f3c8ef44a94d3
--- /dev/null
+++ b/playground.py
@@ -0,0 +1,4 @@
+import NMxxx
+
+print("hello")
+NMxxx.whoami()
\ No newline at end of file
diff --git a/test/test.py b/test/test.py
index f32c31a545703e2c093fbd0384870758c583034f..f57ebdc63d2d5a1022eb66b4591673a8f8777cdd 100644
--- a/test/test.py
+++ b/test/test.py
@@ -4,7 +4,7 @@ from glob import glob
 
 test_cases = [
     ["python", "../instantPASDy.py", "input/station.cfg", "--nonstop"],
-    ["python", "../instantPASDy.py", "input/JJ-config_rover.cfg", "--nonstop"]
+#    ["python", "../instantPASDy.py", "input/JJ-config_rover.cfg", "--nonstop"]
 ]
 
 i = 0