Beispiel #1
0
def make_doppler(cfg: Struct, frequency='A'):
    log.info("Generating Doppler LUT from pointing")
    orbit = get_orbit(cfg)
    attitude = get_attitude(cfg)
    dem = get_dem(cfg)
    opt = cfg.processing.doppler
    az = np.radians(opt.azimuth_boresight_deg)
    rawfiles = cfg.InputFileGroup.InputFilePath
    raw = Raw(hdf5file=rawfiles[0])
    side = raw.identification.lookDirection
    fc = raw.getCenterFrequency(frequency)
    wvl = isce.core.speed_of_light / fc

    epoch, t, r = get_total_grid(rawfiles, opt.spacing.azimuth,
                                 opt.spacing.range)
    t = convert_epoch(t, epoch, orbit.reference_epoch)
    dop = np.zeros((len(t), len(r)))
    for i, ti in enumerate(t):
        _, v = orbit.interpolate(ti)
        vi = np.linalg.norm(v)
        for j, rj in enumerate(r):
            sq = squint(ti, rj, orbit, attitude, side, angle=az, dem=dem,
                        **vars(opt.rdr2geo))
            dop[i,j] = squint_to_doppler(sq, wvl, vi)
    lut = LUT2d(np.asarray(r), t, dop, opt.interp_method, False)
    log.info(f"Constructed Doppler LUT for fc={fc} Hz.")
    return fc, lut
Beispiel #2
0
def get_attitude(cfg: Struct):
    log.info("Loading attitude")
    if cfg.DynamicAncillaryFileGroup.Pointing:
        log.warning("Ignoring input pointing file.  Using L0B attitude.")
    rawfiles = cfg.InputFileGroup.InputFilePath
    if len(rawfiles) > 1:
        raise NotImplementedError("Can't concatenate attitude data.")
    raw = Raw(hdf5file=rawfiles[0])
    return raw.getAttitude()
Beispiel #3
0
def get_orbit(cfg: Struct):
    log.info("Loading orbit")
    if cfg.DynamicAncillaryFileGroup.Orbit:
        log.warning("Ignoring input orbit file.  Using L0B orbits.")
    rawfiles = cfg.InputFileGroup.InputFilePath
    if len(rawfiles) > 1:
        raise NotImplementedError("Can't concatenate orbit data.")
    raw = Raw(hdf5file=rawfiles[0])
    return raw.getOrbit()
Beispiel #4
0
def computeBoundingPolygon(h5file: str, h: float = 0.0):
    """Compute bounding polygon given (an otherwise complete) NISAR L0B product.
    Uses a fixed height in lieu of a digital elevation model.
    """
    dem = isce3.geometry.DEMInterpolator(h)
    raw = Raw(hdf5file=h5file)
    orbit = raw.getOrbit()
    _, grid = raw.getRadarGrid()
    fc, doppler = make_doppler_lut([h5file], az=0.0)
    # Make sure we don't accidentally have an inconsistent wavelength.
    assert numpy.isclose(grid.wavelength, isce3.core.speed_of_light / fc)
    return isce3.geometry.get_geo_perimeter_wkt(grid, orbit, doppler, dem)
Beispiel #5
0
def get_total_grid_bounds(rawfiles: List[str], frequency='A'):
    times, ranges = [], []
    for fn in rawfiles:
        raw = Raw(hdf5file=fn)
        pol = raw.polarizations[frequency][0]
        ranges.append(raw.getRanges(frequency))
        times.append(raw.getPulseTimes(frequency, pol[0]))
    rmin = min(r[0] for r in ranges)
    rmax = max(r[-1] for r in ranges)
    dtmin = min(epoch + isce.core.TimeDelta(t[0]) for (epoch, t) in times)
    dtmax = max(epoch + isce.core.TimeDelta(t[-1]) for (epoch, t) in times)
    epoch = min(epoch for (epoch, t) in times)
    tmin = (dtmin - epoch).total_seconds()
    tmax = (dtmax - epoch).total_seconds()
    return epoch, tmin, tmax, rmin, rmax
