Cannot find the cytation 1 objectives with the cytation 5 backend

New to PLR, but happy to now finally be able to connect some of our instruments. I was able to get some responses out of the cytation 1, using the cytation 5 backend (eg. firmware, temperature, open, close, filters), but the objectives info send_commands (eg. “i”, “h2”) do not seem to respond in the same way as the cytation 5 would do. Any thoughts on that?

3 Likes

welcome to the forum!

i will test on our cytation1

can you share the specific response you get?

Sure. A proper response is when querying the filters:

await self.send_command(“i”, “q1”)
Returns filter part number (1225100)

But for the objectives it gives:
await self.send_command(“i”, “h2”)
Returns 2101

h1, h3, etc give the same. The objectives we have are 1220519: Objective.O_4x_PLAN_FLUORITE and 1220539: Objective.O_2p5x_PLAN_FLUORITE

Ah, the supported objectives are actually very limited:

annulus_part_number2objective = {
            1320520: Objective.O_4x_PL_FL_PHASE,
            1320521: Objective.O_20x_PL_FL_PHASE,
            1322026: Objective.O_40x_PL_FL_PHASE,
          }

could that be the issue?

note that these are annulus part numbers, not part number of the objective. The easiest way to figure out the annulus number is to query it from the firmware, the second easiest way is to go into gen5.exe and export objective information as xml, then look in that file.

1 Like

That is indeed an issue, but I fixed that indeed by getting all the part numbers for the available lenses.

annulus_part_number2objective = {
    # **PL Ach Objectives**
    1220562: Objective.O_1p25x_PL_ACH,  # 1.25x PL Ach
    1220549: Objective.O_2p5x_PL_ACH,   # 2.5x PL Ach
    1220574: Objective.O_2x_PL_ACH,     # 2x PL Ach
    1320546: Objective.O_4x_PL_ACH,     # 4x PL Ach
    1450539: Objective.O_100x_PL_ACH_OIL,  # 100x PL Ach Oil
    1450538: Objective.O_60x_PL_ACH_OIL,   # 60x PL Ach Oil
    2210007: Objective.O_40x_PL_ACH_WATER,  # 40x PL Ach Water
    2210008: Objective.O_60x_PL_ACH_WATER,  # 60x PL Ach Water

    # **PL Extended Apoch Objectives**
    1220573: Objective.O_4x_PL_EXT_APOCH,
    1450569: Objective.O_40x_PL_EXT_APOCH,
    1450568: Objective.O_20x_PL_EXT_APOCH,

    # **PL FL Objectives**
    1220519: Objective.O_4x_PL_FL,
    1220517: Objective.O_20x_PL_FL,
    1220539: Objective.O_2p5x_PL_FL,
    1220518: Objective.O_10x_PL_FL,
    1220544: Objective.O_40x_PL_FL,
    1220545: Objective.O_60x_PL_FL,

    # **PL FL Oil Immersion Objectives**
    1450549: Objective.O_100x_PL_FL_OIL,
    1450548: Objective.O_60x_PL_FL_OIL,

    # **PL FL Phase Objectives (Phase Contrast)**
    1320518: Objective.O_40x_PL_FL_PHASE,
    1320517: Objective.O_20x_PL_FL_PHASE,
    1320516: Objective.O_10x_PL_FL_PHASE,
    1320515: Objective.O_4x_PL_FL_PHASE,

    # **Other Objectives)**
    1320520: Objective.O_4x_PL_FL_PHASE,  # 4x Olympus PL FL Phase
    1320521: Objective.O_20x_PL_FL_PHASE,  # 20x Olympus PL FL Phase
    1322026: Objective.O_40x_PL_FL_PHASE,  # 40x Olympus PL FL Phase
    1650200: Objective.O_UNKNOWN_NEW_OBJECTIVE,  # Placeholder for newer objectives
}

From: https://www.fishersci.co.uk/webfiles/uk/web-docs/11445_Cytation%20Imaging%20Primer_FS.pdf (page 31)

I also changed the Objectives Class accordingly, but my issue is that the instruction seems to be different for querying the objective information from the Cytation 1.

That is:

The response here is 2101. Or the raw Response: b'2101\x03', Hex: 3231303103 This is likely some kind of error code, because i get it also when sending other commands.

1 Like

wait, actually, these ones are objective numbers, not annulus numbers, so I’ll look them up later and correct. But the issue still stands—I first have to query the numbers using send_command(someting, something) before I can map them to the Objective class.

1 Like

when looking at the wireshark capture, i am 99% confident these are the packets that load the objective information. I haven’t figured out how to decode them to part number / objective identifier. Also, in the gen5.exe xml exports of the objectives we have in the cytation1 (4x pl fl and 20x pl fl), there is no annulus part number. However, when i export 20x fl pl phase from this same gen5.exe installation, i do see the annulus part number.

> b'i'
< b'\x06'
> b'o1'
< b'040000013047445745485351                  53484438'
< b'4446002056              0000\x03'
> b'i'
< b'\x06'
> b'o2'
< b'200000045047445745485351                  445335484'
< b'438444600181656        0000\x03'

the middle part decodes to

# first (4x):
>>> [chr(x) for x in bytes.fromhex("534844384446002056")]

['S', 'H', 'D', '8', 'D', 'F', '\x00', ' ', 'V']


# second (20x):

>>> [chr(x) for x in bytes.fromhex("445335484438444600181656")]

['D', 'S', '5', 'H', 'D', '8', 'D', 'F', '\x00', '\x18', '\x16', 'V']

doesn’t ring a bell for me

ah, in the first part:

