What is the structure of a GBT FITS file?


In [9]:
# Typical imports here
import numpy as np
import matplotlib.pyplot as plt
import astropy.units as u
import astropy.constants as c

In [10]:
from astropy.io import fits
from astropy.table import Table,join,vstack

In [11]:
import pickle
from pathlib import Path
homedir = str(Path.home())

Test using AMIGA dataset


In [12]:
fl = homedir+'/Dropbox/GBT-AMIGA/Data-Reduced/AMIGA-GBT.fits'

In [13]:
a=fits.open(fl)
np.shape(a)


Out[13]:
(50,)

There are 50 extensions, each potentially holding multiple sight lines.


In [14]:
a[0:5].info()


Filename: (No file associated with this HDUList)
No.    Name      Ver    Type      Cards   Dimensions   Format
  0  PRIMARY       1 PrimaryHDU       9   ()      
  1  SINGLE DISH    1 BinTableHDU    185   2R x 83C   [32A, D, 22A, D, D, D, 711E, 16A, 6A, 8A, D, D, D, 4A, D, 4A, D, I, 32A, 32A, J, 32A, 16A, D, 8A, D, D, D, D, D, D, D, D, D, D, D, D, D, 8A, D, D, 8A, I, I, D, D, I, A, I, I, 16A, 16A, J, J, 22A, D, D, I, A, D, D, E, D, D, D, D, A, A, 8A, E, E, 16A, 32A, 32A, 32A, D, D, D, I, I, I, J, J]   
  2  SINGLE DISH    1 BinTableHDU    185   1R x 83C   [32A, D, 22A, D, D, D, 1024E, 16A, 6A, 8A, D, D, D, 4A, D, 4A, D, I, 32A, 32A, J, 32A, 16A, D, 8A, D, D, D, D, D, D, D, D, D, D, D, D, D, 8A, D, D, 8A, I, I, D, D, I, A, I, I, 16A, 16A, J, J, 22A, D, D, I, A, D, D, E, D, D, D, D, A, A, 8A, E, E, 16A, 32A, 32A, 32A, D, D, D, I, I, I, J, J]   
  3  SINGLE DISH    1 BinTableHDU    185   3R x 83C   [32A, D, 22A, D, D, D, 711E, 16A, 6A, 8A, D, D, D, 4A, D, 4A, D, I, 32A, 32A, J, 32A, 16A, D, 8A, D, D, D, D, D, D, D, D, D, D, D, D, D, 8A, D, D, 8A, I, I, D, D, I, A, I, I, 16A, 16A, J, J, 22A, D, D, I, A, D, D, E, D, D, D, D, A, A, 8A, E, E, 16A, 32A, 32A, 32A, D, D, D, I, I, I, J, J]   
  4  SINGLE DISH    1 BinTableHDU    185   1R x 83C   [32A, D, 22A, D, D, D, 419E, 16A, 6A, 8A, D, D, D, 4A, D, 4A, D, I, 32A, 32A, J, 32A, 16A, D, 8A, D, D, D, D, D, D, D, D, D, D, D, D, D, 8A, D, D, 8A, I, I, D, D, I, A, I, I, 16A, 16A, J, J, 22A, D, D, I, A, D, D, E, D, D, D, D, A, A, 8A, E, E, 16A, 32A, 32A, 32A, D, D, D, I, I, I, J, J]   

Each extension contains an extensive binary table housing all the data.

Each extension itself has multiple objects?


In [15]:
x1 = a[1]
x2 = a[2]
x3 = a[48]
x1.columns