Beispiel #6
0
def finalizeIdentification(h5file: str):
    """Add identification fields that depend on swath information:
    {"boundingPolygon"," zeroDopplerStartTime", "zeroDopplerEndTime"}
    """
    # NOTE Need complete product to use product reader class, so assume we've
    # already populated dummy values.
    epoch, t = Raw(hdf5file=h5file).getPulseTimes()
    t0 = (epoch + isce3.core.TimeDelta(t[0])).isoformat()
    t1 = (epoch + isce3.core.TimeDelta(t[-1])).isoformat()
    poly = computeBoundingPolygon(h5file)

    with h5py.File(h5file, 'r+') as fid:
        ident = fid["/science/LSAR/identification"]
        # Unlink datasets and create new ones to avoid silent truncation.
        del ident["boundingPolygon"]
        del ident["zeroDopplerStartTime"]
        del ident["zeroDopplerEndTime"]

        def additem(name, value, description):
            ds = ident.create_dataset(name, data=numpy.string_(value))
            ds.attrs["description"] = numpy.string_(description)

        additem(
            "boundingPolygon", poly,
            "OGR compatible WKT representation of bounding polygon of the image"
        )
        additem("zeroDopplerStartTime", t0, "Azimuth start time of product")
        additem("zeroDopplerEndTime", t1, "Azimuth stop time of product")
Beispiel #7
0
def get_chirp(cfg: Struct, raw: Raw, frequency: str):
    if cfg.DynamicAncillaryFileGroup.Waveform:
        log.warning("Ignoring input waveform file.  Using analytic chirp.")
    chirp = raw.getChirp(frequency)
    log.info(f"Chirp length = {len(chirp)}")
    window = get_window(cfg.processing.range_window, msg="Range window: ")
    chirp *= window(len(chirp))
    log.info("Normalizing chirp to unit white noise gain.")
    return chirp / np.linalg.norm(chirp)**2
Beispiel #8
0
def focus(runconfig):
    # Strip off two leading namespaces.
    cfg = runconfig.runconfig.groups
    rawfiles = cfg.InputFileGroup.InputFilePath
    if len(rawfiles) <= 0:
        raise IOError("need at least one raw data file")
    if len(rawfiles) > 1:
        raise NotImplementedError("mixed-mode processing not yet supported")

    raw = Raw(hdf5file=rawfiles[0])
    dem = get_dem(cfg)
    orbit = get_orbit(cfg)
    try:
        attitude = get_attitude(cfg)
    except:
        log.warning("Could not load attitude data.  Assuming zero Doppler.")
        attitude = None
        # XXX This makes for zero-sized dimensions in the Doppler metadata.
        fc_ref, dop_ref = 1.0, isce.core.LUT2d()
    else:
        fc_ref, dop_ref = make_doppler(cfg)
    zerodop = isce.core.LUT2d()
    azres = cfg.processing.azcomp.azimuth_resolution
    atmos = cfg.processing.dry_troposphere_model or "nodelay"
    kernel = get_kernel(cfg)
    scale = cfg.processing.encoding_scale_factor

    use_gpu = check_gpu_opts(cfg)
    if use_gpu:
        # Set the current CUDA device.
        device = isce.cuda.core.Device(cfg.worker.gpu_id)
        isce.cuda.core.set_device(device)

        log.info(f"Processing using CUDA device {device.id} ({device.name})")

        backproject = isce.cuda.focus.backproject
    else:
        backproject = isce.focus.backproject

    # Generate reference output grid based on highest bandwidth, always A.
    log.info(f"Available polarizations: {raw.polarizations}")
    txref = raw.polarizations["A"][0][0]
    pulse_times, raw_grid = raw.getRadarGrid(frequency="A", tx=txref)

    log.info(f"len(pulses) = {len(pulse_times)}")
    log.info("Raw grid is %s", raw_grid)
    # Different grids for frequency A and B.
    ogrid = dict(A = make_output_grid(cfg, raw_grid))
    log.info("Output grid A is %s", ogrid["A"])
    if "B" in raw.frequencies:
        # Ensure aligned grids between A and B by just using an integer skip.
        # Sample rate of A is always an integer multiple of B.
        rskip = int(np.round(raw.getRanges("B").spacing
            / raw.getRanges("A").spacing))
        ogrid["B"] = ogrid["A"][:, ::rskip]
        log.info("Output grid B is %s", ogrid["B"])

    polygon = isce.geometry.get_geo_perimeter_wkt(ogrid["A"], orbit,
                                                  zerodop, dem)

    fn = cfg.ProductPathGroup.SASOutputFile
    product = cfg.PrimaryExecutable.ProductType
    log.info(f"Creating output {product} product {fn}")
    slc = SLC(fn, mode="w", product=product)
    slc.set_orbit(orbit) # TODO acceleration, orbitType
    if attitude:
        slc.set_attitude(attitude, orbit.reference_epoch)
    slc.copy_identification(raw, polygon=polygon,
        track=cfg.Geometry.RelativeOrbitNumber,
        frame=cfg.Geometry.FrameNumber)

    # store metadata for each frequency
    dop = dict()
    for frequency in raw.frequencies:
        # TODO Find center frequencies after mode intersection.
        fc = raw.getCenterFrequency(frequency)
        dop[frequency] = scale_doppler(dop_ref, fc / fc_ref)
        slc.set_parameters(dop[frequency], orbit.reference_epoch, frequency)
        og = ogrid[frequency]
        t = og.sensing_start + np.arange(og.length) / og.prf
        r = og.starting_range + np.arange(og.width) * og.range_pixel_spacing
        slc.update_swath(t, og.ref_epoch, r, fc, frequency)

    # main processing loop
    channels = [(f, p) for f in raw.polarizations for p in raw.polarizations[f]]
    for frequency, pol in channels:
        log.info(f"Processing frequency{frequency} {pol}")
        rawdata = raw.getRawDataset(frequency, pol)
        log.info(f"Raw data shape = {rawdata.shape}")
        _, raw_grid = raw.getRadarGrid(frequency, tx=pol[0])
        fc = raw.getCenterFrequency(frequency)
        na = cfg.processing.rangecomp.block_size.azimuth
        nr = rawdata.shape[1]

        log.info("Generating chirp")
        chirp = get_chirp(cfg, raw, frequency)

        rcmode = parse_rangecomp_mode(cfg.processing.rangecomp.mode)
        log.info(f"Preparing range compressor with {rcmode}")
        rc = isce.focus.RangeComp(chirp, nr, maxbatch=na, mode=rcmode)

        # Rangecomp modifies range grid.  Also update wavelength.
        rc_grid = raw_grid.copy()
        rc_grid.starting_range -= (
            rc_grid.range_pixel_spacing * rc.first_valid_sample)
        rc_grid.width = rc.output_size
        rc_grid.wavelength = isce.core.speed_of_light / fc
        igeom = isce.container.RadarGeometry(rc_grid, orbit, dop[frequency])

        scratch = cfg.ProductPathGroup.ScratchPath
        fd = tempfile.NamedTemporaryFile(dir=scratch, suffix='.rc')
        log.info(f"Writing range compressed data to {fd.name}")
        rcfile = Raster(fd.name, rc.output_size, rawdata.shape[0], GDT_CFloat32)
        log.info(f"Range compressed data shape = {rcfile.data.shape}")

        for pulse in range(0, rawdata.shape[0], na):
            log.info(f"Range compressing block at pulse {pulse}")
            block = np.s_[pulse:pulse+na, :]
            # TODO fill invalid data during presum.
            ps = rawdata[block]
            ps[np.isnan(ps)] = 0.0
            rc.rangecompress(rcfile.data[block], ps)

        acdata = slc.create_image(frequency, pol, shape=ogrid[frequency].shape)

        nr = cfg.processing.azcomp.block_size.range
        na = cfg.processing.azcomp.block_size.azimuth

        if nr < 1:
            nr = ogrid[frequency].width

        if not cfg.processing.is_enabled.azcomp:
            continue

        for i in range(0, ogrid[frequency].length, na):
            # h5py doesn't follow usual slice rules, raises exception
            # if dest_sel slices extend past dataset shape.
            imax = min(i + na, ogrid[frequency].length)
            for j in range(0, ogrid[frequency].width, nr):
                jmax = min(j + nr, ogrid[frequency].width)
                block = np.s_[i:imax, j:jmax]
                log.info(f"Azcomp block at (i, j) = ({i}, {j})")
                bgrid = ogrid[frequency][block]
                ogeom = isce.container.RadarGeometry(bgrid, orbit, zerodop)
                z = np.zeros(bgrid.shape, 'c8')
                backproject(z, ogeom, rcfile.data, igeom, dem, fc, azres,
                            kernel, atmos, vars(cfg.processing.azcomp.rdr2geo),
                            vars(cfg.processing.azcomp.geo2rdr))
                log.debug(f"max(abs(z)) = {np.max(np.abs(z))}")
                zf = to_complex32(scale * z)
                acdata.write_direct(zf, dest_sel=block)