040000013047445745485351 
200000045047445745485351
       ^^ numerical aperature
  ^^^^^ ?
^^ magnification

rest could decode to

>>> [chr(x) for x in bytes.fromhex("47445745485351")]
['G', 'D', 'W', 'E', 'H', 'S', 'Q']

but unsure

putting part number (from xml file) above middle byte:

# 4x
UPLFLN 4x
SHD8DF

# 20x
LUCPLFLN 20x
DS5HD8DF

it’s not clear why, but it’s consistent. wonder if it’s the same on your setup

1 Like

(send_command("i", "o1"))

this is great!

We have the same objective in slot1:

# response ("i", "o1"):
Hex:
30343030303030313330343734343537343534383533353120202020202020202020202020202020202035333438343433383434343630303230353620202020202020202020202020203030303003
Decoded:
040000013047445745485351                  
534844384446002056              
0000

But different objective in slot2 (i can also check two more lenses i think):

# response ("i", "o2"):
Hex:
30323734303030313230353833373431353135312020202020202020202020202020202020202020202033383434353333333530303031383134323135363135313631343137313820203030303003
Decoded:
02740001205837415151                      
384453335000181421561516141718  
0000

We have the Zeiss 2.5x lens in slot2 (#1220539), so:

02740001205837415151
0274 = magnifcation (2.74x)
      0120 = numerical aperture (0.12)

the middle hex part gives:

384453335000181421561516141718

8DS3P\x00\x18\x14!V\x15\x16\x14\x17\x18

I have to check the xml files to see the code for that, maybe it is zeiss specific. The 2.5x lens is also a planar fluorite objective, but does not have the HD8DF code apparently

2 Likes

the code HD8DF decodes to

FLU??
8DS3P

based on the above translation table.

The part number in the xml file is actually ‘fluar’, which matches!

there is no obvious mapping, but here’s what we know so far:

8 -> F
D -> L
S -> U
3 -> A
P -> R
S -> U
H -> P
5 -> C
F -> N

???

actually also looking at the non-alphanumeric bytes:

FLUAR      2    .    5 x /    0    .    1     2
8DS3P \x00 \x18 \x14 ! V \x15 \x16 \x14 \x17 \x18

UPLFLN      4    x
SHD8DF \x00 \x32 V

LUCPLFLN      2    0    x ? ?
DS5HD8DF \x00 \x18 \x16 V H @

there is no clear numbering (0x15 = 1, 0x16 = 2 gives that impression), but

>>> ord("S") - ord("H")
11
>>> ord("U") - ord("P")
5

actually, decoding the alphanumeric chars to hex (using normal ascii, we decoded to ascii before) it becomes clear:

0x00 -> space
0x21 -> 5
0x56 -> X
0x14 -> .
0x15 -> /
0x16 -> 0
0x17 -> 1
0x18 -> 2

0x33 -> A
0x35 -> C
0x38 -> F
0x44 -> L
0x50 -> R
0x53 -> U
0x46 -> N
0x48 -> P

inferring the rest of the encoding structure:

\x19 -> 3 # know
\x20 -> 4 # know
\x21 -> 5 # know 5 = 0x1, break the hex pattern, they are bytes but only 0-9 are used
\x22 -> 6
\x23 -> 7
\x24 -> 8
\x25 -> 9
\x33 -> A # know
\x34 -> B
\x35 -> C # know
\x36 -> D
\x37 -> E
\x38 -> F
\x39 -> G
\x40 -> H
\x41 -> I
\x42 -> J
\x43 -> K
\x44 -> L # know
\x45 -> M
\x46 -> N
\x47 -> O
\x48 -> P
\x49 -> Q
\x50 -> R
\x51 -> S
\x52 -> T
\x53 -> U
\x54 -> V
\x55 -> W
\x56 -> X
\x57 -> Y
\x58 -> Z

special chars are a little harder, but there are only so many :slight_smile:

this is all objectives we have:

UPLSAPO 40X2: 40X PL APO
LUCPLFLN 60X: 60X PL FL
UPLFLN 4X: 4X PL FL
LUCPLFLN 20XPh: 20X PL FL Phase
LUCPLFLN 40XPh: 40X PL FL Phase
U Plan: 2.5X PL ACH Meiji
UPLFLN 10XPh: 10X PL FL Phase
PLAPON 1.25X: 1.25X PL APO
UPLFLN 10X: 10X PL FL
UPLFLN 60XOI: 60X OIL PL FL
PLN 4X: 4X PL ACH
PLN 40X: 40X PL ACH
LUCPLFLN 40X: 40X PL FL
EC-H-Plan/2x: 2X PL ACH Motic
UPLFLN 100XO2: 100X OIL PL FL
UPLFLN 4XPh: 4X PL FL Phase
LUCPLFLN 20X: 20X PL FL
PLN 20X: 20X PL ACH
Fluar 2.5x/0.12: 2.5X FL Zeiss
UPLSAPO 100XO: 100X OIL PL APO
PLAPON 60XO: 60X OIL PL APO
UPLSAPO 20X: 20X PL APO

here’s a branch i just whipped together cytation1 objective loading by rickwierenga · Pull Request #432 · PyLabRobot/pylabrobot · GitHub (haven’t tested on actual machine, will do tmrw when back in lab)

2 Likes

yes this is solved!

Only the zeiss objective name should be capitols:

"FLUAR 2.5X/0.12": Objective.O_2_5X_FL_Zeiss,
instead of
"Fluar 2.5x/0.12"

now will continue capturing images. Have some issues there but need to figure out where it goes wrong.

3 Likes

great! i confirmed it here and merged the branch. if another issue comes up please make a new thread

1 Like