Out[15]:
ColDefs(
    name = 'OBJECT'; format = '32A'
    name = 'BANDWID'; format = 'D'
    name = 'DATE-OBS'; format = '22A'
    name = 'DURATION'; format = 'D'
    name = 'EXPOSURE'; format = 'D'
    name = 'TSYS'; format = 'D'
    name = 'DATA'; format = '711E'
    name = 'TDIM7'; format = '16A'
    name = 'TUNIT7'; format = '6A'
    name = 'CTYPE1'; format = '8A'
    name = 'CRVAL1'; format = 'D'
    name = 'CRPIX1'; format = 'D'
    name = 'CDELT1'; format = 'D'
    name = 'CTYPE2'; format = '4A'
    name = 'CRVAL2'; format = 'D'
    name = 'CTYPE3'; format = '4A'
    name = 'CRVAL3'; format = 'D'
    name = 'CRVAL4'; format = 'I'
    name = 'OBSERVER'; format = '32A'
    name = 'OBSID'; format = '32A'
    name = 'SCAN'; format = 'J'
    name = 'OBSMODE'; format = '32A'
    name = 'FRONTEND'; format = '16A'
    name = 'TCAL'; format = 'D'
    name = 'VELDEF'; format = '8A'
    name = 'VFRAME'; format = 'D'
    name = 'RVSYS'; format = 'D'
    name = 'OBSFREQ'; format = 'D'
    name = 'LST'; format = 'D'
    name = 'AZIMUTH'; format = 'D'
    name = 'ELEVATIO'; format = 'D'
    name = 'TAMBIENT'; format = 'D'
    name = 'PRESSURE'; format = 'D'
    name = 'HUMIDITY'; format = 'D'
    name = 'RESTFREQ'; format = 'D'
    name = 'DOPFREQ'; format = 'D'
    name = 'FREQRES'; format = 'D'
    name = 'EQUINOX'; format = 'D'
    name = 'RADESYS'; format = '8A'
    name = 'TRGTLONG'; format = 'D'
    name = 'TRGTLAT'; format = 'D'
    name = 'SAMPLER'; format = '8A'
    name = 'FEED'; format = 'I'
    name = 'SRFEED'; format = 'I'
    name = 'FEEDXOFF'; format = 'D'
    name = 'FEEDEOFF'; format = 'D'
    name = 'SUBREF_STATE'; format = 'I'
    name = 'SIDEBAND'; format = 'A'
    name = 'PROCSEQN'; format = 'I'
    name = 'PROCSIZE'; format = 'I'
    name = 'PROCSCAN'; format = '16A'
    name = 'PROCTYPE'; format = '16A'
    name = 'LASTON'; format = 'J'
    name = 'LASTOFF'; format = 'J'
    name = 'TIMESTAMP'; format = '22A'
    name = 'QD_XEL'; format = 'D'
    name = 'QD_EL'; format = 'D'
    name = 'QD_BAD'; format = 'I'
    name = 'QD_METHOD'; format = 'A'
    name = 'VELOCITY'; format = 'D'
    name = 'FOFFREF1'; format = 'D'
    name = 'ZEROCHAN'; format = 'E'
    name = 'ADCSAMPF'; format = 'D'
    name = 'VSPDELT'; format = 'D'
    name = 'VSPRVAL'; format = 'D'
    name = 'VSPRPIX'; format = 'D'
    name = 'SIG'; format = 'A'
    name = 'CAL'; format = 'A'
    name = 'CALTYPE'; format = '8A'
    name = 'TWARM'; format = 'E'
    name = 'TCOLD'; format = 'E'
    name = 'CALPOSITION'; format = '16A'
    name = 'BACKEND'; format = '32A'
    name = 'PROJID'; format = '32A'
    name = 'TELESCOP'; format = '32A'
    name = 'SITELONG'; format = 'D'
    name = 'SITELAT'; format = 'D'
    name = 'SITEELEV'; format = 'D'
    name = 'IFNUM'; format = 'I'
    name = 'PLNUM'; format = 'I'
    name = 'FDNUM'; format = 'I'
    name = 'INT'; format = 'J'
    name = 'NSAVE'; format = 'J'
)

Each extension is its own table, containing multiple targets


In [16]:
all_obj = []
for j in np.arange(1,np.size(a)):
    print("{0}: {1}".format(j,a[j].data['object']))
    all_obj.append(a[j].data['object'].tolist())


1: ['M31Halo_105_025' 'M31Halo_285_025']
2: ['RXJ0048.3+3941']
3: ['M31Halo_075_025' 'M31Halo_000_025' 'SDSSJ003125.36+403222.0']
4: ['HS0033+4300']
5: ['LAMOST005016.63+391639.0' 'LAMOST003432.52+391836.1'
 'LAMOST004706.09+384843.9' 'NPM1G+43.0016' 'M31Halo_075_040'
 'M31Halo_145_040' 'GALEXJ002912+433216' 'M31Halo_285_045'
 'M31Halo_200_045' 'M31Halo_000_045' 'M31Halo_105_045']
