Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for UCLA Miniscope V4 #115

Closed
wants to merge 6 commits into from
Closed

Add support for UCLA Miniscope V4 #115

wants to merge 6 commits into from

Conversation

jonnew
Copy link
Member

@jonnew jonnew commented Jun 24, 2024

  • Addresses Miniscope V4 support #112
  • This device needs some work arounds because its clock is completely out of spec for the SERDES it uses. The back channel must be used to configure a PLL on the camera in order to bump the PCLK rate before it will like. For this reason, I made ConfigureFmcLinkController.CheckLinkState virtual so I could override its behavior with these steps before checking the link state
  • Added configuration and streaming functionality.
  • There are few outstanding issues:
  1. Something is wrong with the DS90UB9x configuration resulting in 1 missing column from each frame and imaging data that "slides" by one column for each image produced
  2. The axis map for the BNO needs to be set correctly
  3. I was unable to verify that the EWL was functioning. It might be fine, but I was unable to verify.

- This device needs some work arounds because its clock is completely
out of spec for the SERDES it uses. The back channel must be used to
configure a PLL on the camera in order to bump the PCLK rate before it
will like. For this reason, I mad ConfigureFmcLinkController.
CheckLinkState virtual so I could override its behavior with these steps
before checking the link state
- Added configuraiton and streaming functionality.
- There are few outstanding issues:
1. Something is wrong with the DS90UB9x configuration resulting in 1
missing column from each frame and imaging data that "slides" by one
column for each image produced
2. The axis map for the BNO needs to be set correctly
3. I was unable to verify that the EWL was functioning. It might be
fine, but I was unable to verify.
@jonnew jonnew requested review from aacuevas and glopesdev June 24, 2024 20:11
@jonnew
Copy link
Member Author

jonnew commented Jun 24, 2024

@aacuevas Please have a look at the ConfigureUclaMiniscopeV4Camera.ConfigureMiniscope function to see if there is anything that would result in the "sliding" image (we seem to be missing 1 row of raw data per image)

Copy link
Collaborator

@aacuevas aacuevas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is not that it is skipping rows, but we are not telling the software which row is the first. Since startup is asynchronous and each ONI frame is one picture row, we need to mark the first row of every picture frame so if acquisition starts mid picture (or if there were some transmission error), the software can ignore the first rows until a new picture frame starts, so it can create the image proper.

device.WriteRegister(DS90UB9x.TRIGGER, (uint)DS90UB9xTriggerMode.HsyncEdgePositive);
device.WriteRegister(DS90UB9x.SYNCBITS, 0);
device.WriteRegister(DS90UB9x.DATAGATE, (uint)DS90UB9xDataGate.VsyncPositive);
device.WriteRegister(DS90UB9x.MARK,0);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be DS90UB9xMarkMode.VsyncRising instead of 0

Since each ONI frame is one picture row, we need to know which row is the first on every picture frame. This marks the first row with a specific bit.

var hubClockBuffer = new ulong[UclaMiniscopeV4.SensorRows];
var clockBuffer = new ulong[UclaMiniscopeV4.SensorRows];

