3434from odemis import dataio , model , util
3535from odemis .model import oneway
3636from odemis .util .synthetic import ParabolicMirrorRayTracer
37+ from odemis .util .synthetic import simulate_peak
3738
3839ERROR_STATE_FILE = "simcam-hw.error"
39-
40+ GOFFSET_TO_PIXEL = 0.25 # Conversion factor for grating offset to image pixels.
41+ PEAK_WIDTH = 2.5 # Width of the simulated spectrograph peak in pixels (before binning).
4042
4143class Camera (model .DigitalCamera ):
4244 '''
@@ -179,6 +181,22 @@ def clip_max_res(img_res):
179181 self ._img_simulator = None
180182 self ._mirror = None
181183
184+ try :
185+ spectrograph = dependencies ["spectrograph" ]
186+ if not (
187+ isinstance (spectrograph , model .ComponentBase )
188+ and hasattr (spectrograph , "axes" )
189+ and isinstance (spectrograph .axes , dict )
190+ and "goffset" in spectrograph .axes ):
191+ raise ValueError ("spectrograph %s must have a 'goffset' attribute" % spectrograph )
192+
193+ self ._spectrograph = spectrograph
194+ logging .debug ("Will simulate spectral peaks using spectrograph %s" , spectrograph .name )
195+
196+ except (TypeError , KeyError ):
197+ logging .info ("Will not simulate spectrograph peaks" )
198+ self ._spectrograph = None
199+
182200 # Simple implementation of the flow: we keep generating images and if
183201 # there are subscribers, they'll receive it.
184202 self .data = SimpleDataFlow (self )
@@ -374,6 +392,11 @@ def _simulate(self):
374392 center = self ._img_res [0 ] / 2 , self ._img_res [1 ] / 2
375393 pixel_size = self ._metadata .get (model .MD_PIXEL_SIZE , self .pixelSize .value )
376394
395+ # Extra translation to simulate stage movement
396+ pos = self ._metadata .get (model .MD_POS , (0 , 0 ))
397+ pxs = [p / b for p , b in zip (pixel_size , self .binning .value )]
398+ stage_shift = pos [0 ] / pxs [0 ], - pos [1 ] / pxs [1 ] # Y goes opposite
399+
377400 if self ._mirror :
378401 eff_pixel_size = [p * b for p , b in zip (pixel_size , binning )]
379402 self ._img_simulator .resolution .value = res
@@ -382,11 +405,6 @@ def _simulate(self):
382405 self ._img = model .DataArray (sim_img , self ._img .metadata )
383406 sim_img = self ._img .copy ()
384407 else :
385- # Extra translation to simulate stage movement
386- pos = self ._metadata .get (model .MD_POS , (0 , 0 ))
387- pxs = [p / b for p , b in zip (pixel_size , self .binning .value )]
388- stage_shift = pos [0 ] / pxs [0 ], - pos [1 ] / pxs [1 ] # Y goes opposite
389-
390408 # First and last index (eg, 0 -> 255)
391409 ltrb = [center [0 ] + trans [0 ] + stage_shift [0 ] - (res [0 ] / 2 ) * binning [0 ],
392410 center [1 ] + trans [1 ] + stage_shift [1 ] - (res [1 ] / 2 ) * binning [1 ],
@@ -413,6 +431,31 @@ def _simulate(self):
413431 [int (round (ltrb [1 ] + i * binning [1 ])) for i in range (res [1 ])])
414432 sim_img = self ._img [numpy .ix_ (coord [1 ], coord [0 ])] # copy
415433
434+ # spectrograph peak simulation
435+ if self ._spectrograph :
436+ current_offset = self ._spectrograph .position .value ["goffset" ]
437+
438+ ccd_center_x = self ._img_res [0 ]/ 2.0 # find the x-coordinate of the center of the ccd
439+ x0_px = ccd_center_x + current_offset * GOFFSET_TO_PIXEL
440+ roi_left = center [0 ] + trans [0 ] + stage_shift [0 ] - (res [0 ] / 2 ) * binning [0 ]
441+
442+ bin_x = binning [0 ] # binning factor along x-axis
443+ peak_center_binned = (x0_px - roi_left ) / bin_x # express the peak position in the ROI's coordinate system
444+
445+ logging .info ("DEBUG: x0_px=%s, ltrb0=%s, result=%s" ,
446+ x0_px , roi_left , peak_center_binned
447+ )
448+
449+ width_binned = PEAK_WIDTH / bin_x
450+
451+ peak = simulate_peak (amplitude = 20000 , x0 = peak_center_binned , width = width_binned ,
452+ shape = sim_img .shape , dtype = sim_img .dtype )
453+
454+ # set all values in sim_img to the minimal sim_img value
455+ min_val = sim_img .min ()
456+ sim_img [...] = min_val
457+ sim_img += peak
458+
416459 # Add some noise
417460 mx = self ._img .max ()
418461 sim_img += numpy .random .randint (0 , max (mx // 100 , 10 ), sim_img .shape , dtype = sim_img .dtype )
0 commit comments