6: ['HS0058+4213']
7: ['RXSJ0043.6+372521']
8: ['SDSSJ002827.06+380942.0']
9: ['ZW535.012']
10: ['UVQSJ001903.85+423809.0' 'SDSSJ005547.99+451410.9']
11: ['Q0030+3700']
12: ['LAMOST005846.82+365514.2']
13: ['BLANK']
14: ['MS0108.4+3859']
15: ['RXSJ005050.6+353645']
16: ['GALEXJ001036+400314']
17: ['2E0111.0+3851']
18: ['M31Halo_000_085' 'M31Halo_210_085' 'M31Halo_030_085' 'M31Halo_330_085'
 'LAMOST011724.84+435335.9' 'MRK971']
19: ['IRAS00040+4325' 'RXSJ011848.2+383626']
20: ['HS0010+3611' 'SDSSJ012400.59+410709.7']
21: ['RXJ0117.7+3637' 'SDSSJ001847.44+341209.5']
22: ['SDSSJ012048.92+464344.9' 'SDSSJ013115.62+411704.3'
 'SDSSJ000443.83+353755.0' 'M31Halo_105_130' 'M31Halo_295_130'
 'M31Halo_075_130']
23: ['MRK352']
24: ['M31Halo_030_135' 'M31Halo_270_135' 'M31Halo_000_135']
25: ['RXJ0028.1+3103']
26: ['LAMOST011500.94+324519.5']
27: ['NGC513']
28: ['4C49.48' 'NGC315']
29: ['KAZ238' 'MRK1158']
30: ['4C44.04']
31: ['2MASSJ00413+2816']
32: ['SDSSJ233811.91+472323.5' 'M31Halo_000_175']
33: ['FBS0150+396']
34: ['LAMOST012400.31+304003.0']
35: ['3C48']
36: ['UVQSJ005617.72+275349.7' 'CGCG498-038']
37: ['4C25.01' 'PG0052+251']
38: ['2MASSJ00294+2424' 'RXSJ015536.7+311525']
39: ['RBS2055' 'RBS2061']
40: ['UVQSJ021621.18+371028.3']
41: ['3C66A']
42: ['RXJ0053.7+2232' 'MRK930']
43: ['RXJ0048.7+2127' 'MRK357' '3C59']
44: ['PG0117+213']
45: ['HS0137+2329' 'KUV02196+3253']
46: ['RXJ0029.0+1957']
47: ['RXJ0044.9+1921' 'MRK335' 'UGC1098' 'RXSJ023231.4+340435'
 'RXSJ225148.5+341937']
48: ['MRK1148']
49: ['RBS2005' 'MRK1179' 'PG0003+158' 'UGC12163']

We can create an astropy table from the input data:


In [17]:
Table(x2.data)


Out[17]:
Table length=1
OBJECTBANDWIDDATE-OBSDURATIONEXPOSURETSYSDATA [1024]TDIM7TUNIT7CTYPE1CRVAL1CRPIX1CDELT1CTYPE2CRVAL2CTYPE3CRVAL3CRVAL4OBSERVEROBSIDSCANOBSMODEFRONTENDTCALVELDEFVFRAMERVSYSOBSFREQLSTAZIMUTHELEVATIOTAMBIENTPRESSUREHUMIDITYRESTFREQDOPFREQFREQRESEQUINOXRADESYSTRGTLONGTRGTLATSAMPLERFEEDSRFEEDFEEDXOFFFEEDEOFFSUBREF_STATESIDEBANDPROCSEQNPROCSIZEPROCSCANPROCTYPELASTONLASTOFFTIMESTAMPQD_XELQD_ELQD_BADQD_METHODVELOCITYFOFFREF1ZEROCHANADCSAMPFVSPDELTVSPRVALVSPRPIXSIGCALCALTYPETWARMTCOLDCALPOSITIONBACKENDPROJIDTELESCOPSITELONGSITELATSITEELEVIFNUMPLNUMFDNUMINTNSAVE
str32float64str22float64float64float64float32str16str6str8float64float64float64str4float64str4float64int16str32str32int32str32str16float64str8float64float64float64float64float64float64float64float64float64float64float64float64float64str8float64float64str8int16int16float64float64int16str1int16int16str16str16int32int32str22float64float64int16str1float64float64float32float64float64float64float64str1str1str8float32float32str16str32str32str32float64float64float64int16int16int16int32int32
RXJ0048.3+394123437500.02014-09-26T03:27:43.519332.4316406258963.754882812518.8296699523925780.110209204 .. -0.14185081(1024,1,1,1)Ta*FREQ-OBS1421608409.0512.875-22888.18359375RA12.080509703857576DEC39.687289029721384-5D.J. Pisanounknown73Track:NONE:TPWCALRcvr1_21.809999942779541OPTI-BAR-10629.598269278640.01421608409.080871.8351195271775.9426208892631362.73957852304777285.45001220703125699.98024215636490.97799998521804811420405752.01420405752.022799.1245583948662000.0FK512.0791666666666739.68638888888889A1_0100.00.01L11ONSIMPLE002014_09_26_03:27:43nannan-1-243000.00.0nannannannannanTFLOWnannanUnknownVEGASAGBT14B_436_01NRAO_GBT-79.8398338.43312824.5950100-1

