If you have the module sims working, you can use the toTwist2d method on SwerveDriveKinematics to convert a set of module positions deltas into a Twist2d. This includes components for the position deltas in X, Y, and theta based purely on the modules. That method is what SwerveDriveOdometry uses internally, except it would normally just use the X and Y components since the rotation comes from the gyro. However, you can use the theta value to update a Rotation2d to track the rotation without a gyro. That rotation can then be passed to the normal odometry or pose estimator object. It would look something like this:
Woah learned something new, long while ago I wanted to get the rotation kinematics from the swerve module states and thought they only computed the XY position. Must have missed this method. Thanks!
You need to give module deltas (change in position) to the toTwist2d method, not the current positions. So for each module, its delta is current position - last position