From 3cee9785b658218ffb7520053c2274340a753cc5 Mon Sep 17 00:00:00 2001 From: jingluo Date: Wed, 1 Dec 2021 22:19:21 -0500 Subject: [PATCH 1/6] add disperse shift --- baseband_tasks/dispersion.py | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/baseband_tasks/dispersion.py b/baseband_tasks/dispersion.py index d3679f83..7b94fd80 100644 --- a/baseband_tasks/dispersion.py +++ b/baseband_tasks/dispersion.py @@ -182,3 +182,41 @@ def __init__(self, ih, dm, reference_frequency=None, @property def dm(self): return -self._dm + + +class DisperseSamples(SampleShift): + """Incoherently shift a time stream based on the disperse time delay. + + This task does not handle the in channel smearing, only shift the samples. + + Parameters + ---------- + ih : task or `baseband` stream reader + Input data stream, with time as the first axis. + dm : float or `~baseband_tasks.dm.DispersionMeasure` quantity + Dispersion measure. + reference_frequency : `~astropy.units.Quantity` + Frequency to which the data should be dedispersed. Can be an array. + By default, the mean frequency. If one doesn't want to change the + start time, choose the maximum frequency. + samples_per_frame : int, optional + Number of dedispersed samples which should be produced in one go. + The number of input samples used will be larger to avoid wrapping. + If not given, as produced by the minimum power of 2 of input + samples that yields at least 75% efficiency. + frequency : `~astropy.units.Quantity`, optional + Frequencies for each channel in ``ih`` (channelized frequencies will + be calculated). Default: taken from ``ih`` (if available). + """ + def __init__(self, ih, dm, reference_frequency=None, + samples_per_frame=None, frequency=None): + # Compute the time shift + dm = DispersionMeasure(dm) + # The sample disperse does not handle the sidebands, since it does not + # handle the in channel smearing. + frequency = getattr_if_none(ih, 'frequency', frequency) + reference_frequency = frequency.mean() + + # Treat the input frequency as the time delay frequency. + time_delay = dm.time_delay(freqency, reference_frequency) + super().__init__(ih, time_delay, samples_per_frame=samples_per_frame) From 0fd6f1e9ac53b94c20bcaaca8ce3856b8d1f485a Mon Sep 17 00:00:00 2001 From: Jing Luo Date: Mon, 6 Dec 2021 12:04:07 -0500 Subject: [PATCH 2/6] add frequency passing, tests and fix bugs --- baseband_tasks/dispersion.py | 28 ++++++++++----- baseband_tasks/tests/test_dispersion.py | 46 ++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/baseband_tasks/dispersion.py b/baseband_tasks/dispersion.py index 7b94fd80..f186710e 100644 --- a/baseband_tasks/dispersion.py +++ b/baseband_tasks/dispersion.py @@ -4,12 +4,13 @@ from astropy import units as u from astropy.utils import lazyproperty -from .base import PaddedTaskBase, getattr_if_none +from .base import PaddedTaskBase, getattr_if_none, SetAttribute from .fourier import fft_maker from .dm import DispersionMeasure +from .sampling import ShiftSamples -__all__ = ['Disperse', 'Dedisperse'] +__all__ = ['Disperse', 'Dedisperse', 'DisperseSamples'] class Disperse(PaddedTaskBase): @@ -184,7 +185,7 @@ def dm(self): return -self._dm -class DisperseSamples(SampleShift): +class DisperseSamples(ShiftSamples): """Incoherently shift a time stream based on the disperse time delay. This task does not handle the in channel smearing, only shift the samples. @@ -205,18 +206,29 @@ class DisperseSamples(SampleShift): If not given, as produced by the minimum power of 2 of input samples that yields at least 75% efficiency. frequency : `~astropy.units.Quantity`, optional - Frequencies for each channel in ``ih`` (channelized frequencies will - be calculated). Default: taken from ``ih`` (if available). + Frequencies for each channel in ``ih``. + Default: taken from ``ih`` (if available). + sideband : array, optional + Whether frequencies in ``ih`` are upper (+1) or lower (-1) sideband. + Default: taken from ``ih`` (if available). This class does not use + the ``sideband``, but for pairing with frequency in the metadata for + other tasks. """ def __init__(self, ih, dm, reference_frequency=None, - samples_per_frame=None, frequency=None): + samples_per_frame=None, frequency=None, sideband=None): # Compute the time shift dm = DispersionMeasure(dm) # The sample disperse does not handle the sidebands, since it does not # handle the in channel smearing. frequency = getattr_if_none(ih, 'frequency', frequency) - reference_frequency = frequency.mean() + if reference_frequency is None: + reference_frequency = frequency.mean() # Treat the input frequency as the time delay frequency. - time_delay = dm.time_delay(freqency, reference_frequency) + time_delay = dm.time_delay(frequency, reference_frequency) super().__init__(ih, time_delay, samples_per_frame=samples_per_frame) + self.reference_frequency = reference_frequency + self._dm = dm + # Put the metadata back if the user inputs the frequency. + if frequency is not None or sideband is not None: + ih = SetAttribute(ih, frequency=frequency, sideband=sideband) diff --git a/baseband_tasks/tests/test_dispersion.py b/baseband_tasks/tests/test_dispersion.py index 3e5d8159..5bec1131 100644 --- a/baseband_tasks/tests/test_dispersion.py +++ b/baseband_tasks/tests/test_dispersion.py @@ -6,7 +6,8 @@ from astropy.tests.helper import assert_quantity_allclose from baseband_tasks.fourier import fft_maker -from baseband_tasks.dispersion import Disperse, Dedisperse, DispersionMeasure +from baseband_tasks.dispersion import (Disperse, Dedisperse, DispersionMeasure, + DisperseSamples) from baseband_tasks.generators import StreamGenerator @@ -287,3 +288,46 @@ def test_disperse_negative_dm(self): # Lower sideband [1] is dedispersed to earlier. assert p[10, 0] > 0.99 and p[9, 0] < 0.006 assert p[9, 1] > 0.99 and p[10, 1] < 0.006 + + +class TestDispersSample(TestDispersion): + + @pytest.mark.parametrize('reference_frequency', REFERENCE_FREQUENCIES) + def test_disperse_sample(self, reference_frequency): + disperse = DisperseSamples(self.gp, self.dm, + reference_frequency=reference_frequency) + + # Seek input time of the giant pulse, corrected to the reference + # frequency, and read around it. + t_gp = (self.start_time + self.gp_sample / self.sample_rate + + self.dm.time_delay(300. * u.MHz, + disperse.reference_frequency)) + disperse.seek(t_gp) + disperse.seek(-self.gp_sample // 2, 1) + around_gp = disperse.read(self.gp_sample) + # Power in 20 bins of 0.025 s around the giant pulse. + p = (np.abs(around_gp) ** 2).reshape( + -1, 10, self.gp_sample // 20 // 10, 2).sum(2) + # Note: FT leakage means that not everything outside of the dispersed + # pulse is zero. But the total power there is small. + assert np.all(p[:9].sum(1) < 0.005) + assert np.all(p[11:].sum(1) < 0.005) + assert np.all(p[9:11].sum() > 0.99) + if disperse.reference_frequency != disperse.frequency: + assert not np.all(disperse._shift == 0) + + @pytest.mark.parametrize('reference_frequency', REFERENCE_FREQUENCIES) + def test_disperse_roundtrip1(self, reference_frequency): + self.gp.seek(self.start_time + 0.5 * u.s) + self.gp.seek(-1024, 1) + gp = self.gp.read(2048) + # Set up dispersion as above, and check that one can invert + disperse = DisperseSamples(self.gp, self.dm, + reference_frequency=reference_frequency) + dedisperse = DisperseSamples(disperse, -self.dm, + reference_frequency=reference_frequency) + dedisperse.seek(self.start_time + self.gp_sample / self.sample_rate) + dedisperse.seek(-1024, 1) + gp_dd = dedisperse.read(2048) + # Note: rounding errors mean this doesn't work perfectly. + assert np.all(np.abs(gp_dd - gp) < 1.e-4) From f5aad7e79669e9223791108651886b8bc8f2d307 Mon Sep 17 00:00:00 2001 From: Marten van Kerkwijk Date: Mon, 6 Dec 2021 15:31:53 -0500 Subject: [PATCH 3/6] Use SetAttribute at start; introduce DedisperseSamples --- baseband_tasks/dispersion.py | 99 +++++++++++++++++++++++++++++------- 1 file changed, 81 insertions(+), 18 deletions(-) diff --git a/baseband_tasks/dispersion.py b/baseband_tasks/dispersion.py index f186710e..a661d9de 100644 --- a/baseband_tasks/dispersion.py +++ b/baseband_tasks/dispersion.py @@ -10,7 +10,7 @@ from .sampling import ShiftSamples -__all__ = ['Disperse', 'Dedisperse', 'DisperseSamples'] +__all__ = ['Disperse', 'Dedisperse', 'DisperseSamples', 'DedisperseSamples'] class Disperse(PaddedTaskBase): @@ -41,6 +41,8 @@ class Disperse(PaddedTaskBase): See Also -------- baseband_tasks.fourier.fft_maker : to select the FFT package used. + baseband_tasks.dispersion.Dedisperse : for coherent dedispersion + baseband_tasks.dispersion.DisperseSamples : for incoherent dispersion """ def __init__(self, ih, dm, *, reference_frequency=None, @@ -171,7 +173,8 @@ class Dedisperse(Disperse): See Also -------- baseband_tasks.fourier.fft_maker : to select the FFT package used. - + baseband_tasks.dispersion.Disperse : for coherent dispersion + baseband_tasks.dispersion.DedisperseSamples : for incoherent dedispersion """ def __init__(self, ih, dm, reference_frequency=None, @@ -186,22 +189,23 @@ def dm(self): class DisperseSamples(ShiftSamples): - """Incoherently shift a time stream based on the disperse time delay. + """Incoherently shift a time stream to give it a dispersive time delay. - This task does not handle the in channel smearing, only shift the samples. + This task does not handle any in-channel dispersive smearing, but only + shifts the samples according to the mid-channel frequency. Parameters ---------- ih : task or `baseband` stream reader Input data stream, with time as the first axis. dm : float or `~baseband_tasks.dm.DispersionMeasure` quantity - Dispersion measure. + Dispersion measure to disperse with. If negative, will dedisperse, + but clearer to use `~baseband_tasks.dispersion.DedisperseSamples`. reference_frequency : `~astropy.units.Quantity` - Frequency to which the data should be dedispersed. Can be an array. - By default, the mean frequency. If one doesn't want to change the - start time, choose the maximum frequency. + Frequency to which the data should be dispersed. Can be an array. + By default, the mean frequency. samples_per_frame : int, optional - Number of dedispersed samples which should be produced in one go. + Number of dispersed samples which should be produced in one go. The number of input samples used will be larger to avoid wrapping. If not given, as produced by the minimum power of 2 of input samples that yields at least 75% efficiency. @@ -210,17 +214,31 @@ class DisperseSamples(ShiftSamples): Default: taken from ``ih`` (if available). sideband : array, optional Whether frequencies in ``ih`` are upper (+1) or lower (-1) sideband. - Default: taken from ``ih`` (if available). This class does not use - the ``sideband``, but for pairing with frequency in the metadata for - other tasks. + Default: taken from ``ih`` (if available). Note that while this is + only used if the data is real (to calculate the mid-channel + frequency), it should always be passed in together with ``frequency``, + since otherwise other tasks cannot interpret frequency correctly. + + See Also + -------- + baseband_tasks.dispersion.DedisperseSamples : for incoherent dedispersion + baseband_tasks.dispersion.Disperse : for coherent dispersion """ + def __init__(self, ih, dm, reference_frequency=None, samples_per_frame=None, frequency=None, sideband=None): # Compute the time shift dm = DispersionMeasure(dm) - # The sample disperse does not handle the sidebands, since it does not - # handle the in channel smearing. - frequency = getattr_if_none(ih, 'frequency', frequency) + + # Set possible missing frequency/sideband attributes + if frequency is not None or sideband is not None: + ih = SetAttribute(ih, frequency=frequency, sideband=sideband) + frequency = ih.frequency + if not ih.complex_data: + # Calculate mid-channel frequency for real data (for complex, + # the frequencies are already mid-channel). + frequency = frequency + ih.sideband * ih.sample_rate / 2. + if reference_frequency is None: reference_frequency = frequency.mean() @@ -229,6 +247,51 @@ def __init__(self, ih, dm, reference_frequency=None, super().__init__(ih, time_delay, samples_per_frame=samples_per_frame) self.reference_frequency = reference_frequency self._dm = dm - # Put the metadata back if the user inputs the frequency. - if frequency is not None or sideband is not None: - ih = SetAttribute(ih, frequency=frequency, sideband=sideband) + + +class DedisperseSamples(ShiftSamples): + """Incoherently shift a time stream to correct for a dispersive time delay. + + This task does not handle any in-channel dispersive smearing, but only + shifts the samples according to the mid-channel frequency. + + Parameters + ---------- + ih : task or `baseband` stream reader + Input data stream, with time as the first axis. + dm : float or `~baseband_tasks.dm.DispersionMeasure` quantity + Dispersion measure to correct for. If negative, will disperse, + but clearer to use `~baseband_tasks.dispersion.DisperseSamples`. + reference_frequency : `~astropy.units.Quantity` + Frequency to which the data should be dispersed. Can be an array. + By default, the mean frequency. + samples_per_frame : int, optional + Number of dispersed samples which should be produced in one go. + The number of input samples used will be larger to avoid wrapping. + If not given, as produced by the minimum power of 2 of input + samples that yields at least 75% efficiency. + frequency : `~astropy.units.Quantity`, optional + Frequencies for each channel in ``ih``. + Default: taken from ``ih`` (if available). + sideband : array, optional + Whether frequencies in ``ih`` are upper (+1) or lower (-1) sideband. + Default: taken from ``ih`` (if available). Note that while this is + only used if the data is real (to calculate the mid-channel + frequency), it should always be passed in together with ``frequency``, + since otherwise other tasks cannot interpret frequency correctly. + + See Also + -------- + baseband_tasks.dispersion.DisperseSamples : for incoherent dispersion + baseband_tasks.dispersion.Dedisperse : for coherent dedispersion + """ + + def __init__(self, ih, dm, reference_frequency=None, + samples_per_frame=None, frequency=None, sideband=None): + super().__init__(ih, -dm, reference_frequency=reference_frequency, + samples_per_frame=samples_per_frame, + frequency=frequency, sideband=sideband) + + @property + def dm(self): + return -self._dm From ff461674b7e580530095e5092469a8cc3a34b1b2 Mon Sep 17 00:00:00 2001 From: Jing Luo Date: Tue, 7 Dec 2021 14:30:33 -0500 Subject: [PATCH 4/6] update tests --- baseband_tasks/dispersion.py | 2 +- baseband_tasks/tests/test_dispersion.py | 55 +++++++++++++++---------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/baseband_tasks/dispersion.py b/baseband_tasks/dispersion.py index a661d9de..c6a47d42 100644 --- a/baseband_tasks/dispersion.py +++ b/baseband_tasks/dispersion.py @@ -249,7 +249,7 @@ def __init__(self, ih, dm, reference_frequency=None, self._dm = dm -class DedisperseSamples(ShiftSamples): +class DedisperseSamples(DisperseSamples): """Incoherently shift a time stream to correct for a dispersive time delay. This task does not handle any in-channel dispersive smearing, but only diff --git a/baseband_tasks/tests/test_dispersion.py b/baseband_tasks/tests/test_dispersion.py index 5bec1131..322ff874 100644 --- a/baseband_tasks/tests/test_dispersion.py +++ b/baseband_tasks/tests/test_dispersion.py @@ -7,7 +7,7 @@ from baseband_tasks.fourier import fft_maker from baseband_tasks.dispersion import (Disperse, Dedisperse, DispersionMeasure, - DisperseSamples) + DisperseSamples, DedisperseSamples) from baseband_tasks.generators import StreamGenerator @@ -290,31 +290,39 @@ def test_disperse_negative_dm(self): assert p[9, 1] > 0.99 and p[10, 1] < 0.006 -class TestDispersSample(TestDispersion): +class TestDispersSample(TestDispersionReal): @pytest.mark.parametrize('reference_frequency', REFERENCE_FREQUENCIES) - def test_disperse_sample(self, reference_frequency): - disperse = DisperseSamples(self.gp, self.dm, - reference_frequency=reference_frequency) + @pytest.mark.parametrize('frequency, sideband', [(None, None), + ([199.936, 200.064]*u.MHz, + np.array((1, -1))), + ([200.064, 199.936]*u.MHz, + np.array((-1, 1)))]) + def test_disperse_sample(self, reference_frequency, frequency, sideband): + disperse = DisperseSamples(self.gp, self.dm, frequency=frequency, + reference_frequency=reference_frequency, + sideband=sideband) # Seek input time of the giant pulse, corrected to the reference # frequency, and read around it. + center_frequency = (disperse.frequency + disperse.sideband + * disperse.sample_rate / 2.) + time_delay = self.dm.time_delay(center_frequency, + disperse.reference_frequency) t_gp = (self.start_time + self.gp_sample / self.sample_rate - + self.dm.time_delay(300. * u.MHz, - disperse.reference_frequency)) - disperse.seek(t_gp) - disperse.seek(-self.gp_sample // 2, 1) + + time_delay) + + disperse.seek(t_gp.min()) + sample_shift_diff = ((time_delay.max() - time_delay.min()) + * self.sample_rate) + sample_shift_diff = np.round(sample_shift_diff.to(u.one)).astype(int) around_gp = disperse.read(self.gp_sample) - # Power in 20 bins of 0.025 s around the giant pulse. - p = (np.abs(around_gp) ** 2).reshape( - -1, 10, self.gp_sample // 20 // 10, 2).sum(2) - # Note: FT leakage means that not everything outside of the dispersed - # pulse is zero. But the total power there is small. - assert np.all(p[:9].sum(1) < 0.005) - assert np.all(p[11:].sum(1) < 0.005) - assert np.all(p[9:11].sum() > 0.99) - if disperse.reference_frequency != disperse.frequency: - assert not np.all(disperse._shift == 0) + + assert around_gp[0, t_gp.argmin()] == 1.0 + assert around_gp[0, t_gp.argmax()] != 1.0 + assert around_gp[sample_shift_diff, t_gp.argmax()] == 1.0 + assert around_gp[sample_shift_diff, t_gp.argmin()] != 1.0 + @pytest.mark.parametrize('reference_frequency', REFERENCE_FREQUENCIES) def test_disperse_roundtrip1(self, reference_frequency): @@ -324,10 +332,13 @@ def test_disperse_roundtrip1(self, reference_frequency): # Set up dispersion as above, and check that one can invert disperse = DisperseSamples(self.gp, self.dm, reference_frequency=reference_frequency) - dedisperse = DisperseSamples(disperse, -self.dm, - reference_frequency=reference_frequency) + dedisperse = DedisperseSamples(disperse, self.dm, + reference_frequency=reference_frequency) + assert dedisperse.dm == self.dm + assert dedisperse._dm == -self.dm dedisperse.seek(self.start_time + self.gp_sample / self.sample_rate) dedisperse.seek(-1024, 1) gp_dd = dedisperse.read(2048) # Note: rounding errors mean this doesn't work perfectly. - assert np.all(np.abs(gp_dd - gp) < 1.e-4) + assert np.any(gp_dd == 1.0) + assert np.all(gp_dd == gp) From 0bd8f82cc1c6980332b6b44ec7260100c14cfd8d Mon Sep 17 00:00:00 2001 From: Marten van Kerkwijk Date: Thu, 9 Dec 2021 21:17:16 -0500 Subject: [PATCH 5/6] Bit of cleanup, trying to make intent of tests even clearer. --- baseband_tasks/dispersion.py | 15 ++++---- baseband_tasks/tests/test_dispersion.py | 47 +++++++++++++------------ 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/baseband_tasks/dispersion.py b/baseband_tasks/dispersion.py index c6a47d42..c92aaf2a 100644 --- a/baseband_tasks/dispersion.py +++ b/baseband_tasks/dispersion.py @@ -177,7 +177,7 @@ class Dedisperse(Disperse): baseband_tasks.dispersion.DedisperseSamples : for incoherent dedispersion """ - def __init__(self, ih, dm, reference_frequency=None, + def __init__(self, ih, dm, *, reference_frequency=None, samples_per_frame=None, frequency=None, sideband=None): super().__init__(ih, -dm, reference_frequency=reference_frequency, samples_per_frame=samples_per_frame, @@ -225,11 +225,8 @@ class DisperseSamples(ShiftSamples): baseband_tasks.dispersion.Disperse : for coherent dispersion """ - def __init__(self, ih, dm, reference_frequency=None, + def __init__(self, ih, dm, *, reference_frequency=None, samples_per_frame=None, frequency=None, sideband=None): - # Compute the time shift - dm = DispersionMeasure(dm) - # Set possible missing frequency/sideband attributes if frequency is not None or sideband is not None: ih = SetAttribute(ih, frequency=frequency, sideband=sideband) @@ -242,9 +239,11 @@ def __init__(self, ih, dm, reference_frequency=None, if reference_frequency is None: reference_frequency = frequency.mean() - # Treat the input frequency as the time delay frequency. + # Compute the time shift and use it to set up ShiftSamples. + dm = DispersionMeasure(dm) time_delay = dm.time_delay(frequency, reference_frequency) super().__init__(ih, time_delay, samples_per_frame=samples_per_frame) + self.reference_frequency = reference_frequency self._dm = dm @@ -266,7 +265,7 @@ class DedisperseSamples(DisperseSamples): Frequency to which the data should be dispersed. Can be an array. By default, the mean frequency. samples_per_frame : int, optional - Number of dispersed samples which should be produced in one go. + Number of dedispersed samples which should be produced in one go. The number of input samples used will be larger to avoid wrapping. If not given, as produced by the minimum power of 2 of input samples that yields at least 75% efficiency. @@ -286,7 +285,7 @@ class DedisperseSamples(DisperseSamples): baseband_tasks.dispersion.Dedisperse : for coherent dedispersion """ - def __init__(self, ih, dm, reference_frequency=None, + def __init__(self, ih, dm, *, reference_frequency=None, samples_per_frame=None, frequency=None, sideband=None): super().__init__(ih, -dm, reference_frequency=reference_frequency, samples_per_frame=samples_per_frame, diff --git a/baseband_tasks/tests/test_dispersion.py b/baseband_tasks/tests/test_dispersion.py index 322ff874..5e2951a3 100644 --- a/baseband_tasks/tests/test_dispersion.py +++ b/baseband_tasks/tests/test_dispersion.py @@ -22,9 +22,8 @@ 299.872 * u.MHz) # Below lower edge -class TestDispersion: - - def setup(self): +class GiantPulseSetup: + def setup_class(self): self.start_time = Time('2010-11-12T13:14:15') self.sample_rate = 128. * u.kHz self.shape = (164000, 2) @@ -39,12 +38,16 @@ def setup(self): # Time delay of 0.05 s over 128 kHz band. self.dm = DispersionMeasure(1000.*0.05/0.039342251) + @classmethod def make_giant_pulse(self, sh): data = np.empty((sh.samples_per_frame,) + sh.shape[1:], sh.dtype) do_gp = sh.tell() + np.arange(sh.samples_per_frame) == self.gp_sample data[...] = do_gp[:, np.newaxis] return data + +class TestDispersion(GiantPulseSetup): + def test_time_delay(self): time_delay = self.dm.time_delay( self.gp.frequency - self.sample_rate / 2., @@ -190,8 +193,8 @@ def test_disperse_closing(self): assert 'phase_factor' not in disperse.__dict__ -class TestDispersionReal(TestDispersion): - def setup(self): +class GiantPulseSetupReal(GiantPulseSetup): + def setup_class(self): self.start_time = Time('2010-11-12T13:14:15') self.sample_rate = 256. * u.kHz self.shape = (328000, 2) @@ -208,6 +211,8 @@ def setup(self): # Time delay of 0.05 s over 128 kHz band. self.dm = DispersionMeasure(1000.*0.05/0.039342251) + +class TestDispersionReal(TestDispersion, GiantPulseSetupReal): # Override tests that do not simply work for the real data, # since the sample rate is twice as high. def test_time_delay(self): @@ -290,14 +295,13 @@ def test_disperse_negative_dm(self): assert p[9, 1] > 0.99 and p[10, 1] < 0.006 -class TestDispersSample(TestDispersionReal): +class TestDispersSample(GiantPulseSetupReal): @pytest.mark.parametrize('reference_frequency', REFERENCE_FREQUENCIES) - @pytest.mark.parametrize('frequency, sideband', [(None, None), - ([199.936, 200.064]*u.MHz, - np.array((1, -1))), - ([200.064, 199.936]*u.MHz, - np.array((-1, 1)))]) + @pytest.mark.parametrize('frequency, sideband', [ + (None, None), + ([199.936, 200.064]*u.MHz, np.array((1, -1))), # Far off from normal. + ([200.064, 199.936]*u.MHz, np.array((-1, -1)))]) def test_disperse_sample(self, reference_frequency, frequency, sideband): disperse = DisperseSamples(self.gp, self.dm, frequency=frequency, reference_frequency=reference_frequency, @@ -305,28 +309,27 @@ def test_disperse_sample(self, reference_frequency, frequency, sideband): # Seek input time of the giant pulse, corrected to the reference # frequency, and read around it. - center_frequency = (disperse.frequency + disperse.sideband - * disperse.sample_rate / 2.) + center_frequency = (disperse.frequency + + disperse.sideband * disperse.sample_rate / 2.) time_delay = self.dm.time_delay(center_frequency, disperse.reference_frequency) t_gp = (self.start_time + self.gp_sample / self.sample_rate + time_delay) disperse.seek(t_gp.min()) + around_gp = disperse.read(self.gp_sample) + sample_shift_diff = ((time_delay.max() - time_delay.min()) * self.sample_rate) sample_shift_diff = np.round(sample_shift_diff.to(u.one)).astype(int) - around_gp = disperse.read(self.gp_sample) - - assert around_gp[0, t_gp.argmin()] == 1.0 - assert around_gp[0, t_gp.argmax()] != 1.0 - assert around_gp[sample_shift_diff, t_gp.argmax()] == 1.0 - assert around_gp[sample_shift_diff, t_gp.argmin()] != 1.0 + expected = np.zeros_like(around_gp) + expected[0, t_gp.argmin()] = 1.0 + expected[sample_shift_diff, t_gp.argmax()] = 1.0 + assert np.all(around_gp == expected) - - @pytest.mark.parametrize('reference_frequency', REFERENCE_FREQUENCIES) + @pytest.mark.parametrize('reference_frequency', [200 * u.MHz, 300 * u.MHz]) def test_disperse_roundtrip1(self, reference_frequency): - self.gp.seek(self.start_time + 0.5 * u.s) + self.gp.seek(self.start_time + self.gp_sample / self.sample_rate) self.gp.seek(-1024, 1) gp = self.gp.read(2048) # Set up dispersion as above, and check that one can invert From e8b17591a067003dc64dcc3bbfb89a97ece69a99 Mon Sep 17 00:00:00 2001 From: Marten van Kerkwijk Date: Thu, 9 Dec 2021 21:18:17 -0500 Subject: [PATCH 6/6] Changelog entry --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 2dc020cd..22ddee32 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -12,6 +12,9 @@ New Features - Add a simpler ``ShiftSamples`` task that just shifts channels by integer number of samples. [#226, #235, #239] +- Add ability for incoherent dedispersion with ``DisperseSamples`` and + ``Dedispersamples``. [#238] + - Streams can now carry meta-data in a ``meta`` attribute. This includes information on ``frequency``, ``sideband``, and ``polarization``, all of which are stored in ``meta['__attributes__']`` entry (like astropy's