Create a spectrum


In [18]:
b=x2.data[0]
nu0=b['RESTFREQ']
nu = ((np.arange(np.size(b['DATA']))+1)-b['CRPIX1'])*b['CDELT1'] + b['CRVAL1']

In [19]:
vel = (nu0-nu)/nu0 * c.c.to('km/s')
Ta = b['DATA']

In [20]:
plt.figure(figsize=(10,5))
plt.plot(vel,Ta)
plt.xlim(-800,500)
plt.ylim(-0.05,0.1)
plt.axhline(0.,linestyle='--',color='k',linewidth=1)
plt.title(b['OBJECT']);


Find a specific object:


In [13]:
fnd_obj = 'RBS2055'
for j in np.arange(1,np.size(a)):
    xxx = a[j].data
    gd=(xxx['OBJECT'] == fnd_obj)
    if gd.sum() > 0:
        out = xxx[gd]

In [14]:
b=out.copy()

In [15]:
b.columns


Out[15]:
ColDefs(
    name = 'OBJECT'; format = '32A'
    name = 'BANDWID'; format = 'D'
    name = 'DATE-OBS'; format = '22A'
    name = 'DURATION'; format = 'D'
    name = 'EXPOSURE'; format = 'D'
    name = 'TSYS'; format = 'D'
    name = 'DATA'; format = '430E'
    name = 'TDIM7'; format = '16A'
    name = 'TUNIT7'; format = '6A'
    name = 'CTYPE1'; format = '8A'
    name = 'CRVAL1'; format = 'D'
    name = 'CRPIX1'; format = 'D'
    name = 'CDELT1'; format = 'D'
    name = 'CTYPE2'; format = '4A'
    name = 'CRVAL2'; format = 'D'
    name = 'CTYPE3'; format = '4A'
    name = 'CRVAL3'; format = 'D'
    name = 'CRVAL4'; format = 'I'
    name = 'OBSERVER'; format = '32A'
    name = 'OBSID'; format = '32A'
    name = 'SCAN'; format = 'J'
    name = 'OBSMODE'; format = '32A'
    name = 'FRONTEND'; format = '16A'
    name = 'TCAL'; format = 'D'
    name = 'VELDEF'; format = '8A'
    name = 'VFRAME'; format = 'D'
    name = 'RVSYS'; format = 'D'
    name = 'OBSFREQ'; format = 'D'
    name = 'LST'; format = 'D'
    name = 'AZIMUTH'; format = 'D'
    name = 'ELEVATIO'; format = 'D'
    name = 'TAMBIENT'; format = 'D'
    name = 'PRESSURE'; format = 'D'
    name = 'HUMIDITY'; format = 'D'
    name = 'RESTFREQ'; format = 'D'
    name = 'DOPFREQ'; format = 'D'
    name = 'FREQRES'; format = 'D'
    name = 'EQUINOX'; format = 'D'
    name = 'RADESYS'; format = '8A'
    name = 'TRGTLONG'; format = 'D'
    name = 'TRGTLAT'; format = 'D'
    name = 'SAMPLER'; format = '8A'
    name = 'FEED'; format = 'I'
    name = 'SRFEED'; format = 'I'
    name = 'FEEDXOFF'; format = 'D'
    name = 'FEEDEOFF'; format = 'D'
    name = 'SUBREF_STATE'; format = 'I'
    name = 'SIDEBAND'; format = 'A'
    name = 'PROCSEQN'; format = 'I'
    name = 'PROCSIZE'; format = 'I'
    name = 'PROCSCAN'; format = '16A'
    name = 'PROCTYPE'; format = '16A'
    name = 'LASTON'; format = 'J'
    name = 'LASTOFF'; format = 'J'
    name = 'TIMESTAMP'; format = '22A'
    name = 'QD_XEL'; format = 'D'
    name = 'QD_EL'; format = 'D'
    name = 'QD_BAD'; format = 'I'
    name = 'QD_METHOD'; format = 'A'
    name = 'VELOCITY'; format = 'D'
    name = 'FOFFREF1'; format = 'D'
    name = 'ZEROCHAN'; format = 'E'
    name = 'ADCSAMPF'; format = 'D'
    name = 'VSPDELT'; format = 'D'
    name = 'VSPRVAL'; format = 'D'
    name = 'VSPRPIX'; format = 'D'
    name = 'SIG'; format = 'A'
    name = 'CAL'; format = 'A'
    name = 'CALTYPE'; format = '8A'
    name = 'TWARM'; format = 'E'
    name = 'TCOLD'; format = 'E'
    name = 'CALPOSITION'; format = '16A'
    name = 'BACKEND'; format = '32A'
    name = 'PROJID'; format = '32A'
    name = 'TELESCOP'; format = '32A'
    name = 'SITELONG'; format = 'D'
    name = 'SITELAT'; format = 'D'
    name = 'SITEELEV'; format = 'D'
    name = 'IFNUM'; format = 'I'
    name = 'PLNUM'; format = 'I'
    name = 'FDNUM'; format = 'I'
    name = 'INT'; format = 'J'
    name = 'NSAVE'; format = 'J'
)