var frameObserver = Observer.Create<oni.Frame>(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure where in this lib it should go, but need a filter to wait until the first row is received. On the old lib we did it with SkipWhile(f => (f.Sample[5] & 0x8000) == 0)

Additionally, this "first row" bit needs to be ignored, if it were to cause an issue later (Miniscope data is only 12 bits after all)

@jonnew
Copy link
Member Author

jonnew commented Jun 25, 2024

Thanks for the feedback. While I did see this in the old library, I was a bit confused why it was needed. My understanding is that device.WriteRegister(DS90UB9x.DATAGATE, (uint)DS90UB9xDataGate.VsyncPositive); means that, although we might start in the middle of an image, this phase shift should be preserved for all time. Obviously that is still an issue, but I dont get why we seem to be transmitting one too few rows per frame.

I will try these suggestions regardless because I might be misunderstanding.

@jonnew jonnew added the feature New planned feature label Jun 25, 2024
@jonnew jonnew added this to the 0.1.0 milestone Jun 25, 2024
@jonnew
Copy link
Member Author

jonnew commented Jun 25, 2024

Here is a GIF illustrating the issue (note that this does not use Pixel Mark). Adding pixel mark would appear to solve this issue by resetting the phase back to 0 for each image, but it looks like data is being lost and this is independent of that.

ezgif-5-5ac264e2dd

- Consistent naming with with ConfigureUclaMiniscopeV4Camera
- Capture configuration properties outside of ConfigureDevice in
ConfigureUclaMiniscopeV4Camera
@aacuevas
Copy link
Collaborator

Thanks for the feedback. While I did see this in the old library, I was a bit confused why it was needed. My understanding is that device.WriteRegister(DS90UB9x.DATAGATE, (uint)DS90UB9xDataGate.VsyncPositive); means that, although we might start in the middle of an image, this phase shift should be preserved for all time. Obviously that is still an issue, but I dont get why we seem to be transmitting one too few rows per frame.

I will try these suggestions regardless because I might be misunderstanding.

The gate is just that, a gate. It just tells the raw device "ignore anything that happens when vsync is not high", but vsync is high for the entire duration of a pixel frame.
This would not be an issue if an ONI frame were a picture frame, but due to FIFO size constraints, an ONI frame is a picture row. And the important part: the raw device can start sending rows before the Bonsai node subscription happens. If that is the case, then the first rows will be discarded and Bonsai will start to listen mid-picture frame. We need to tell Bonsai to ignore everything until a new picture frame happens, that is what the mark does, set a mark on the oni frames that correspond with the 1st row, so bonsai can wait for it and then reconstruct a full frame.

The ´SkipWhile´ statement in the observer is required so Bonsai skips anything until it knows it's going to have a full picture frame.

@jonnew
Copy link
Member Author

jonnew commented Jun 26, 2024

FWIW, I used another miniscope PCB and the sliding phase shift in the image data disappeared. This indicates that although we are not guaranteed to start at the correct row, the data integrity is another issue entirely. Some scope PCBs seem to have significant data loss. Not sure if this has to do with miniscope design or the generic DS90UB9x receiver, or some combination of the two. In the meantime I have added the MarkMode so that images are aligned to row zero. The data loss issue is something else though.

jonnew added 2 commits June 26, 2024 11:22
- Address code syntax and naming issues
- Add Vsync rising mark to frame data so that we can align images to the
first row in the face of asynchronous bonsai fraime collection
- There is a larger issue about data integrity that seems to have more
to do with the miniscope pcb design than issues with our hardware this
library that is documented in #115
@aacuevas
Copy link
Collaborator

Can we compare the same faulty devices with the old lib? We had never noticed this specific behavior, so it could be that the old lib handled data losses better somehow, that there is still some software issue going on or that we were just lucky then and unlucky now.

Comment on lines 17 to 19
readonly BehaviorSubject<double> ledBrightness = new(0);
readonly BehaviorSubject<UclaMiniscopeV4SensorGain> sensorGain = new(UclaMiniscopeV4SensorGain.Low);
readonly BehaviorSubject<double> liquidLensVoltage = new(47); // NB: middle of range
Copy link
Collaborator

@glopesdev glopesdev Jul 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these be in the data node or the configure node? On one hand I can see the appeal of dynamic configuration while the device is streaming, on the other hand I would expect the configure node to be able to fully configure the sensor even if no downstream data node is immediately used. It also makes it more confusing that settings are distributed across two nodes.

Maybe this is one of the cases where we want to allow for the configure node to write to the device while the workflow is running, or alternatively we could have an editor that previews the camera for configuration.

Related to #114

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I agree. I put them here because I for some reason thought they should belong to the Data node. I think its better not to split configuration.

@jonnew
Copy link
Member Author

jonnew commented Jul 18, 2024

Aaron suggests testing the same hardware with the deprecated Bonsai.ONIX library to see if there is a different behavior.

Copy link
Collaborator

@glopesdev glopesdev Jul 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jonnew What was the motivation to rename UclaMiniscopeV4DataFrame to UclaMiniscopeV4Image? It wasn't mentioned in the commit message, but it seems confusing since it might give people the wrong impression that you can directly manipulate this type as if it was an image, which is not true.

In fact, the type has an Image property which is what effectively gives you the image. It also creates inconsistency with both the rest of the library, and with other camera packages, e.g. Spinnaker, Pylon, etc which also use the DataFrame terminology.

Copy link
Member Author

@jonnew jonnew Jul 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The motivation is that cameras produce images. All data types contain data. "UclaMiniscopeDataFrame" could be BNO data since that is also a device on the scope that produces a DataFrame.

Inconsistency: this was already present. I tried to bring this up with e.g. AnalogInout versus AnalogInputData. Or with the location of the Behavior Subject properties. But in those cases, we went forward with inconsistency to preserve user clarity (maybe...?). I was doing the same here.

We could do UclaMiniscopeV4CameraData instead.

Copy link
Collaborator

@glopesdev glopesdev Jul 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The output of the AnalogInput node is still AnalogInputDataFrame. In that case the discussion was what to name the "data" node, not the "data frame" type.

The BNO data for the UCLA miniscope is just a Bno055DataFrame since that is what comes out of the BNO055 device.

You are right that the Miniscope headstage has multiple devices inside it, but we are naming DataFrame types after device names, not after headstage names, and I think there is a hard constraint in ONI that for a single device there can be at most a single data frame stream, is that right? So I don't think we really need to have any inconsistency here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My point was that in other circumstances, we didn't follow conventions in the case where clarity was at stake. I think UclaMiniscopeV4CameraData is a good compromise.

Remember that the Miniscope is not an ONI device. The ONI device is the link controller in passthrough mode. If we were going to follow this convention rigidly, then Miniscope would not even appear in the type.

In any case, UclaMiniscopeV4CameraData is a good compromise, so lets use that.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remember that the Miniscope is not an ONI device.

It is not an ONI device, but it is registered in the package DeviceManager as a virtual device. The only reason this is not a real ONI device is an implementation detail due to the constraints of the passthrough mode. Even conceptually from a user-centric perspective I feel it is probably better to call it a device.

I don't think we should use the technical details of passthrough mode devices as an argument to guide our naming conventions. If we did, then we would have to change the other passthrough DataFrame types like the NPX 1.0e, 2.0e, etc.

The reason we didn't use the Data suffix for these types to begin with is that we use Data as a suffix for the data node, as that is more readable in the workflow and we can't have name clashes, so the NeuropixelsV1eData node produces a sequence of NeuropixelsV1eDataFrame objects.

From looking further in the commit, I see the reason you didn't fall into this name clash is that you named the data node as UclaMiniscopeV4Camera instead of UclaMiniscopeV4CameraData as per the other devices.

The reason we use the Data suffix for data nodes is that we use the device name for the static class containing the device registers. In this case you called this class UclaMiniscopeV4 which is why you didn't get a name clash there either, but this class really feels like it should be called UclaMiniscopeV4Camera since the registers here should be the passthrough registers for the pass-through camera device.

Given the above, I don't think we should agree on this compromise, unless we change our naming convention for all the other devices in the package.

@jonnew jonnew modified the milestones: 0.2.0, 0.3.0 Aug 13, 2024
@jonnew
Copy link
Member Author

jonnew commented Aug 26, 2024

@aacuevas brought a very obvious test to do: Make sure the miniscopes I'm using function correctly in Bonsai.ONIX because its using the same mechanism to collect data. Then look for the deltas between passthrough settings and how data is processed in that library versus this one.

@jonnew jonnew marked this pull request as draft September 5, 2024 16:22
@jonnew
Copy link
Member Author

jonnew commented Sep 5, 2024

This work was completed in #275

@jonnew jonnew closed this Sep 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New planned feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants