diff --git a/config/cfg.py b/config/cfg.py
index c3f91b77bb11f5626796ee5dfe0c49dc27c78007..91c91dc7c34cdbb34b96e490e729274c30e6aea6 100755
--- a/config/cfg.py
+++ b/config/cfg.py
@@ -12,12 +12,15 @@ exp.model_dx = 2000
 exp.timestep = 10
 exp.n_ens = 40
 exp.n_nodes = 10
-exp.n_obs = 81  # radar: n_obs for each observation height level
+
+n_obs = 81  # radar: n_obs for each observation height level
 
 vis = dict(sat=True, channel=1, n_obs=n_obs, err_std=0.03, 
-           distance_between_obs_meters=50000,)
+           distance_between_obs_km=50, 
+           cov_loc_radius_km=10)
 radar = dict(sat=False, kind='radar', n_obs=n_obs, err_std=5.,
-             distance_between_obs_meters=50000,)
+             distance_between_obs_km=50, 
+             cov_loc_radius_km=10)
 
 exp.observations = [vis,]
 
diff --git a/scripts/create_obsseq.py b/scripts/create_obsseq.py
index cb7a161a3a82be81d6caaa6e7724979d8e3862ba..91b39dff9328115ca2cf8d2e2141eafe064158e8 100755
--- a/scripts/create_obsseq.py
+++ b/scripts/create_obsseq.py
@@ -33,13 +33,18 @@ def get_dart_date(time_dt):
     secs_thatday = str(int((time_dt - round_to_day(time_dt)).total_seconds()))
     return dart_date_day, secs_thatday
 
-def calc_obs_locations(n_obs, distance_between_obs_meters,
-                       coords_from_domaincenter=True, fpath_obs_locations=False):
+def calc_obs_locations(n_obs, coords_from_domaincenter=True,
+                       distance_between_obs_km=9999, fpath_obs_locations=False):
     """Calculate coordinate pairs for locations
 
     Args:
         n_obs (int):
             number of observations (must be a square of an integer: 4, 9, 1000, ...)
+        coords_from_domaincenter (bool):
+            if False: spread observations evenly
+            if True: spread from domaincenter, `distance_between_obs_km` apart
+        distance_between_obs_km (int):
+            only used when coords_from_domaincenter=True
         fpath_obs_locations (False or str):
             write an obs_coords.pkl, can be used to check observation locations
             if str, write to file
@@ -60,6 +65,7 @@ def calc_obs_locations(n_obs, distance_between_obs_meters,
         nx, ny = int(np.sqrt(n_obs)), int(np.sqrt(n_obs))
 
         m_per_degree = 2*np.pi*radius_earth_meters/360
+        distance_between_obs_meters = distance_between_obs_km*1000.
         dy_4km_in_degree = distance_between_obs_meters/m_per_degree
 
         for iy in range(int(-ny/2), int(ny/2+1)):
@@ -84,8 +90,8 @@ def calc_obs_locations(n_obs, distance_between_obs_meters,
 
         for i in range(n_obs_x):
             for j in range(n_obs_x):
-                coords.append((lats[skip+i*dx,skip+j*dx],
-                               lons[skip+i*dx,skip+j*dx]))
+                coords.append((lats[skip+i*dx, skip+j*dx],
+                               lons[skip+i*dx, skip+j*dx]))
 
     try:
         if fpath_obs_locations:
@@ -157,27 +163,22 @@ kind
         f.write(msg)
         print(fpath, 'saved.')
 
-def sat(time_dt, channel_id, n_obs, error_var, distance_between_obs_meters,
-        output_path='./', fpath_obs_locations=False):
+def sat(time_dt, channel_id, coords, error_var, output_path='./'):
     """Create obs_seq.in
 
     Args:
         time_dt (dt.datetime): time of observation
         channel_id (int): SEVIRI channel number
             see https://nwp-saf.eumetsat.int/downloads/rtcoef_rttov12/ir_srf/rtcoef_msg_4_seviri_srf.html
-        n_obs (int):
-            number of observations squared (must be a square of an integer: 4, 9, 1000, ...)
+        coords (list of 2-tuples with (lat,lon))
         error_var (float):
             gaussian error with this variance is added to the truth at observation time
         output_path (str): directory where `obs_seq.in` will be saved