Create an index


In [16]:
# Counter variable
i=0
# Outputs
indx=[]
object_names = []
array_indeces = []

for j in np.arange(1,np.size(a)):
    xxx = a[j].data
    for k in np.arange(np.size(xxx)):
        indx.append(i)
        object_names.append(xxx['OBJECT'][k])
        array_indeces.append((j,k))

        print("{0}: {1:20s}   \t{2}".format(i,
            object_names[i],array_indeces[i]))
        # Advance the counter
        i+=1


0: M31Halo_105_025        	(1, 0)
1: M31Halo_285_025        	(1, 1)
2: RXJ0048.3+3941         	(2, 0)
3: M31Halo_075_025        	(3, 0)
4: M31Halo_000_025        	(3, 1)
5: SDSSJ003125.36+403222.0   	(3, 2)
6: HS0033+4300            	(4, 0)
7: LAMOST005016.63+391639.0   	(5, 0)
8: LAMOST003432.52+391836.1   	(5, 1)
9: LAMOST004706.09+384843.9   	(5, 2)
10: NPM1G+43.0016          	(5, 3)
11: M31Halo_075_040        	(5, 4)
12: M31Halo_145_040        	(5, 5)
13: GALEXJ002912+433216    	(5, 6)
14: M31Halo_285_045        	(5, 7)
15: M31Halo_200_045        	(5, 8)
16: M31Halo_000_045        	(5, 9)
17: M31Halo_105_045        	(5, 10)
18: HS0058+4213            	(6, 0)
19: RXSJ0043.6+372521      	(7, 0)
20: SDSSJ002827.06+380942.0   	(8, 0)
21: ZW535.012              	(9, 0)
22: UVQSJ001903.85+423809.0   	(10, 0)
23: SDSSJ005547.99+451410.9   	(10, 1)
24: Q0030+3700             	(11, 0)
25: LAMOST005846.82+365514.2   	(12, 0)
26: BLANK                  	(13, 0)
27: MS0108.4+3859          	(14, 0)
28: RXSJ005050.6+353645    	(15, 0)
29: GALEXJ001036+400314    	(16, 0)
30: 2E0111.0+3851          	(17, 0)
31: M31Halo_000_085        	(18, 0)
32: M31Halo_210_085        	(18, 1)
33: M31Halo_030_085        	(18, 2)
34: M31Halo_330_085        	(18, 3)
35: LAMOST011724.84+435335.9   	(18, 4)
36: MRK971                 	(18, 5)
37: IRAS00040+4325         	(19, 0)
38: RXSJ011848.2+383626    	(19, 1)
39: HS0010+3611            	(20, 0)
40: SDSSJ012400.59+410709.7   	(20, 1)
41: RXJ0117.7+3637         	(21, 0)
42: SDSSJ001847.44+341209.5   	(21, 1)
43: SDSSJ012048.92+464344.9   	(22, 0)
44: SDSSJ013115.62+411704.3   	(22, 1)
45: SDSSJ000443.83+353755.0   	(22, 2)
46: M31Halo_105_130        	(22, 3)
47: M31Halo_295_130        	(22, 4)
48: M31Halo_075_130        	(22, 5)
49: MRK352                 	(23, 0)
50: M31Halo_030_135        	(24, 0)
51: M31Halo_270_135        	(24, 1)
52: M31Halo_000_135        	(24, 2)
53: RXJ0028.1+3103         	(25, 0)
54: LAMOST011500.94+324519.5   	(26, 0)
55: NGC513                 	(27, 0)
56: 4C49.48                	(28, 0)
57: NGC315                 	(28, 1)
58: KAZ238                 	(29, 0)
59: MRK1158                	(29, 1)
60: 4C44.04                	(30, 0)
61: 2MASSJ00413+2816       	(31, 0)
62: SDSSJ233811.91+472323.5   	(32, 0)
63: M31Halo_000_175        	(32, 1)
64: FBS0150+396            	(33, 0)
65: LAMOST012400.31+304003.0   	(34, 0)
66: 3C48                   	(35, 0)
67: UVQSJ005617.72+275349.7   	(36, 0)
68: CGCG498-038            	(36, 1)
69: 4C25.01                	(37, 0)
70: PG0052+251             	(37, 1)
71: 2MASSJ00294+2424       	(38, 0)
72: RXSJ015536.7+311525    	(38, 1)
73: RBS2055                	(39, 0)
74: RBS2061                	(39, 1)
75: UVQSJ021621.18+371028.3   	(40, 0)
76: 3C66A                  	(41, 0)
77: RXJ0053.7+2232         	(42, 0)
78: MRK930                 	(42, 1)
79: RXJ0048.7+2127         	(43, 0)
80: MRK357                 	(43, 1)
81: 3C59                   	(43, 2)
82: PG0117+213             	(44, 0)
83: HS0137+2329            	(45, 0)
84: KUV02196+3253          	(45, 1)
85: RXJ0029.0+1957         	(46, 0)
86: RXJ0044.9+1921         	(47, 0)
87: MRK335                 	(47, 1)
88: UGC1098                	(47, 2)
89: RXSJ023231.4+340435    	(47, 3)
90: RXSJ225148.5+341937    	(47, 4)
91: MRK1148                	(48, 0)
92: RBS2005                	(49, 0)
93: MRK1179                	(49, 1)
94: PG0003+158             	(49, 2)
95: UGC12163               	(49, 3)

