Radar Geometry Tutorial

This tutorial is organized in 4 sections.

  1. Working with Orbits

  2. Working with RadarGridParameters

  3. Forward mapping example - determining bounding boxes

  4. Inverse mapping example - locating known targets

Working with Orbits

Orbit data structure is at the heart of geometric manipulation from the python level in ISCE. This is just a uniformly sampled, time-tagged collection of state vectors in Earth Centered Earth Fixed (ECEF) coordinates. In this example, we will walk through an example of constructing a Orbit object with a list of state vectors. The input state vectors are provided in a simple 7-column text file as shown below:

2023-01-03T14:21:09.00000 -4068790.9009072506 -5854638.09684865 -157546.3974678845 -1412.350370142649 793.0611670136079 7396.0204940760395
2023-01-03T14:21:19.00000 -4082685.404401117 -5846376.85649058 -83578.87959621595 -1366.509117081948 859.1613883382139 7397.35020326244
2023-01-03T14:21:29.00000 -4096120.2529026493 -5837455.401027409 -9602.145399636449 -1320.4202266388909 925.1029289217265 7397.863734708695
2023-01-03T14:21:39.00000 -4109092.9966275753 -5827875.355589654 64375.642680131175 -1274.0890232893116 990.8781282973218 7397.560970339528
2023-01-03T14:21:49.00000 -4121601.239232442 -5817638.421788033 138346.3214850321 -1227.5208704931913 1056.4793512317062 7396.441885067954
2023-01-03T14:21:59.00000 -4133642.6382011203 -5806746.377455956 212301.72807049527 -1180.7211700363855 1121.8989887092027 7394.506546829757
2023-01-03T14:22:09.00000 -4145214.905228363 -5795201.076386861 286233.7006414636 -1133.6953612277905 1187.1294591349363 7391.755116862469
2023-01-03T14:22:19.00000 -4156315.806580762 -5783004.448041838 360134.0794648736 -1086.448920636437 1252.163208639342 7388.187848922919
2023-01-03T14:22:29.00000 -4166943.1634739274 -5770158.497276335 433994.70781745116 -1038.987361000223 1316.9927127108183 7383.805090037879
2023-01-03T14:22:39.00000 -4177094.85242924 -5756665.304040207 507807.43291334657 -991.316230586328 1381.6104770689078 7378.607280350906
2023-01-03T14:22:49.00000 -4186768.805625253 -5742527.02307026 581564.1068345518 -943.4411124673695 1446.0090386496333 7372.59495308022
2023-01-03T14:22:59.00000 -4195963.011242143 -5727745.883573368 655256.5874615454 -895.3676237681589 1510.1809666126744 7365.768734481611
2023-01-03T14:23:09.00000 -4204675.513793652 -5712324.1888926225 728876.7394021156 -847.1014148874277 1574.1188632617775 7358.129343442166
2023-01-03T14:23:19.00000 -4212904.414471599 -5696264.316192334 802416.434923766 -798.6481687670465 1637.815365281732 7349.677592449129
2023-01-03T14:23:29.00000 -4220647.871452852 -5679568.716090969 875867.5548781296 -750.0136000742974 1701.2631444783617 7340.414386460979
def loadOrbit(infilename):
    from isce3.core import statevector, orbit, dateTime

    #List of state vectors
    svs = []

    #Open file with state vectors for reading
    with open(infilename, 'r') as fid:

        #For each line in file
        for line in fid:
            vals = line.strip().split()

            #Create dateTime object.
            tstamp = dateTime(dt=vals[0])

            pos = [float(x) for x in vals[1:4]]
            vel = [float(x) for x in vals[4:7]]


            svs.append( statevector(datetime=tstamp,
                                    position=pos,
                                    velocity=vel))

    return orbit(statevecs=svs)

Note

In this example, we demonstrated creation of Orbit objects with simple text files. The same approach can be used to generate Orbits from database queries or Sentinel-1/ NISAR XML files.

Working with RadarGridParameters

RadarGridParameters is the basic minimal data structure used to represent the limits of a radar image in azimuth time and slant range coordinates. This data structure is relevant for all NISAR L1 and L2 radar geometry products. It is inherently assumed that the imagery is laid out on a uniform grid in both azimuth time and slant range.

A simple RadarGridParameters object can be created as shown below:

from isce3.product import radarGridParameters
from isce3.core import dateTime


grid = radarGridParameters()

#lookSide
grid.lookSide = "left"

#Imaging wavelength
grid.wavelength = 0.06

#Slant range extent
grid.startingRange = 8.0e5
grid.rangePixelSpacing = 10.
grid.width = 1000

#Along track extent
grid.referenceEpoch = dateTime(dt="2023-01-03T14:21:55.125")
grid.sensingStart = 0.  #Seconds since refEpoch
grid.prf = 1000.
grid.length = 1500

Note

This example goes into gory detail of setting up a basic radar grid at the lowest level. In future, higher level python classes will include a getRadarGrid() method that should return a populated grid structure with data from HDF5 products.

Forward mapping example - determining bounding boxes

In this example, we will demonstrate the forward mapping algorithm by using it to determine approximate bounding boxes on the ground.

from isce3.core import dateTime, orbit, projection
from isce3.product import radarGridParameters
from isce3.geometry import getGeoPerimeter

#See above for implementation details
arc = loadOrbit('orbit_arc.txt')

#Create radar grid, but sync referenceEpoch for fast computation
grid = radarGridParameters()
grid.lookSide = "left"
grid.wavelength = 0.06
grid.startingRange = 8.0e5
grid.rangePixelSpacing = 10.
grid.width = 1000
grid.referenceEpoch = arc.referenceEpoch
grid.sensingStart = (dateTime(dt="2023-01-03T14:21:55.125") - grid.referenceEpoch).getTotalSeconds()
grid.prf = 1000.
grid.length = 1500

assert(grid.referenceEpoch == arc.referenceEpoch)


##Use perimeter functionality
epsg = projection(epsg=4326)
box = getGeoPerimeter(grid, arc, epsg, pointsPerEdge=5)

#box is a Geojson string
print(box)

Note

We could also have implemented the perimeter estimation by looping over points on the edge of the swath and using isce3.geometry.rdr2geo_pt function with appropriate inputs

Inverse mapping example - locating known targets

In this example, we will demonstrate the inverse mapping algorithm by using it to determine the location of a known target in a radar image. For this example, we will use the coordinates of the estimated perimeter above and reuse the orbit data structure

from isce3.core import lut2d
from isce3.geometry import geo2rdr_point
import json
import numpy as np

##Get points from Geojson
targets = json.loads(box)['coordinates']

#Set up zero doppler
doppler = lut2d()

#Get ellipsoid spec
elp = epsg.ellipsoid()

for targ in targets:
   #Convert from degrees to radians
   llh = [np.radians(targ[0]), np.radians(targ[1]), targ[2]]

   #Estimate target position
   taz, rng = geo2rdr_point(llh, elp, arc, doppler,
                            grid.wavelength, grid.lookSide)

   #Line, pixel number
   print('Target at: ', *targ)
   print('Estimated line number: ', (taz - grid.sensingStart) * grid.prf)
   print('Estimated pixel number: ',(rng - grid.startingRange)/grid.rangePixelSpacing)

Note

The threshold parameter to rdr2geo_point determines the accuracy of the inversion. For precise location, use threshold on order of 1.0e-6. Default threshold at Python level is on order of cm, which is generally good enough for bounding box estimates.