def test_card_no_default(exposure, value): card = models.Card("MYCARD", value=value) with pytest.raises(ValueError) as err: assert card.evaluate(exposure=exposure) assert "The context does not include all the Card value placeholders" in str( err)
def test_evaluate_callable(exposure): card = models.Card("testcall", value=lambda x, y: x + y, fargs=(1, 2)) name, value, comment = card.evaluate(exposure) assert name == "testcall" assert comment == "" assert value == 3
def test_basic_header_model(exposure): basic_header_model = models.basic_header_model basic_header_model.append(MacroCardTest()) basic_header_model.append(models.Card("TEST", "test")) header = basic_header_model.to_header(exposure) assert isinstance(header, astropy.io.fits.Header) assert "IMAGETYP" in header assert "TEST" in header
def test_card_default_evaluate(exposure): models.DEFAULT_CARDS["TESTCARD"] = models.DefaultCard( "TESTCARD", value="2+2", comment="", evaluate=True, ) card = models.Card("TESTCARD") assert card.evaluate(exposure)[1] == 4
def test_autocast_boolean(value, expected, exposure): card = models.Card("MYCARD", value=value, autocast=True) assert card.evaluate(exposure).value == expected
def test_placeholder_fails_no_default(exposure): card = models.Card("MYCARD", value="{__exposure__.blah}") with pytest.raises(AttributeError): card.evaluate(exposure=exposure)
def test_placeholder_fails_with_default(exposure): card = models.Card("MYCARD", value="{__exposure__.blah}", default="a_default") result = card.evaluate(exposure=exposure) assert result.value == "a_default"
def test_default_card_default_value(exposure): del exposure.camera card = models.Card("CAMUID") assert isinstance(card, DefaultCard) assert card.evaluate(exposure).value == "NA"
def test_card_default(exposure): card = models.Card("MYCARD", value="{some_value_not_passed}", default="a_default") result = card.evaluate(exposure=exposure) assert result.value == "a_default"
def test_card_name_trimming(): with pytest.warns(CardWarning): card = models.Card("AVERYLARGENAME", "value") assert card.name == "AVERYLAR"
def test_default_card_overridden(): card = models.Card("VCAM", value="a value") assert card.value == "a value"
async def singleFrame(exptim, name, verb=False, ip_add=None, config="cameras.yaml", targ=None, kmirr=0.0, flen=None): """ Expose once and write the image to a FITS file. :param exptim: The exposure time in seconds. Non-negative. :type exptim: float :param verb: Verbosity on or off :type verb: boolean :param ip_add: list of explicit IP's (like 192.168.70.51 or lvmt.irws2.mpia.de) :type ip_add: list of strings :param config: Name of the YAML file with the cameras configuration :type config: string of the file name :param targ: alpha/delta ra/dec of the sidereal target :type targ: astropy.coordinates.SkyCoord :param kmirr: Kmirr angle in degrees (0 if up, positive with right hand rule along North on bench) :type kmirr: float :param flen: focal length of telescope/siderostat in mm If not provided it will be taken from the configuration file :type flen: float """ cs = BlackflyCameraSystem(BlackflyCamera, camera_config=config, verbose=verb, ip_list=ip_add) cam = await cs.add_camera(name=name) # print("cameras", cs.cameras) # print("config" ,config) exp = await cam.expose(exptim, "LAB TEST") if targ is not None and kmirr is not None: # if there is already a (partial) header information, keep it, # otherwise create one ab ovo. if exp.wcs is None: wcshdr = astropy.io.fits.Header() else: wcshdr = exp.wcs.to_header() key = astropy.io.fits.Card("CUNIT1", "deg", "WCS units along axis 1") wcshdr.append(key) key = astropy.io.fits.Card("CUNIT2", "deg", "WCS units along axis 2") wcshdr.append(key) key = astropy.io.fits.Card("CTYPE1", "RA---TAN", "WCS type axis 1") wcshdr.append(key) key = astropy.io.fits.Card("CTYPE2", "DEC--TAN", "WCS type axis 2") wcshdr.append(key) key = astropy.io.fits.Card("CRVAL1", targ.ra.deg, "[deg] RA at reference pixel") wcshdr.append(key) key = astropy.io.fits.Card("CRVAL2", targ.dec.deg, "[deg] DEC at reference pixel") wcshdr.append(key) # field angle: degrees, then radians # direction of NCP on the detectors (where we have already flipped pixels # on all detectors so fieldrot=kmirr=0 implies North is up and East is left) # With right-handed-rule: zero if N=up (y-axis), 90 deg if N=right (x-axis) # so the direction is the vector ( sin(f), cos(f)) before the K-mirror. # Action of K-mirror is ( cos(2*m), sin(2*m); sin(2*m), -cos(2*m)) # and action of prism is (-1 0 ; 0 1), i.e. to flip the horizontal coordinate. # todo: get starting value from a siderostat field rotation tracking model fieldrot = 0.0 if name[-1] == 'c': # without prism, assuming center camera placed horizontally if name[:4] == 'spec': # without K-mirror pass else: # with K-mirror # in the configuration the y-axis of the image has been flipped, # the combined action of (1, 0; 0, -1) and the K-mirror is (cos(2m), sin(2m); -sin(2m), cos(2m)) # and applied to the input vector this is (sin(2m+f), cos(2m+f)) fieldrot += 2. * kmirr else: # with prism if name[:4] == 'spec': # without K-mirror # Applied to input beam this gives (-sin(f), cos(f)) but prism effect # had been undone by vertical flip in the FLIR image. pass else: # with K-mirror # Combined action of K-mirror and prism is (-cos(2*m), -sin(2*m);sin(2*m), -cos(2*m)). # Applied to input beam this gives (-sin(2*m+f), -cos(2*m+f)) = (sin(2*m+f+pi), cos(2*m+f+pi)). fieldrot += 2. * kmirr + 180.0 if name[-1] == 'w': # Camera is vertically, # so up in the lab is right in the image fieldrot += 90 else: # Camera is vertically, # so up in the lab is left in the image fieldrot -= 90 fieldrot = math.radians(fieldrot) # the section/dictionary of the yaml file for this camera yamlconfig = cs._config[name] if flen is None: flen = yamlconfig["flen"] # pixel scale per arcseconds is focal length *pi/180 /3600 # = flen * mm *pi/180 /3600 # = flen * um *pi/180 /3.6, so in microns per arcsec... pixscal = math.radians(flen) / 3.6 # degrees per pixel is arcseconds per pixel/3600 = (mu/pix)/(mu/arcsec)/3600 degperpix = yamlconfig["pixsize"] / pixscal / 3600.0 # for the right handed coordinates # (pixx,pixy) = (cos f', -sin f'; sin f', cos f')*(DEC,RA) where f' =90deg -fieldrot # (pixx,pixy) = (sin f, -cos f; cos f , sin f)*(DEC,RA) # (sin f, cos f; -cos f, sin f)*(pixx,pixy) = (DEC,RA) # (-cos f, sin f; sin f, cos f)*(pixx,pixy) = (RA,DEC) # Note that the det of the WCS matrix is negativ (because RA/DEC is left-handed...) cosperpix = degperpix * math.cos(fieldrot) sinperpix = degperpix * math.sin(fieldrot) key = astropy.io.fits.Card("CD1_1", -cosperpix, "[deg/px] WCS matrix diagonal") wcshdr.append(key) key = astropy.io.fits.Card("CD2_2", cosperpix, "[deg/px] WCS matrix diagonal") wcshdr.append(key) key = astropy.io.fits.Card("CD1_2", sinperpix, "[deg/px] WCS matrix outer diagonal") wcshdr.append(key) key = astropy.io.fits.Card("CD2_1", sinperpix, "[deg/px] WCS matrix outer diagonal") wcshdr.append(key) exp.wcs = astropy.wcs.WCS(wcshdr) # print(exp.wcs.to_header_string()) for headr in wcshdr.cards: exp.fits_model[0].header_model.append(models.Card(headr)) await exp.write() if verb: print("wrote ", exp.filename)
async def _expose_internal(self, exposure): """ Read a single unbinned full frame and store in a FITS file. :param exposure: On entry exposure.exptim is the intended exposure time in [sec] On exit, exposure.data contains the 16bit data of a single frame :return: There is no return value """ # fill exposure.data with the frame's 16bit data # reg becomes a x=, y=, width= height= dictionary # these are in standard X11 coordinates where upper left =(0,0) reg = await self._expose_grabFrame(exposure) # print('region',reg) binxy = {} try: # becomes a dictionary with dx=... dy=... for the 2 horiz/vert binn fact binxy = self.device.get_binning() except Exception as ex: binxy = None # append FITS header cards # For the x/y coordinates transform from X11 to FITS coordinates # Todo: reports the camera y-flipped reg.y if ReversY=true above?? addHeaders = [ ("BinX", binxy.dx, "[ct] Horizontal Bin Factor 1, 2 or 4"), ("BinY", binxy.dy, "[ct] Vertical Bin Factor 1, 2 or 4"), ("Width", reg.width, "[ct] Pixel Columns"), ("Height", reg.height, "[ct] Pixel Rows"), ("RegX", 1 + reg.x, "[ct] Pixel Region Horiz start"), # The lower left FITS corner is the upper left X11 corner... ("RegY", self.regionBounds[1] - (reg.y + reg.height - 1), "[ct] Pixel Region Vert start") ] dev = self.device.get_device() # print(dir(dev)) # print(dir(self)) # print(self.camera_system.get_camera(self.name)) # print(self.camera_system._config[self.name]) try: gain = dev.get_float_feature_value("Gain") addHeaders.append(("Gain", gain, "Gain")) except Exception as ex: # print("failed to read gain" + str(ex)) pass imgrev = [False, False] try: imgrev[0] = self.device.get_boolean("ReverseX") addHeaders.append( ("ReverseX", imgrev[0] != 0, " Flipped left-right")) imgrev[1] = self.device.get_boolean("ReverseY") addHeaders.append(("ReverseY", imgrev[1] != 0, " Flipped up-down")) # print("reversed" + str(imgrev[0]) + str(imgrev[1]) ) except Exception as ex: # print("failed to read ReversXY" + str(ex)) pass # This is an enumeration in the GenICam. See features list of # `arv-tool-0.8 --address=192.168.70.50 features` binMod = [-1, -1] try: binMod[0] = dev.get_integer_feature_value("BinningHorizontalMode") if binMod[0] == 0: addHeaders.append( ("BinModeX", "Averag", "Horiz Bin Mode Sum or Averag")) else: addHeaders.append( ("BinModeX", "Sum", "Horiz Bin Mode Sum or Averag")) binMod[1] = dev.get_integer_feature_value("BinningVerticalMode") if binMod[1] == 0: addHeaders.append( ("BinModeY", "Averag", "Vert Bin Mode Sum or Averag")) else: addHeaders.append( ("BinModeY", "Sum", "Vert Bin Mode Sum or Averag")) except Exception as ex: # print("failed to read binmode" + str(ex)) pass tmp = False try: tmp = self.device.get_boolean("BlackLevelClampingEnable") addHeaders.append( ("CAMBLCLM", tmp != 0, "Black Level Clamping en/disabled")) # print("BlackLevelClampingEnable" + str(imgrev[0]) + str(imgrev[1]) ) except Exception as ex: # print("failed to read BlackLevelClampingEnable" + str(ex)) pass try: camtyp = self.device.get_model_name() addHeaders.append(("CAMTYP", camtyp, "Camera model")) except: pass # call _expose_wcs() to gather WCS header keywords addHeaders.extend(self._expose_wcs(exposure, reg)) for headr in addHeaders: exposure.fits_model[0].header_model.append(models.Card(headr)) # print(repr(exposure.to_hdu()[0].header)) # unref() is currently usupported in this GObject library. # Hope that this does not lead to any memory leak.... # buf.unref() return