In [17]:
tbl = Table([indx,object_names,array_indeces],
                        names=['INDX','OBJECT','ARRAY_INDECES'])

In [18]:
tbl[0:5]


Out[18]:
Table length=5
INDXOBJECTARRAY_INDECES [2]
int64str24int64
0M31Halo_105_0251 .. 0
1M31Halo_285_0251 .. 1
2RXJ0048.3+39412 .. 0
3M31Halo_075_0253 .. 0
4M31Halo_000_0253 .. 1

In [19]:
tbl['ARRAY_INDECES'][5]


Out[19]:
array([3, 2])

In [22]:
tbl[tbl.colnames[0]][0:5]


Out[22]:
<Column name='INDX' dtype='int64' length=5>
0
1
2
3
4

An aside: testing valid inputs


In [20]:
def example():
    max_time = 7
    
    # Calls for an infinite loop that keeps executing
    # until an exception occurs
    valid = False
    while valid == False:
        try:
            test4num = int(input("From 1 to 7, how many hours do you play in your mobile?" ))

        # If something else that is not the string
        # version of a number is introduced, the
        # ValueError exception will be called.
        except:
            # The cycle will go on until validation
            print("Error! This is not a number. Try again.")

        # When successfully converted to an integer,
        # the loop will end.
        if test4num <= max_time:
            valid = True
            print("Impressive! You spent", test4num*60, 
                  "minutes or", test4num*60*60, "seconds in your mobile!")
            break
        else:
            print("Your answer is outside the allowed range.")

In [21]:
example()


Your answer is outside the allowed range.
Impressive! You spent 420 minutes or 25200 seconds in your mobile!

In [ ]: