Skip to content

Commit

Permalink
[Sigma] Refactor for FW 5.02
Browse files Browse the repository at this point in the history
  • Loading branch information
baku89 committed Oct 6, 2023
1 parent e1fb459 commit 6c836e2
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 76 deletions.
1 change: 1 addition & 0 deletions demo/src/useTethr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ export function useTethr(onSave: (object: TethrObject) => void) {
facingMode: useTethrConfig(camera, 'facingMode'),
focalLength: useTethrConfig(camera, 'focalLength'),
focusDistance: useTethrConfig(camera, 'focusDistance'),
focusMeteringMode: useTethrConfig(camera, 'focusMeteringMode'),
focusPeaking: useTethrConfig(camera, 'focusPeaking'),
liveviewMagnifyRatio: useTethrConfig(camera, 'liveviewMagnifyRatio'),
liveviewEnabled: useTethrConfig(camera, 'liveviewEnabled'),
Expand Down
12 changes: 11 additions & 1 deletion src/IFD.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,22 @@ export function encodeIFD(data: IFDData): ArrayBuffer {
dataView.writeUint32(count)

switch (entry.type) {
case IFDType.Byte: {
if (count > 4) {
throw new Error('Not yet supported')
} else {
for (let i = 0; i < 4; i++) {
dataView.writeUint8(entry.value[i])
}
}
break
}
case IFDType.Short: {
if (entry.value.length > 2) {
throw new Error('Not yet supported')
} else {
for (let i = 0; i < 2; i++) {
dataView.writeUint16(entry.value[i] ?? 0)
dataView.writeUint16(entry.value[i])
}
}
break
Expand Down
197 changes: 123 additions & 74 deletions src/TethrPTPUSB/TethrSigma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
computeShutterSpeedSeconds,
ConfigName,
ExposureMode,
FocusMeteringMode,
FocusPeaking,
ISO,
WhiteBalance,
Expand Down Expand Up @@ -109,13 +110,15 @@ enum SnapCaptureMode {

const ConfigListSigma: ConfigName[] = [
'aperture',
'batteryLevel',
'iso',
'canRunAutoFocus',
'colorTemperature',
'colorMode',
'destinationToSave',
'focalLength',
'focusDistance',
'focusMeteringMode',
'focusPeaking',
'exposureComp',
'exposureMode',
Expand All @@ -139,18 +142,22 @@ export class TethrSigma extends TethrPTPUSB {
async open() {
await super.open()

const {data} = await this.device.receiveData({
await this.device.receiveData({
label: 'SigmaFP ConfigApi',
opcode: OpCodeSigma.ConfigApi,
parameters: [0x0],
})

/**
* Don't need this IFD data
*
decodeIFD(data, {
cameraModel: {tag: 1, type: IFDType.Ascii},
serialNumber: {tag: 2, type: IFDType.Ascii},
firmwareVersion: {tag: 3, type: IFDType.Ascii},
communiationVersion: {tag: 5, type: IFDType.Float},
})
*/

const checkPropChangedInterval = () => {
if (!this.opened) return
Expand Down Expand Up @@ -276,7 +283,9 @@ export class TethrSigma extends TethrPTPUSB {
// NOTE: the colorModeOptions lacks Warm Gold (0xf0).
// it must be manually added only if the firmware is 5.0,
// but configAPI doesn't return the version information by some reason...
colorModeOptions.push(0x11)
if (!colorModeOptions.includes(0x11)) {
colorModeOptions.push(0x11)
}

return {
writable: colorModeOptions.length > 0,
Expand Down Expand Up @@ -515,15 +524,11 @@ export class TethrSigma extends TethrPTPUSB {
}
}

async setFocusDistance(
value: number
): Promise<{status: OperationResultStatus}> {
async setFocusDistance(value: number): Promise<OperationResult> {
const {focusPosition: range} = await this.getCamCanSetInfo5()

const focusPosition = scalar.round(scalar.lerp(range[1], range[0], value))

console.error(value, focusPosition)

const data = encodeIFD({
focusPosition: {tag: 81, type: IFDType.Short, value: [focusPosition]},
})
Expand All @@ -541,8 +546,50 @@ export class TethrSigma extends TethrPTPUSB {
return {status: 'ok'}
}

async getFocusMeteringModeDesc(): Promise<ConfigDesc<FocusMeteringMode>> {
const {focusArea: ids} = await this.getCamCanSetInfo5()

const id = (await this.getCamStatus()).focusArea[0]

const value = this.focusAreaTable.get(id) as FocusMeteringMode

const values = ids.map(n =>
this.focusAreaTable.get(n)
) as FocusMeteringMode[]

return {
writable: values.length > 0,
value,
option: {
type: 'enum',
values: values,
},
}
}

async setFocusMeteringMode(
value: FocusMeteringMode
): Promise<OperationResult> {
const id = this.focusAreaTable.getKey(value) as number
const data = encodeIFD({
focusArea: {tag: 10, type: IFDType.Byte, value: [id]},
})

try {
await this.device.sendData({
label: 'SigmaFP SetCamDataGroupFocus',
opcode: OpCodeSigma.SetCamDataGroupFocus,
data,
})
} catch (err) {
return {status: 'invalid parameter'}
}

return {status: 'ok'}
}

async getFocusPeakingDesc(): Promise<ConfigDesc<FocusPeaking>> {
// const value = (await this.getCamDataGroup5()).focusPeaking
// TODO: There's no way to retrieve and configure this value in the latest firmware
const value = 0
const {focusPeaking: values} = await this.getCamCanSetInfo5()

Expand Down Expand Up @@ -606,10 +653,10 @@ export class TethrSigma extends TethrPTPUSB {
let jpegQuality: string | null = null
let dngBitDepth: number | null = null

const hasDngMatch = imageQuality.match(/^raw (12|14)bit(?:,([a-z]+))?/i)
const pattern = imageQuality.match(/^raw (12|14)bit(?:,([a-z]+))?/i)

if (hasDngMatch) {
const [, dngBitDepthStr, jpegQualityStr] = hasDngMatch
if (pattern) {
const [, dngBitDepthStr, jpegQualityStr] = pattern
jpegQuality = jpegQualityStr ?? null
dngBitDepth = parseInt(dngBitDepthStr)
} else {
Expand Down Expand Up @@ -655,40 +702,31 @@ export class TethrSigma extends TethrPTPUSB {
}

async getImageQualityDesc() {
type ImageQualityConfig = {
jpegQuality: string | null
hasDNG: boolean
}
const {imageQuality, dngImageQuality: dngBitDepth} =
await this.getCamStatus()

const imageQuality: ImageQualityConfig = await (async () => {
const {imageQuality} = await this.getCamStatus()

let jpegQuality: string | null = null
switch (imageQuality & 0x0f) {
case 0x02:
jpegQuality = 'fine'
break
case 0x04:
jpegQuality = 'standard'
break
case 0x08:
jpegQuality = 'low'
break
}
let jpegQuality: string | null = null
switch (imageQuality & 0x0f) {
case 0x02:
jpegQuality = 'fine'
break
case 0x04:
jpegQuality = 'standard'
break
case 0x08:
jpegQuality = 'low'
break
}

const hasDNG = !!(imageQuality & 0x10)
const hasDNG = !!(imageQuality & 0x10)

return {
jpegQuality,
hasDNG,
}
})()

const {dngImageQuality} = await this.getCamStatus()
const value = [hasDNG ? `raw ${dngBitDepth}bit` : null, jpegQuality]
.filter(Boolean)
.join(',')

return {
writable: true,
value: stringifyImageQuality(imageQuality, dngImageQuality),
value: value,
option: {
type: 'enum',
values: [
Expand All @@ -703,25 +741,6 @@ export class TethrSigma extends TethrPTPUSB {
],
},
} as ConfigDesc<string>

function stringifyImageQuality(
quality: ImageQualityConfig,
dngBitDepth: number
) {
if (quality.hasDNG) {
if (quality.jpegQuality) {
return `raw ${dngBitDepth}bit,${quality.jpegQuality}`
} else {
return `raw ${dngBitDepth}bit`
}
} else {
if (quality.jpegQuality) {
return quality.jpegQuality
} else {
throw new Error('Invalid ImageQualityConfig')
}
}
}
}

async setImageSize(imageSize: string): Promise<OperationResult> {
Expand All @@ -733,10 +752,14 @@ export class TethrSigma extends TethrPTPUSB {

async getImageSizeDesc(): Promise<ConfigDesc<string>> {
const {resolution} = await this.getCamStatus()
const {stillImageResolution} = await this.getCamCanSetInfo5()

const value = this.imageSizeTable.get(resolution)
const values = stillImageResolution.map(v =>
this.imageSizeTableIFD.get(v)
) as string[]

if (!value) {
if (!value || values.length === 0) {
return {
writable: false,
value: null,
Expand All @@ -748,7 +771,7 @@ export class TethrSigma extends TethrPTPUSB {
value,
option: {
type: 'enum',
values: ['low', 'medium', 'high'],
values,
},
}
}
Expand Down Expand Up @@ -832,7 +855,7 @@ export class TethrSigma extends TethrPTPUSB {
const {lvMagnifyRatio} = await this.getCamStatus()
const value = this.liveviewMagnifyRatioTable.get(lvMagnifyRatio) ?? null

const {lvMagnifyRatio: values} = await this.getCamCanSetInfo5()
const {lvMagnificationRate: values} = await this.getCamCanSetInfo5()

return {
writable: values.length > 0,
Expand Down Expand Up @@ -1044,14 +1067,17 @@ export class TethrSigma extends TethrPTPUSB {
}

async runAutoFocus(): Promise<OperationResult> {
const succeed = await this.executeSnapCommand(SnapCaptureMode.StartAF)
await this.clearImageDBAll()
const startAFSucceed = await this.executeSnapCommand(
SnapCaptureMode.StartAF
)

if (succeed !== null) {
return {status: 'ok'}
} else {
return {status: 'general error'}
}
if (!startAFSucceed) return {status: 'general error'}

const stopAFSucceed = await this.executeSnapCommand(SnapCaptureMode.StopAF)

if (!stopAFSucceed) return {status: 'general error'}

return {status: 'ok'}
}

async getLiveViewImage(): Promise<OperationResult<Blob>> {
Expand Down Expand Up @@ -1354,11 +1380,11 @@ export class TethrSigma extends TethrPTPUSB {
afLock: {tag: 2, type: IFDType.Byte},
afFaceEyePriorMode: {tag: 3, type: IFDType.Byte},
afFaceEyePriorDetectionStatus: {tag: 4, type: IFDType.Byte},
afAreaSelect: {tag: 10, type: IFDType.Byte},
afAreaMode: {tag: 11, type: IFDType.Byte},
afFrameSize: {tag: 12, type: IFDType.Byte},
// afFramePosition: {tag: 13, type: IFDType.Byte},
// afFrameFaceFocusDetection: {tag: 14, type: IFDType.Byte},
focusArea: {tag: 10, type: IFDType.Byte},
onePointSelectionMethod: {tag: 11, type: IFDType.Byte},
distanceMeasurementFrameSize: {tag: 12, type: IFDType.Byte},
// distanceMeasurementFramePosition: {tag: 13, type: IFDType.Short},
// distanceMeasurementFrame: {tag: 14, type: IFDType.Byte},
preAlwaysAf: {tag: 51, type: IFDType.Byte},
afLimit: {tag: 52, type: IFDType.Byte},
focusPosition: {tag: 81, type: IFDType.Short},
Expand Down Expand Up @@ -1397,9 +1423,18 @@ export class TethrSigma extends TethrPTPUSB {
colorTemerature: {tag: 302, type: IFDType.Short},
colorMode: {tag: 320, type: IFDType.Byte},
focusMode: {tag: 600, type: IFDType.Byte},

focusArea: {tag: 610, type: IFDType.Byte},
onePointSelectionMethod: {tag: 611, type: IFDType.Byte},
focusOverallArea: {tag: 612, type: IFDType.Short},
focusValidArea: {tag: 613, type: IFDType.Short},
distanceMeasurementFrameSize: {tag: 614, type: IFDType.Byte},
eachDistanceMesasurementFrameSize: {tag: 615, type: IFDType.Short},
distanceMeasurementFrameMovementAmount: {tag: 616, type: IFDType.Byte},

focusPosition: {tag: 658, type: IFDType.Short},
lvImageTransfer: {tag: 700, type: IFDType.Byte},
lvMagnifyRatio: {tag: 701, type: IFDType.Byte},
lvMagnificationRate: {tag: 701, type: IFDType.Byte},
focusPeaking: {tag: 702, type: IFDType.Byte},
shutterSound: {tag: 801, type: IFDType.Byte},
afVolume: {tag: 802, type: IFDType.Byte},
Expand Down Expand Up @@ -1953,6 +1988,14 @@ export class TethrSigma extends TethrPTPUSB {
[0x1, 'high'],
[0x3, 'medium'],
[0x4, 'low'],
[0xff, 'raw'],
])

private imageSizeTableIFD = new BiMap<number, string>([
[0x1, 'high'],
[0x2, 'medium'],
[0x3, 'low'],
[0xff, 'raw'],
])

private destinationToSaveTable = new BiMap<number, string>([
Expand All @@ -1961,4 +2004,10 @@ export class TethrSigma extends TethrPTPUSB {
[0x02, 'pc'],
[0x03, 'camera,pc'],
])

private focusAreaTable = new BiMap<number, FocusMeteringMode>([
[1, 'multi spot'],
[2, 'vendor:1 point selection'],
[3, 'vendor:tracking'],
])
}
5 changes: 4 additions & 1 deletion src/configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ export type FocusMode = 'af' | 'mf'

export type FunctionalMode = 'standard' | 'sleep'

export type FocusMeteringMode = 'center-spot' | 'multi-spot'
export type FocusMeteringMode =
| 'center spot'
| 'multi spot'
| `vendor:${string}`

/**
* Focus peaking mode. `false` means disabled.
Expand Down

0 comments on commit 6c836e2

Please sign in to comment.