-        fpath_obs_locations (False or str):
-            write an obs_coords.pkl, can be used to check observation locations
-            if str, write to file
     """
     # time_dt = add_timezone_UTC(time_dt)
     # time_dt = dt.datetime(2008, 7, 30, 15, 30, tzinfo=dt.timezone.utc)
-    assert n_obs == int(n_obs)
     error_var = str(error_var)
+    n_obs = len(coords)
 
     # Brightness temperature or Reflectance?
     channel_id = int(channel_id)
@@ -189,10 +190,6 @@ def sat(time_dt, channel_id, n_obs, error_var, distance_between_obs_meters,
         code = '255'
     channel_id = str(channel_id)
 
-    coords = calc_obs_locations(n_obs, distance_between_obs_meters,
-                                coords_from_domaincenter=False,
-                                fpath_obs_locations=fpath_obs_locations)
-
     time_dt = add_timezone_UTC(time_dt)
     sun_az = str(get_azimuth(lat0, lon0, time_dt))
     sun_zen = str(90. - get_altitude(lat0, lon0, time_dt))
@@ -269,13 +266,7 @@ kind
         print(fpath, 'saved.')
 
 
-def gen_coords(n_obs, distance_between_obs_meters, heights,
-               coords_from_domaincenter=True, fpath_obs_locations=False):
-
-    coords = calc_obs_locations(n_obs, distance_between_obs_meters,
-                coords_from_domaincenter=coords_from_domaincenter,
-                fpath_obs_locations=fpath_obs_locations)
-
+def calc_obs_locations_3d(coords, heights):
     # append height
     coords2 = []
     for i in range(len(coords)):
@@ -283,23 +274,19 @@ def gen_coords(n_obs, distance_between_obs_meters, heights,
             coords2.append(coords[i] + (hgt_m,))
 
     n_obs = len(coords2)
-    print('effective number of observations (with vertical levels):', n_obs, 
+    print('effective number of observations (with vertical levels):', n_obs,
           ' on each level:', len(coords))
     return coords2
 
 
-def generic_obs(obs_type, time_dt, n_obs, error_var, distance_between_obs_meters,
-                output_path='./', fpath_obs_locations=False):
+def generic_obs(obs_type, time_dt, coords, error_var, output_path='./'):
 
     obs_codes = {'RASO_T': {'name': 'RADIOSONDE_TEMPERATURE', 'nr': '5'},
                  'RADAR': {'name': 'RADAR_REFLECTIVITY', 'nr': '37'}
                  }
 
     heights = np.arange(1000, 15001, 1000)
-
-    coords = gen_coords(n_obs, distance_between_obs_meters, heights,
-                        coords_from_domaincenter=False, 
-                        fpath_obs_locations=fpath_obs_locations)
+    coords = calc_obs_locations_3d(coords, heights)
 
     dart_date_day, secs_thatday = get_dart_date(time_dt)
     print('secs, days:', secs_thatday, dart_date_day)
diff --git a/scripts/gen_synth_obs.py b/scripts/gen_synth_obs.py
index 25041296c12f096e15f4a22646fe7f37509a009e..c655b85558e3bc9fe3cff1c5d0298d9c10dd89b8 100755
--- a/scripts/gen_synth_obs.py
+++ b/scripts/gen_synth_obs.py
@@ -4,7 +4,9 @@ import numpy as np
 from scipy.interpolate import interp1d
 from config.cfg import exp, cluster
 from utils import symlink, copy, sed_inplace, append_file
-import create_obsseq
+import create_obsseq as osq
+
+earth_radius_km = 6370
 
 # fit of Fig 7, Harnisch 2016
 x_ci = [0,   5, 10.5, 13, 16]
@@ -70,7 +72,8 @@ def edit_obserr_in_obsseq(fpath_obsseqin, OEs):
         for line in obsseq_new:
             f.write(line)
 
-def set_input_nml(sat_channel=False, just_prior_values=False):
+def set_input_nml(sat_channel=False, just_prior_values=False,
+                  cov_loc_radius_km=32):
     """descr"""
     if just_prior_values:
         template = cluster.scriptsdir+'/../templates/input.prioronly.nml'
@@ -78,6 +81,8 @@ def set_input_nml(sat_channel=False, just_prior_values=False):
         template = cluster.scriptsdir+'/../templates/input.nml'
     copy(template, cluster.dartrundir+'/input.nml')
     sed_inplace(cluster.dartrundir+'/input.nml', '<n_ens>', str(int(exp.n_ens)))
+    cov_loc_radian = cov_loc_radius_km/earth_radius_km
+    sed_inplace(cluster.dartrundir+'/input.nml', '<cov_loc_radian>', str(cov_loc_radian))
 
     # input.nml for RTTOV
     if sat_channel > 0:
@@ -91,6 +96,7 @@ def set_input_nml(sat_channel=False, just_prior_values=False):
 if __name__ == "__main__":
 
     time = dt.datetime.strptime(sys.argv[1], '%Y-%m-%d_%H:%M')
+    fpath_obs_coords = cluster.archivedir()+time.strftime('/%Y-%m-%d_%H:%M/obs_coords.pkl')
 
     # remove any existing observation files
     os.system('rm -f '+cluster.dartrundir+'/obs_seq_*.out')
@@ -100,32 +106,27 @@ if __name__ == "__main__":
 
         n_obs = obscfg['n_obs']
         error_var = (obscfg['err_std'])**2
-        distance_between_obs_meters = obscfg['distance_between_obs_meters']
+        sat_channel = obscfg.get('channel', False)
+        cov_loc = obscfg.get['cov_loc_radius_km']
+        dist_obs = obscfg.get('distance_between_obs_km', False)
 
         # generate obs_seq.in
-        if not obscfg['sat']:
-            create_obsseq.generic_obs(obscfg['kind'], time, n_obs, error_var,
-                        distance_between_obs_meters,
-                        output_path=cluster.dartrundir,
-                        fpath_obs_locations=cluster.archivedir()+time.strftime('/%Y-%m-%d_%H:%M')
-                                            +'/obs_coords.pkl')
-            set_input_nml()
+        obs_coords = osq.calc_obs_locations(n_obs, coords_from_domaincenter=False, 
+                                            distance_between_obs_km=dist_obs, 
+                                            fpath_obs_locations=fpath_obs_coords)
+
+        if obscfg['sat']:
+            osq.sat(time, sat_channel, obs_coords, error_var,
+                    output_path=cluster.dartrundir)
         else:
-            create_obsseq.sat(time, obscfg['channel'], n_obs, error_var,
-                                distance_between_obs_meters,
-                                output_path=cluster.dartrundir, 
-                                fpath_obs_locations='./domain.pkl')
-            
-            if obscfg['channel'] == 6:
-                # for cloud dependent error
-                set_input_nml(sat_channel=obscfg['channel'], just_prior_values=True)
-            else:
-                set_input_nml(sat_channel=obscfg['channel'])
+            osq.generic_obs(obscfg['kind'], time, obs_coords, error_var,
+                            output_path=cluster.dartrundir)
 
         if not os.path.exists(cluster.dartrundir+'/obs_seq.in'):
             raise RuntimeError('obs_seq.in does not exist in '+cluster.dartrundir)
 
         # generate observations (obs_seq.out)
+        set_input_nml(sat_channel=sat_channel, cov_loc_radius_km=cov_loc)
         os.chdir(cluster.dartrundir)
         t = dt.datetime.now()
         os.system('mpirun -np 12 ./perfect_model_obs')
@@ -133,9 +134,9 @@ if __name__ == "__main__":
 
         # cloud dependent observation error
         if obscfg['sat']:
-            if obscfg['channel'] == 6:
+            if sat_channel == 6:
                 # run ./filter to have prior observation estimates from model state
-                set_input_nml(sat_channel=obscfg['channel'], just_prior_values=True)
+                set_input_nml(sat_channel=sat_channel, just_prior_values=True)
                 t = dt.datetime.now()
                 os.system('mv obs_seq.out obs_seq_all.out; mpirun -np 20 ./filter')
                 print('1st filter', (dt.datetime.now()-t).total_seconds())
@@ -166,10 +167,12 @@ if __name__ == "__main__":
                 print('real obs gen', (dt.datetime.now()-t).total_seconds())
 
                 # correct input.nml for actual assimilation later on
-                set_input_nml(sat_channel=obscfg['channel'], just_prior_values=False)
+                set_input_nml(sat_channel=sat_channel,
+                              cov_loc_radius_km=cov_loc)
 
         # rename according to i_obs
-        os.rename(cluster.dartrundir+'/obs_seq.out', cluster.dartrundir+'/obs_seq_'+str(i_obs)+'.out')
+        os.rename(cluster.dartrundir+'/obs_seq.out', 
+                  cluster.dartrundir+'/obs_seq_'+str(i_obs)+'.out')
 
     # concatenate the created obs_seq_*.out files
     os.chdir(cluster.dartrundir)