Beispiel #9
0
def make_doppler_lut(rawfiles: List[str],
                     az: float = 0.0,
                     orbit: isce.core.Orbit = None,
                     attitude: isce.core.Attitude = None,
                     dem: isce.geometry.DEMInterpolator = None,
                     azimuth_spacing: float = 1.0,
                     range_spacing: float = 1e3,
                     frequency: str = "A",
                     interp_method: str = "bilinear",
                     **rdr2geo):
    """Generate Doppler look up table (LUT).

    Parameters
    ----------
    rawfiles
        List of NISAR L0B format raw data files.
    az : optional
        Complement of the angle between the along-track axis of the antenna and
        its electrical boresight, in radians.  Zero for non-scanned, flush-
        mounted antennas like ALOS-1.
    orbit : optional
        Path of antenna phase center.  Defaults to orbit in first L0B file.
    attitude : optional
        Orientation of antenna.  Defaults to attitude in first L0B file.
    dem : optional
        Digital elevation model, height in m above WGS84 ellipsoid. Default=0 m.
    azimuth_spacing : optional
        LUT grid spacing in azimuth, in seconds.  Default=1 s.
    range_spacing : optional
        LUT grid spacing in range, in meters.  Default=1000 m.
    frequency : {"A", "B"}, optional
        Band to use.  Default="A"
    interp_method : optional
        LUT interpolation method. Default="bilinear".
    threshold : optional
    maxiter : optional
    extraiter : optional
        See rdr2geo

    Returns
    -------
    fc
        Center frequency, in Hz, assumed for Doppler calculation.
    LUT
        Look up table of Doppler = f(r,t)
    """
    # Input wrangling.
    assert len(rawfiles) > 0, "Need at least one L0B file."
    assert (azimuth_spacing > 0.0) and (range_spacing > 0.0)
    raw = Raw(hdf5file=rawfiles[0])
    if orbit is None:
        orbit = raw.getOrbit()
    if attitude is None:
        attitude = raw.getAttitude()
    if dem is None:
        dem = isce.geometry.DEMInterpolator()
    # Assume look side and center frequency constant across files.
    side = raw.identification.lookDirection
    fc = raw.getCenterFrequency(frequency)

    # Now do the actual calculations.
    wvl = isce.core.speed_of_light / fc
    epoch, t, r = get_total_grid(rawfiles, azimuth_spacing, range_spacing)
    t = convert_epoch(t, epoch, orbit.reference_epoch)
    dop = np.zeros((len(t), len(r)))
    for i, ti in enumerate(t):
        _, v = orbit.interpolate(ti)
        vi = np.linalg.norm(v)
        for j, rj in enumerate(r):
            sq = squint(ti,
                        rj,
                        orbit,
                        attitude,
                        side,
                        angle=az,
                        dem=dem,
                        **rdr2geo)
            dop[i, j] = squint_to_doppler(sq, wvl, vi)
    lut = LUT2d(np.asarray(r), t, dop, interp_method, False)
    return fc, lut